addExam.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. <script setup lang="ts">
  2. import { ref, reactive, nextTick, watch } from "vue";
  3. import { ElMessageBox, ElMessage } from "element-plus";
  4. import { Calendar } from "@element-plus/icons-vue";
  5. import dayjs from "dayjs";
  6. import { postAddAssessment } from "@/api/assessment";
  7. import {
  8. postListTreeWithUser,
  9. postListTree,
  10. postListTreeWithUserApi,
  11. treeDept
  12. } from "@/api/department";
  13. import { encryption } from "@/utils/encrypt";
  14. // 人员,医疗组,负责人
  15. import {
  16. postUserList,
  17. postOrganizationUserPage,
  18. getLeaderList
  19. } from "@/api/userSetting";
  20. // 医疗组
  21. import { getTemplateInfoAllList } from "@/api/templateInfo";
  22. // 获取医疗组的数据
  23. import { postPageGroup } from "@/api/userGroup";
  24. import ElPicker from "@/components/ELPicker/index.vue";
  25. const $emit = defineEmits(["addHandClick"]);
  26. const dialogVisibleAdd = ref(false);
  27. const titleHeader = ref<any>("发起考核");
  28. // 季度 半年
  29. const ElPickerRef = ref();
  30. const form = reactive({
  31. name: "",
  32. cycle: "月度",
  33. cycleValue: "",
  34. assessmentType: null,
  35. listArr: [
  36. {
  37. assessmentObjectId: "",
  38. idList: [],
  39. assessmentObjectName: "",
  40. assessmentModelId: "",
  41. assessmentModelName: ""
  42. }
  43. ]
  44. });
  45. const rules = reactive({
  46. name: [{ required: true, message: "请输入考核名称", trigger: "blur" }],
  47. cycle: [{ required: true, message: "请选择考核周期", trigger: "change" }]
  48. });
  49. const formLeftRules = reactive({
  50. left: [{ required: true, message: "请选择考核指标", trigger: "blur" }],
  51. value: [{ required: true, message: "请选择被考核对象", trigger: "blur" }]
  52. });
  53. const formRightRules = reactive({
  54. left: [{ required: true, message: "请选择考核指标", trigger: "blur" }],
  55. right: [{ required: true, message: "请选择考核模板", trigger: "blur" }]
  56. });
  57. const formRef = ref();
  58. const handleClose = () => {
  59. formRef.value.clearValidate(); // 清除验证错误
  60. formRef.value.resetFields(); // 重置表单字段
  61. dialogVisibleAdd.value = !dialogVisibleAdd.value;
  62. ElMessage({
  63. message: "已关闭"
  64. });
  65. // ElMessageBox.confirm("确认关闭弹窗吗?")
  66. // .then(() => {})
  67. // .catch(() => {
  68. // // catch error
  69. // });
  70. };
  71. const arrList = ref([]); // 考核对象与模版设置
  72. const transformArrayFormat = data => {
  73. return data
  74. .map(item => {
  75. return item.idList.map((id, index) => {
  76. return {
  77. assessmentObjectId: item?.assessmentObjectId[index],
  78. assessmentObjectName: item?.assessmentObjectName[index],
  79. assessmentModelId: item?.assessmentModelId,
  80. assessmentModelName: item?.assessmentModelName
  81. };
  82. });
  83. })
  84. .flat();
  85. };
  86. // 添加部门保存
  87. const saveDepartment = () => {
  88. formRef.value.validate(async valid => {
  89. if (valid) {
  90. if (!form.cycleValue) {
  91. ElMessage.error("请选择考核周期的具体时间");
  92. return false;
  93. }
  94. console.log("考核周期", form.listArr);
  95. form.assessmentObjectList = transformArrayFormat(form.listArr);
  96. if (timeType.value == "月度") {
  97. form.cycleValue = dayjs(new Date(form.cycleValue)).format("YYYY-MM");
  98. }
  99. if (timeType.value == "年度") {
  100. form.cycleValue = dayjs(new Date(form.cycleValue)).format("YYYY");
  101. }
  102. if (timeType.value == "日期") {
  103. form.cycleValue = dayjs(new Date(form.cycleValue)).format("YYYY-MM-DD");
  104. }
  105. form.assessmentObjectList = form.assessmentObjectList.filter(
  106. item => item.assessmentObjectId && item.assessmentObjectName
  107. );
  108. delete form.listArr;
  109. const { code, msg } = await postAddAssessment(form);
  110. if (code === 200) {
  111. ElMessage({
  112. message: "添加成功",
  113. type: "success"
  114. });
  115. $emit("addHandClick");
  116. dialogVisibleAdd.value = false;
  117. } else {
  118. // ElMessage.error(msg);
  119. }
  120. }
  121. });
  122. };
  123. const open = (item: any, index: string) => {
  124. // postListTreeWithUserApi();
  125. assessmentTypeShow.value = true;
  126. Object.assign(form, {
  127. name: "",
  128. cycle: "月度",
  129. cycleValue: "",
  130. assessmentType: null,
  131. listArr: [
  132. {
  133. idList: [],
  134. assessmentObjectId: null,
  135. assessmentObjectName: null,
  136. assessmentModelId: null,
  137. assessmentModelName: null
  138. }
  139. ]
  140. });
  141. if (item) {
  142. titleHeader.value = index;
  143. }
  144. dialogVisibleAdd.value = true;
  145. };
  146. const addNew = () => {
  147. form.listArr.push({
  148. assessmentObjectId: "",
  149. idList: [],
  150. assessmentObjectName: "",
  151. assessmentModelId: "",
  152. assessmentModelName: ""
  153. });
  154. arrList.value.push([]);
  155. // form.assessmentObjectList.left.push({ value: "" });
  156. };
  157. const deleteItem = (index: any) => {
  158. form.listArr.splice(index, 1);
  159. // form.assessmentObjectList.right.splice(index, 1);
  160. };
  161. // 单选
  162. const timeType = ref<any>("月度");
  163. const quarterValue = ref(true);
  164. const format = ref<any>("YYYY-MM");
  165. const pickerType = ref<any>("month");
  166. const handleRegionChange = (value: any) => {
  167. console.log("value", value);
  168. timeType.value = value;
  169. console.log("timeType.value", timeType.value);
  170. switch (value) {
  171. case "年度":
  172. form.cycleValue = "";
  173. format.value = "YYYY";
  174. pickerType.value = "year";
  175. break;
  176. case "月度":
  177. form.cycleValue = "";
  178. format.value = "YYYY-MM";
  179. pickerType.value = "month";
  180. break;
  181. case "日期":
  182. form.cycleValue = "";
  183. format.value = "YYYY-MM-DD";
  184. pickerType.value = "";
  185. break;
  186. case "季度":
  187. form.cycleValue = "";
  188. quarterValue.value = true;
  189. break;
  190. case "半年":
  191. form.cycleValue = "";
  192. quarterValue.value = false;
  193. break;
  194. }
  195. };
  196. // 关闭弹框-考核
  197. const handleCloseKH = () => {
  198. console.log("关闭弹框", "月度");
  199. handleRegionChange("月度");
  200. };
  201. const yearTime = ref<any>(dayjs(new Date()).format("YYYY"));
  202. const decreaseYear = () => {
  203. yearTime.value = dayjs().year(yearTime.value).subtract(1, "year").year(); // 减一年
  204. };
  205. const increaseYear = () => {
  206. yearTime.value = dayjs().year(yearTime.value).add(1, "year").year(); // 加一年
  207. };
  208. const monthOne = item => {
  209. let start = "";
  210. let end = "";
  211. switch (item) {
  212. case "第一季度":
  213. start = `${yearTime.value}-01-01`;
  214. end = `${yearTime.value}-03-31`;
  215. break;
  216. case "第二季度":
  217. start = `${yearTime.value}-04-01`;
  218. end = `${yearTime.value}-06-30`;
  219. break;
  220. case "第三季度":
  221. start = `${yearTime.value}-07-01`;
  222. end = `${yearTime.value}-09-30`;
  223. break;
  224. case "第四季度":
  225. start = `${yearTime.value}-10-01`;
  226. end = `${yearTime.value}-12-31`;
  227. break;
  228. case "上半年":
  229. start = `${yearTime.value}-01-01`;
  230. end = `${yearTime.value}-06-30`;
  231. break;
  232. case "下半年":
  233. start = `${yearTime.value}-07-01`;
  234. end = `${yearTime.value}-12-31`;
  235. break;
  236. }
  237. form.cycleValue = `${yearTime.value}年${item}`;
  238. // return `从 ${start} 到 ${end}`; // 设置时间区间
  239. };
  240. // 部门人员与考核模板数据
  241. const treeDeptList = ref([]);
  242. const templateparams = reactive({
  243. page: 1,
  244. pageSize: 100
  245. });
  246. const resTmp = ref([]);
  247. // 医疗 -- 部门负责人
  248. const selectRefs = ref();
  249. const handChange = index => {
  250. const arr = treeDeptList.value.filter(item => {
  251. return form.listArr[index].idList.includes(item.groupCode);
  252. });
  253. form.listArr[index].assessmentObjectId = arr.map(item => item.groupCode);
  254. form.listArr[index].assessmentObjectName = arr.map(item => item.groupName);
  255. };
  256. const handChange1 = index => {
  257. const arr = treeDeptList.value.filter(item => {
  258. return form.listArr[index].idList.includes(item.userCode);
  259. });
  260. form.listArr[index].assessmentObjectId = arr.map(item => item.userCode);
  261. form.listArr[index].assessmentObjectName = arr.map(item => item.realName);
  262. };
  263. const assessmentTypeRef = ref();
  264. const handleRreeSelect = index => {
  265. const arr = assessmentTypeRef.value[index].getCheckedNodes().filter(item => {
  266. return item.children.length == 0;
  267. });
  268. if (form.assessmentType === 0) {
  269. // 更新选中的 idList
  270. // form.listArr[index].idList = filteredNodes;
  271. // let aa = [];
  272. // form.listArr[index].idList.map(item => {
  273. // if (item.substring(0, 4) == "user") {
  274. // aa.push(item);
  275. // }
  276. // });
  277. // console.log(11111, aa);
  278. // form.listArr[index].idList = aa;
  279. form.listArr[index].assessmentObjectId = arr.map(item => {
  280. if (item.value.substring(0, 4) == "user") {
  281. return item.value;
  282. }
  283. });
  284. form.listArr[index].assessmentObjectName = arr.map(item => {
  285. if (item.value.substring(0, 4) == "user") {
  286. return item.label;
  287. }
  288. });
  289. } else {
  290. form.listArr[index].assessmentObjectId = arr.map(item => {
  291. return item.value;
  292. });
  293. form.listArr[index].assessmentObjectName = arr.map(item => {
  294. return item.label;
  295. });
  296. }
  297. };
  298. const handleSelect = index => {
  299. form.listArr[index].assessmentModelName = form.listArr[index].am.tpName;
  300. form.listArr[index].assessmentModelId = form.listArr[index].am.id;
  301. };
  302. const aaaa = a => {
  303. console.log(a);
  304. };
  305. // 考核类型
  306. const dpetTree = ref();
  307. // 转换函数 --- 部门
  308. const convertDepartmentDataRecursive = data => {
  309. return data.map(department => {
  310. const { deptCode, deptName, childrenRes } = department;
  311. return {
  312. value: deptCode,
  313. label: deptName,
  314. children:
  315. childrenRes.length > 0
  316. ? childrenRes.map(child => convertDepartmentDataRecursive([child])[0])
  317. : []
  318. };
  319. });
  320. };
  321. // 转换函数 --- 人员
  322. const convertDepartmentDataRecursive_Person = data => {
  323. return data.map(department => {
  324. const { userNameNew, userCodeNew, children, type } = department;
  325. // 递归处理子节点
  326. const processedChildren =
  327. children.length > 0
  328. ? convertDepartmentDataRecursive_Person(children)
  329. : [];
  330. // 检查子节点是否有 type 为 "user" 的节点
  331. const hasUserInChildren = processedChildren.some(child => !child.disabled);
  332. // 当前节点是否为 user 类型
  333. const isUser = type === "user";
  334. // 判断当前节点是否需要禁用
  335. const shouldDisable = !isUser && !hasUserInChildren;
  336. return {
  337. value: userCodeNew,
  338. label: userNameNew,
  339. children: processedChildren,
  340. disabled: shouldDisable
  341. };
  342. });
  343. };
  344. const treeDeptListOkay = ref([]);
  345. const assessmentTypeShow = ref(true);
  346. const assessmentTypeApi = async value => {
  347. assessmentTypeShow.value = false;
  348. // 人员,医疗组,负责人
  349. const { data = [], code } = await getTemplateInfoAllList();
  350. if (code == 200) {
  351. resTmp.value = data;
  352. }
  353. switch (value) {
  354. case 0:
  355. postListTreeWithUserApi();
  356. break;
  357. case 1:
  358. const { data, code } = await postListTree();
  359. dpetTree.value = convertDepartmentDataRecursive(data);
  360. break;
  361. case 2:
  362. const yiliao = await postPageGroup({
  363. pageNumber: 1,
  364. pageSize: 1000
  365. });
  366. treeDeptList.value = yiliao.data.records;
  367. break;
  368. case 3:
  369. const fuzhere = await getLeaderList({
  370. type: "dept"
  371. });
  372. treeDeptList.value = fuzhere.data;
  373. }
  374. };
  375. const monthTime = ref("");
  376. defineExpose({
  377. open
  378. });
  379. const handChangeTree = value => {
  380. console.log(1122131, value);
  381. // if (Array.isArray(value)) {
  382. // value.forEach(item => {
  383. // if (item.substring(0, 4) != "user") {
  384. // return delete item;
  385. // }
  386. // });
  387. // }
  388. };
  389. const idList = ref([]);
  390. </script>
  391. <template>
  392. <div>
  393. <el-dialog
  394. v-model="dialogVisibleAdd"
  395. :title="titleHeader"
  396. width="500"
  397. @close="handleCloseKH"
  398. >
  399. <el-form
  400. ref="formRef"
  401. :model="form"
  402. label-width="auto"
  403. style="max-width: 600px"
  404. :rules="rules"
  405. label-position="top"
  406. >
  407. <el-form-item label="考核名称" prop="name">
  408. <el-input v-model="form.name" placeholder="请输入" />
  409. </el-form-item>
  410. <el-form-item label="考核周期" prop="cycle">
  411. <el-radio-group v-model="form.cycle" @change="handleRegionChange">
  412. <el-radio value="月度" size="large">月度</el-radio>
  413. <el-radio value="季度" size="large">季度</el-radio>
  414. <el-radio value="半年" size="large">半年</el-radio>
  415. <el-radio value="年度" size="large">年度</el-radio>
  416. <el-radio value="日期" size="large">日期</el-radio>
  417. </el-radio-group>
  418. <el-date-picker
  419. v-if="
  420. timeType === '月度' || timeType === '日期' || timeType === '年度'
  421. "
  422. v-model="form.cycleValue"
  423. :type="pickerType"
  424. :format="format"
  425. placeholder="请选择"
  426. @change="aaaa"
  427. />
  428. <el-dropdown
  429. v-else-if="timeType === '季度' || timeType === '半年'"
  430. trigger="click"
  431. >
  432. <span class="el-dropdown-link navbar-bg-hover select-none">
  433. <el-input
  434. v-model="form.cycleValue"
  435. style="width: 240px"
  436. placeholder="请选择"
  437. :prefix-icon="Calendar"
  438. />
  439. </span>
  440. <template #dropdown>
  441. <el-dropdown-menu class="setting" style="width: 300px">
  442. <div class="flex justify-between align-center text-base mt-4">
  443. <div
  444. class="cursor-pointer ml-4 text-xs pt-2"
  445. @click="decreaseYear"
  446. >
  447. <el-icon>
  448. <DArrowLeft />
  449. </el-icon>
  450. </div>
  451. <div>{{ yearTime }}</div>
  452. <div
  453. class="cursor-pointer mr-4 text-xs pt-2"
  454. @click="increaseYear"
  455. >
  456. <el-icon>
  457. <DArrowRight />
  458. </el-icon>
  459. </div>
  460. </div>
  461. <div
  462. v-if="quarterValue"
  463. class="flex justify-center align-center mt-5 mb-5"
  464. >
  465. <el-dropdown-item @click="monthOne('第一季度')">
  466. 一季度
  467. </el-dropdown-item>
  468. <el-dropdown-item @click="monthOne('第二季度')">
  469. 二季度
  470. </el-dropdown-item>
  471. <el-dropdown-item @click="monthOne('第三季度')">
  472. 三季度
  473. </el-dropdown-item>
  474. <el-dropdown-item @click="monthOne('第四季度')">
  475. 四季度
  476. </el-dropdown-item>
  477. </div>
  478. <div v-else class="flex justify-around align-center mt-5 mb-5">
  479. <el-dropdown-item @click="monthOne('上半年')">
  480. 上半年
  481. </el-dropdown-item>
  482. <el-dropdown-item @click="monthOne('下半年')">
  483. 下半年
  484. </el-dropdown-item>
  485. </div>
  486. </el-dropdown-menu>
  487. </template>
  488. </el-dropdown>
  489. </el-form-item>
  490. <el-form-item label="被考核类型">
  491. <el-select
  492. v-model="form.assessmentType"
  493. placeholder="请选择被考核类型"
  494. @change="assessmentTypeApi"
  495. >
  496. <el-option label="员工" :value="0" />
  497. <el-option label="部门" :value="1" />
  498. <el-option label="医疗组" :value="2" />
  499. <el-option label="部门负责人" :value="3" />
  500. </el-select>
  501. </el-form-item>
  502. <el-form-item
  503. label="被考核对象与模板设置"
  504. prop="form1"
  505. label-position="top"
  506. >
  507. <div class="w-full flex">
  508. <div class="w-1/3">
  509. <el-form
  510. label-position="top"
  511. :model="form.listArr"
  512. :rules="formLeftRules"
  513. >
  514. <el-form-item label="被考核对象" prop="left">
  515. <div
  516. v-for="(item, index) in form.listArr"
  517. :key="index"
  518. class="w-full mt-1"
  519. >
  520. <el-tree-select
  521. v-if="form.assessmentType === 0"
  522. ref="assessmentTypeRef"
  523. v-model="item.idList"
  524. multiple
  525. :data="convertDepartmentDataRecursive_Person(treeDept)"
  526. :render-after-expand="false"
  527. show-checkbox
  528. style="width: 180px"
  529. @change="handChangeTree"
  530. @check-change="handleRreeSelect(index)"
  531. />
  532. <el-tree-select
  533. v-if="form.assessmentType === 1"
  534. ref="assessmentTypeRef"
  535. v-model="item.idList"
  536. :data="dpetTree"
  537. show-checkbox
  538. multiple
  539. style="width: 180px"
  540. :render-after-expand="false"
  541. @check-change="handleRreeSelect(index)"
  542. />
  543. <el-select
  544. v-if="form.assessmentType == 2"
  545. ref="selectRefs"
  546. v-model="item.idList"
  547. multiple
  548. @change="handChange(index)"
  549. >
  550. <el-option
  551. v-for="(it, id) in treeDeptList"
  552. :key="id"
  553. :label="it.groupName"
  554. :value="it.groupCode"
  555. />
  556. </el-select>
  557. <el-select
  558. v-if="form.assessmentType == 3"
  559. ref="selectRefs"
  560. v-model="item.idList"
  561. multiple
  562. @change="handChange1(index)"
  563. >
  564. <el-option
  565. v-for="it in treeDeptList"
  566. :key="it.userCode"
  567. :label="it.realName"
  568. :value="it.userCode"
  569. />
  570. </el-select>
  571. <el-select v-if="assessmentTypeShow" multiple />
  572. </div>
  573. </el-form-item>
  574. </el-form>
  575. </div>
  576. <div class="w-1/2 ml-7">
  577. <el-form
  578. label-position="top"
  579. :model="form.listArr"
  580. :rules="formRightRules"
  581. >
  582. <el-form-item label="考核模板" prop="right">
  583. <div
  584. v-for="(item, index) in form.listArr"
  585. :key="index"
  586. class="w-full flex mt-1"
  587. >
  588. <el-select
  589. v-model="item.am"
  590. placeholder="请选择"
  591. filterable
  592. style="width: 180px"
  593. value-key="id"
  594. @change="handleSelect(index)"
  595. >
  596. <el-option
  597. v-for="itemTmp in resTmp"
  598. :key="itemTmp.id"
  599. :label="itemTmp.tpName"
  600. :value="itemTmp"
  601. style="width: 180px"
  602. />
  603. </el-select>
  604. <div
  605. v-if="index !== 0"
  606. class="ml-7 cursor-pointer w-1"
  607. @click="deleteItem(index)"
  608. >
  609. <el-text type="danger">
  610. <el-icon>
  611. <Delete />
  612. </el-icon>
  613. </el-text>
  614. </div>
  615. </div>
  616. </el-form-item>
  617. </el-form>
  618. </div>
  619. </div>
  620. </el-form-item>
  621. <el-form-item>
  622. <template #label>
  623. <div class="cursor-pointer w-14" @click="addNew">
  624. <el-text type="primary">
  625. <el-icon> <Plus /> </el-icon>添加
  626. </el-text>
  627. </div>
  628. </template>
  629. </el-form-item>
  630. </el-form>
  631. <template #footer>
  632. <div class="dialog-footer">
  633. <el-button @click="handleClose">取消</el-button>
  634. <el-button type="primary" @click="saveDepartment"> 确认 </el-button>
  635. </div>
  636. </template>
  637. </el-dialog>
  638. </div>
  639. </template>