newAdd.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. <script setup lang="ts">
  2. defineOptions({
  3. name: "evaluateNewAdd"
  4. });
  5. import { ref, reactive, onMounted } from "vue";
  6. import one from "@/assets/svg/2.svg";
  7. import two from "@/assets/svg/1.svg";
  8. import one1 from "@/assets/svg/2-2.svg";
  9. import two2 from "@/assets/svg/1-1.svg";
  10. import { useRouter, useRoute } from "vue-router";
  11. import { Edit, Operation } from "@element-plus/icons-vue";
  12. import { ElMessageBox, ElMessage } from "element-plus";
  13. import { postAddRelationList, postUpdateDept } from "@/api/dimension";
  14. import { useAppStoreHook } from "@/store/modules/app";
  15. import { addQuota } from "@/api/indexDefine";
  16. import { postUpdateTemplateIn, deleteIndicator } from "@/api/templateInfo";
  17. import {
  18. postAddDimension,
  19. getDimensionRemove,
  20. getQuotaByDimensionId
  21. } from "@/api/dimension";
  22. import editMould from "./editMould.vue";
  23. import {
  24. VueDraggable,
  25. useDraggable,
  26. type DraggableEvent,
  27. type UseDraggableReturn
  28. } from "vue-draggable-plus";
  29. import { delQuota, postAddDisposableQuota } from "@/api/indexDefine";
  30. import settingIndexDrawer from "./settingIndexDrawer.vue";
  31. import {
  32. postAddTemplate,
  33. getInfo,
  34. getListBy,
  35. postUpdate
  36. } from "@/api/templateInfo";
  37. import importIndex from "./importIndex.vue";
  38. import { formulaParamsChange } from "@/utils/business";
  39. const router = useRouter();
  40. const route = useRoute();
  41. const formRef = ref();
  42. const active = ref(0);
  43. const editDrawer = ref();
  44. const editDrawerShow = ref(false);
  45. const titleShow = ref(false);
  46. // 序号
  47. const order = ref();
  48. // 指标设置
  49. const settingIndexDrawerRef = ref();
  50. const settingIndexDrawerShow = ref(false);
  51. // 导入指标
  52. const importIndexRef = ref();
  53. const importIndexShow = ref(false);
  54. const handleSelect = index => {
  55. // console.log(index);
  56. active.value = index;
  57. };
  58. const tepName = ref();
  59. const tepNameForm = reactive({
  60. tpName: "",
  61. id: "",
  62. isScoreConverse: 1
  63. });
  64. onMounted(() => {
  65. if (route.query.tpName && route.query.id) {
  66. tepNameForm.tpName = route.query.tpName;
  67. tepNameForm.id = route.query.id;
  68. console.log("route.query", route.query);
  69. tepNameForm.isScoreConverse = Number(route.query.isScoreConverse);
  70. }
  71. });
  72. const tableData = ref([]);
  73. // 新建模板
  74. const postAddTemplateApi = async () => {
  75. const res = await postAddTemplate({ ...tepNameForm });
  76. Object.assign(tepNameForm, {
  77. tpName: "",
  78. id: ""
  79. });
  80. if (res.code === 200) {
  81. ElMessage({
  82. message: "创建成功",
  83. type: "success"
  84. });
  85. tepNameForm.tpName = res.data.tpName;
  86. tepNameForm.id = res.data.id;
  87. tepNameForm.isScoreConverse = res.data.isScoreConverse;
  88. titleShow.value = true;
  89. } else {
  90. ElMessage.error(res.msg);
  91. }
  92. };
  93. // 考核维度卡片
  94. const eaxmCard = ref([]);
  95. // 获取维度
  96. const getListByApi = async (id?: number | string) => {
  97. eaxmCard.value = [];
  98. const obj = {
  99. tpId: tepNameForm.id,
  100. id
  101. };
  102. const { code, data, msg } = await getListBy(obj);
  103. if (code === 200) {
  104. titleShow.value = true;
  105. eaxmCard.value = data;
  106. eaxmCard.value.forEach(item => {
  107. initializeTableData(item);
  108. });
  109. } else {
  110. ElMessage.error(msg);
  111. }
  112. };
  113. const getAddListByApi = async id => {
  114. const obj = {
  115. tpId: id
  116. };
  117. const { code, data, msg } = await getListBy(obj);
  118. if (code === 200) {
  119. eaxmCard.value = data;
  120. eaxmCard.value.forEach(item => {
  121. initializeTableData(item);
  122. });
  123. } else {
  124. ElMessage.error(msg);
  125. }
  126. };
  127. // 获取指标信息
  128. const paramsIndex = reactive({
  129. id: "",
  130. tpId: "",
  131. dimId: ""
  132. });
  133. const getQuotaByDimensionIdApi = async id => {
  134. paramsIndex.dimId = id;
  135. const { code, data, msg } = await getQuotaByDimensionId(paramsIndex);
  136. if (code === 200) {
  137. return data;
  138. } else {
  139. ElMessage.error(msg);
  140. return [];
  141. }
  142. };
  143. const initializeTableData = async item => {
  144. item.tableData = await getQuotaByDimensionIdApi(item.id);
  145. };
  146. const amountTo = itemList => {
  147. let num = 0;
  148. if (itemList) {
  149. itemList.forEach(item => {
  150. num = num + item.weight;
  151. });
  152. return num;
  153. }
  154. };
  155. const addDimension = reactive({
  156. list: [
  157. {
  158. id: "",
  159. dimName: "",
  160. dimWeight: "",
  161. mode: ""
  162. }
  163. ],
  164. dimName: "",
  165. dimWeight: "",
  166. mode: "",
  167. showIndicRemark: "",
  168. showScoreRule: "",
  169. showDatasource: "",
  170. showTargetValue: "",
  171. showFinalValue: "",
  172. showChallengeValue: "",
  173. showStartValue: "",
  174. remark: ""
  175. });
  176. // 创建考核维度
  177. const createAdd = () => {
  178. order.value = eaxmCard.value.length + 1;
  179. editDrawer.value.open(tepNameForm, "新建", order.value);
  180. };
  181. const backChange = () => {
  182. useAppStoreHook().toggleSideBar(true, "打开");
  183. router.back();
  184. };
  185. // 更新模板
  186. const postUpdateTemplateInApi = async () => {
  187. const { code, msg } = await postUpdateTemplateIn({
  188. tpName: tepNameForm.tpName,
  189. id: tepNameForm.id,
  190. isScoreConverse: tepNameForm.isScoreConverse
  191. });
  192. if (code == 200) {
  193. titleShow.value = true;
  194. } else {
  195. ElMessage.error(msg);
  196. }
  197. };
  198. // 保存
  199. const save = () => {
  200. if (titleShow.value) {
  201. router.back();
  202. useAppStoreHook().toggleSideBar(true, "打开");
  203. } else {
  204. formRef.value.validate(valid => {
  205. if (valid) {
  206. if (tepNameForm.id) {
  207. getAddListByApi(tepNameForm.id);
  208. postUpdateTemplateInApi();
  209. if (router.query) {
  210. }
  211. } else {
  212. postAddTemplateApi();
  213. }
  214. }
  215. });
  216. }
  217. };
  218. const deleteRow = row => {
  219. console.log(row);
  220. ElMessageBox.confirm(
  221. "指标删除后不可恢复,请谨慎操作!",
  222. "确定删除该指标吗?",
  223. {
  224. confirmButtonText: "确认",
  225. cancelButtonText: "取消",
  226. type: "warning"
  227. }
  228. )
  229. .then(async () => {
  230. const { code, msg } = await deleteIndicator(row.id);
  231. if (code === 200) {
  232. ElMessage({
  233. type: "success",
  234. message: "删除成功"
  235. });
  236. // initializeTableData(row.dimId);
  237. getListByApi();
  238. } else {
  239. ElMessage.error(msg);
  240. }
  241. })
  242. .catch(() => {
  243. ElMessage({
  244. type: "info",
  245. message: "用户取消"
  246. });
  247. });
  248. };
  249. // 编辑
  250. const editPen = (item, order) => {
  251. editDrawer.value.open(item, "编辑", order);
  252. };
  253. // 删除考核维度
  254. const deletePen = item => {
  255. ElMessageBox.confirm(
  256. "该维度删除后不可恢复,请谨慎操作!",
  257. "确定删除考核维度",
  258. {
  259. confirmButtonText: "确认",
  260. cancelButtonText: "取消",
  261. type: "warning"
  262. }
  263. )
  264. .then(async () => {
  265. // eaxmCard.value.splice(index, 1);
  266. const { code, msg } = await getDimensionRemove(item.id);
  267. if (code === 200) {
  268. getListByApi();
  269. ElMessage({
  270. type: "success",
  271. message: "删除成功"
  272. });
  273. } else {
  274. ElMessage.error(msg);
  275. }
  276. })
  277. .catch(() => {
  278. ElMessage({
  279. type: "info",
  280. message: "用户取消"
  281. });
  282. });
  283. };
  284. const settingIndex = row => {
  285. settingIndexDrawerRef.value.open(row);
  286. };
  287. const importIndexDialog = row => {
  288. importIndexRef.value.open(row);
  289. };
  290. const importIndexOne = async row => {
  291. let formula = JSON.stringify({
  292. noConditionFormula: ""
  293. });
  294. console.log(row, "获取的数据");
  295. const { code, data, msg } = await postAddDisposableQuota({ name: "" });
  296. if (code == 200) {
  297. const res = await postAddRelationList([
  298. {
  299. dimId: row.id,
  300. indId: data.id,
  301. tpId: row.tpId,
  302. formula,
  303. scoreStandard: 0,
  304. order: row.tableData.length + 1
  305. }
  306. ]);
  307. if (res.code === 200) {
  308. initializeTableData(row);
  309. ElMessage({
  310. type: "success",
  311. message: "添加成功"
  312. });
  313. } else {
  314. ElMessage.error(res.msg);
  315. }
  316. } else {
  317. ElMessage.error(msg);
  318. }
  319. };
  320. // 拖拽
  321. const elDraggable = ref();
  322. const elDraggableTableData = ref();
  323. // 更新
  324. const postUpdateDeptApi = async (id, dimName, order) => {
  325. await postUpdateDept({
  326. id,
  327. dimName,
  328. order,
  329. tpId: tepNameForm.id
  330. });
  331. };
  332. const onEnd = (e: DraggableEvent) => {
  333. eaxmCard.value.map((item, index) => {
  334. postUpdateDeptApi(item.id, item.dimName, index + 1);
  335. });
  336. };
  337. const postUpdateApi = async row => {
  338. // indexOf.indName = indexOf.name
  339. const params = { ...row };
  340. const { code, msg } = await postUpdate(params);
  341. if (code == 200) {
  342. // getListByApi();
  343. ElMessage.success("编辑成功");
  344. } else {
  345. ElMessage.error(msg);
  346. }
  347. };
  348. const indexOf = reactive({
  349. id: "",
  350. tpId: "",
  351. dimId: "",
  352. indId: "",
  353. valueInput: 0,
  354. designatedPersonnel: "",
  355. scoreStandard: 0,
  356. remark: "",
  357. createUser: "",
  358. createTime: 0,
  359. updateUser: "",
  360. updateTime: 0,
  361. isDelete: 0,
  362. scoreRule: "",
  363. targetValue: 0,
  364. finalValue: 0,
  365. challengeValue: 0,
  366. startValue: 0,
  367. datasoure: "",
  368. weight: 0,
  369. scoreValue: 0,
  370. formula: "",
  371. formulaType: 0,
  372. formulaParams: [],
  373. order: 0,
  374. indName: ""
  375. });
  376. const editConfig = ref<any>({
  377. trigger: "click",
  378. mode: "cell",
  379. beforeEditMethod({ row, column }) {
  380. if (row.stshowDatasourceate == "BI") {
  381. return false;
  382. }
  383. if (
  384. row.importStatus &&
  385. ["indName", "indicRemark", "dataSoure"].includes(column.property)
  386. ) {
  387. return false;
  388. }
  389. return true;
  390. }
  391. });
  392. const tableVxeRef = ref();
  393. const editClosedEvent = ({ row, column }) => {
  394. const $table = tableVxeRef.value;
  395. if ($table) {
  396. Object.assign(indexOf, row);
  397. console.log("indexOf", indexOf, row);
  398. postUpdateApi(row);
  399. }
  400. };
  401. // 处理表格行拖拽
  402. const rowDragendEvent = ({ newRow, oldRow, dragPos }) => {
  403. // 行拖拽的回调函数,可以处理拖拽后的数据更新等操作
  404. console.log("行拖拽", newRow, oldRow, dragPos);
  405. // 示例:更新数据顺序
  406. // const movedRow = tableData.value.splice(oldIndex, 1)[0];
  407. // tableData.value.splice(newIndex, 0, movedRow);
  408. };
  409. </script>
  410. <template>
  411. <div class="w-[100%]">
  412. <!-- 导入指标 -->
  413. <importIndex
  414. ref="importIndexRef"
  415. v-model="importIndexShow"
  416. @handClickInit="getListByApi"
  417. />
  418. <!-- 指标设置 -->
  419. <settingIndexDrawer
  420. ref="settingIndexDrawerRef"
  421. v-model:drawerValue="settingIndexDrawerShow"
  422. @refresh="getListByApi"
  423. />
  424. <!-- 新增、编辑模块 -->
  425. <editMould
  426. ref="editDrawer"
  427. v-model:drawerValue="editDrawerShow"
  428. @handClick="getAddListByApi"
  429. />
  430. <div class="w-[100%] flex justify-evenly">
  431. <div class="left-box">
  432. <el-text class="cursor-pointer" @click="backChange"
  433. ><el-icon> <ArrowLeft /> </el-icon>返回</el-text
  434. >
  435. </div>
  436. <div class="center-box">
  437. <div
  438. style="max-width: 220px"
  439. class="m-auto flex justify-between items-center"
  440. >
  441. <div
  442. :class="{ 'step-success': !titleShow, 'step-error': titleShow }"
  443. class="w-[100px] flex justify-center items-center"
  444. @click="titleShow = false"
  445. >
  446. <one v-if="titleShow" />
  447. <two2 v-else />基础信息
  448. </div>
  449. <div
  450. :class="{ 'step-success': titleShow, 'step-error': !titleShow }"
  451. class="w-[100px] flex justify-center items-center"
  452. >
  453. <two v-if="titleShow" />
  454. <one1 v-else />考核指标
  455. </div>
  456. </div>
  457. </div>
  458. <div class="right-box">
  459. <el-button type="primary" class="mr-2" @click="save()">保存</el-button>
  460. </div>
  461. </div>
  462. <div class="mt-2">
  463. <div v-if="!titleShow" class="w-[40%] m-auto mt-10">
  464. <el-form
  465. ref="formRef"
  466. :model="tepNameForm"
  467. label-width="auto"
  468. style="max-width: 600px"
  469. >
  470. <el-form-item
  471. prop="tpName"
  472. label="模板名称"
  473. :rules="[
  474. {
  475. required: true,
  476. message: '请输入模板名称',
  477. trigger: 'blur'
  478. }
  479. ]"
  480. >
  481. <el-input
  482. v-model="tepNameForm.tpName"
  483. placeholder="最多输入100字"
  484. />
  485. </el-form-item>
  486. <!-- <el-form-item
  487. prop="isScoreConverse"
  488. label="是否支持得分换算"
  489. :rules="[
  490. {
  491. required: true,
  492. message: '是否支持得分换算',
  493. trigger: 'blur'
  494. }
  495. ]"
  496. >
  497. <el-radio-group v-model="tepNameForm.isScoreConverse">
  498. <el-radio :value="1" size="large">是</el-radio>
  499. <el-radio :value="0" size="large">否</el-radio>
  500. </el-radio-group>
  501. </el-form-item> -->
  502. </el-form>
  503. </div>
  504. <div v-else class="w-[90%] m-auto mt-4">
  505. <div class="relative h-10">
  506. <el-button class="float-right" type="primary" plain @click="createAdd"
  507. >创建考核维度</el-button
  508. >
  509. </div>
  510. <VueDraggable
  511. ref="elDraggable"
  512. v-model="eaxmCard"
  513. :animation="150"
  514. ghostClass="ghost"
  515. handle=".card-header"
  516. class="flex flex-col gap-2 p-4 w-300px h-300px m-auto bg-gray-500/5 rounded"
  517. @end="onEnd"
  518. >
  519. <el-card v-for="(item, index) in eaxmCard" :key="index" class="mb-3">
  520. <template #header>
  521. <div class="card-header cursor-move">
  522. <span>{{ item.dimName }}({{ item.dimWeight }}%)</span>
  523. <div class="float-right mr-7">
  524. <el-icon class="mr-3" @click="editPen(item, index + 1)">
  525. <EditPen />
  526. </el-icon>
  527. <el-icon @click="deletePen(item)">
  528. <Delete class="text-red-500" />
  529. </el-icon>
  530. </div>
  531. </div>
  532. </template>
  533. <vxe-table
  534. ref="tableVxeRef"
  535. border
  536. show-overflow
  537. :edit-config="editConfig"
  538. :data="item.tableData"
  539. :row-config="{ drag: true }"
  540. @row-dragend="rowDragendEvent"
  541. >
  542. <vxe-column title="" width="60" fixed="left" drag-sort />
  543. <vxe-column
  544. field="indName"
  545. fixed
  546. title="指标名称"
  547. :edit-render="{ name: 'input' }"
  548. />
  549. <vxe-column
  550. field="indicRemark"
  551. title="指标说明"
  552. :edit-render="{ name: 'input' }"
  553. drag-sort
  554. />
  555. <vxe-column
  556. field="scoreRule"
  557. title="评价标准"
  558. :edit-render="{ name: 'input' }"
  559. width="200"
  560. />
  561. <vxe-column
  562. field="dataSoure"
  563. title="数据来源"
  564. :edit-render="{ name: 'input' }"
  565. />
  566. <vxe-column
  567. field="weight"
  568. title="权重"
  569. :edit-render="{ name: 'input' }"
  570. />
  571. <vxe-column
  572. field="scoreValue"
  573. title="分值"
  574. :edit-render="{ name: 'input' }"
  575. />
  576. <vxe-column
  577. field="targetValue"
  578. title="目标值"
  579. :edit-render="{ name: 'input' }"
  580. />
  581. <!-- <vxe-column
  582. v-if="item.showFinalValue"
  583. field="finalValue"
  584. title="完成值"
  585. :edit-render="{ name: 'input' }"
  586. /> -->
  587. <vxe-column
  588. v-if="item.showChallengeValue"
  589. field="challengeValue"
  590. title="挑战值"
  591. :edit-render="{ name: 'input' }"
  592. />
  593. <vxe-column
  594. v-if="item.showStartValue"
  595. field="startValue"
  596. title="门槛值"
  597. :edit-render="{ name: 'input' }"
  598. />
  599. <vxe-column
  600. v-if="item.showRemark"
  601. field="remark"
  602. title="备注"
  603. :edit-render="{ name: 'input' }"
  604. />
  605. <vxe-column field="age" fixed="right" title="操作">
  606. <template #default="{ row }">
  607. <el-icon class="mr-3" @click="settingIndex(row)">
  608. <Setting />
  609. </el-icon>
  610. <el-icon @click="deleteRow(row)">
  611. <Delete class="text-red-500" />
  612. </el-icon>
  613. </template>
  614. </vxe-column>
  615. </vxe-table>
  616. <template #footer>
  617. <el-button
  618. type="primary"
  619. link
  620. class="mr-4"
  621. @click="importIndexOne(item)"
  622. >
  623. 添加指标
  624. </el-button>
  625. <el-button type="primary" link @click="importIndexDialog(item)">
  626. 导入指标
  627. </el-button>
  628. <span class="float-right num">
  629. 指标权重合计:{{ amountTo(item.tableData) }}
  630. </span>
  631. </template>
  632. </el-card>
  633. </VueDraggable>
  634. </div>
  635. </div>
  636. </div>
  637. </template>
  638. <style lang="scss" scoped>
  639. .left-box {
  640. display: flex;
  641. flex: 0 0 5%;
  642. align-items: center;
  643. margin: auto;
  644. text-align: center;
  645. // justify-content: center;
  646. }
  647. .center-box {
  648. flex: 0 0 70%;
  649. }
  650. .right-box {
  651. display: flex;
  652. flex: 0 0 5%;
  653. align-items: center;
  654. justify-content: center;
  655. margin: auto;
  656. }
  657. .step-success {
  658. height: 24px;
  659. padding-bottom: 5px;
  660. font-size: 16px;
  661. // font-family: PingFangSC-SNaNpxibold;
  662. font-weight: 600;
  663. line-height: 24px;
  664. color: black;
  665. color: #000000e6;
  666. border-bottom: 2px solid #022bbd;
  667. }
  668. .step-error {
  669. height: 24px;
  670. padding-bottom: 5px;
  671. font-size: 16px;
  672. // font-family: PingFangSC-SNaNpxibold;
  673. font-weight: 600;
  674. line-height: 24px;
  675. color: #0006;
  676. letter-spacing: 0;
  677. }
  678. .num {
  679. font-size: 14px;
  680. // font-family: PingFangSC-Regular;
  681. font-weight: 400;
  682. line-height: 22px;
  683. color: #0009;
  684. letter-spacing: 0;
  685. }
  686. </style>