PatientEmrData.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <script setup lang="ts">
  2. import { useCompRef } from "@/utils/useCompRef";
  3. import { ElIcon, ElInput, ElMessageBox, ElTree } from "element-plus";
  4. import { h, ref } from "vue";
  5. import { Document, Folder, Lock, Open, Sort } from "@element-plus/icons-vue";
  6. import { stringIsBlank } from "@/utils/blank-utils";
  7. import { xcMessage } from "@/utils/xiaochan-element-plus";
  8. import {
  9. emrMitt,
  10. isCourse,
  11. patientInfo,
  12. } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/emr-init";
  13. import {
  14. delDir,
  15. electronicMedicalRecordSequencing,
  16. newDir,
  17. rename,
  18. } from "@/api/zhu-yuan-yi-sheng/emr-patient";
  19. import { useDialog } from "@/components/cy/CyDialog/index";
  20. import AddEmrDialog from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/components/add-emr-dialog/AddEmrDialog.vue";
  21. import { emrRootContextKey } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/useEmrStore";
  22. import ContextMenu from "@imengyu/vue3-context-menu";
  23. const props = defineProps<{
  24. treeData: any[];
  25. }>();
  26. const root = inject(emrRootContextKey);
  27. const emits = defineEmits(["nodeClick", "refresh"]);
  28. const inputValue = ref("");
  29. const treeRef = useCompRef(ElTree);
  30. const defaultProps = {
  31. children: "children",
  32. label: "name",
  33. };
  34. const handleNodeClick = (data, node) => {
  35. if (data.type === "group-category") return;
  36. if (data.courseJumpId) {
  37. emits("nodeClick", {
  38. ...node.parent.data,
  39. courseJumpId: data.courseJumpId,
  40. });
  41. } else {
  42. emits("nodeClick", data);
  43. }
  44. };
  45. function inputChange() {
  46. treeRef.value?.filter(inputValue.value);
  47. }
  48. const filterNode = (query: string, node) => {
  49. if (!query) return true;
  50. return node.name.includes(query);
  51. };
  52. let drag = new Map();
  53. const dragEnd = (moveNode, inNode, type, event) => {
  54. if (type === "none") return;
  55. let temp = {
  56. newParent: inNode.data.emrDocumentId,
  57. oldParent: moveNode.data.parent,
  58. };
  59. drag.set(moveNode.data.id, temp);
  60. };
  61. const dragLimit = (moveNode, inNode, type) => {
  62. if (moveNode.data.jump) return false;
  63. if (inNode.data.emrDocumentId && type === "inner") {
  64. return inNode.data.type !== "category";
  65. }
  66. };
  67. const allowDrag = node => {
  68. if (isCourse(node.data.emrCategoryCode)) {
  69. return true;
  70. }
  71. return node.data.type !== "group-category";
  72. };
  73. const isItAFolder = data => {
  74. return h(ElIcon, null, {
  75. default: () => {
  76. if (data.type === "category") {
  77. return h(Document);
  78. } else {
  79. return h(Folder);
  80. }
  81. },
  82. });
  83. };
  84. function isFiler(data) {
  85. return data.type !== "group-category";
  86. }
  87. const getDirName = async (title: string): Promise<string> => {
  88. return ElMessageBox.prompt("文件夹名称", title, {
  89. type: "info",
  90. inputErrorMessage: "名称不能为空",
  91. inputPattern: /\S/,
  92. }).then(({ value }) => {
  93. return value;
  94. });
  95. };
  96. const contextmenuItemV2 = (e, data) => {
  97. e.preventDefault();
  98. e.stopPropagation();
  99. function createVerification() {
  100. if (isCourse(data.code)) {
  101. return false;
  102. }
  103. return !isFiler(data);
  104. }
  105. const disabledChangeDir = data.data.parent === null;
  106. ContextMenu.showContextMenu({
  107. x: e.x,
  108. y: e.y,
  109. items: [
  110. {
  111. disabled: !createVerification(),
  112. label: "新建文件夹",
  113. onClick: async () => {
  114. const name = await getDirName("新建文件夹");
  115. await newDir({
  116. parent: data.folderId,
  117. patNo: patientInfo.value.inpatientNo,
  118. times: patientInfo.value.admissTimes,
  119. name: name,
  120. });
  121. emits("refresh");
  122. },
  123. },
  124. {
  125. disabled: !createVerification(),
  126. label: "新建病历",
  127. onClick: () => {
  128. useDialog<any>(AddEmrDialog, {
  129. dialogProps: {
  130. title: `在【${data.name}】文件夹下面创建病历`,
  131. top: "2%",
  132. },
  133. showConfirm: false,
  134. params: {
  135. templateData: root.store.emrTemplate.value.emrTree,
  136. deptTree: root.store.emrTemplate.value.deptTree,
  137. },
  138. }).then(res => {
  139. // @ts-ignore
  140. emrMitt.emit("loadTemplate", { ...res, parent: data.folderId });
  141. });
  142. },
  143. },
  144. {
  145. label: "打开(只读)",
  146. onClick: () => {
  147. if (!data.id) {
  148. xcMessage.error("请选中保存的病历。");
  149. return;
  150. }
  151. emrMitt.emit("只读病历", data.id);
  152. },
  153. icon: h(ElIcon, {}, () => h(Open)),
  154. disabled: !isFiler(data),
  155. },
  156. {
  157. label: "确认排序",
  158. onClick: () => {
  159. let temp = [];
  160. drag.forEach((value, key) => {
  161. if (value.newParent !== value.oldParent) {
  162. temp.push({ id: key, parent: value.newParent });
  163. }
  164. });
  165. if (temp.length === 0) {
  166. drag.clear();
  167. return xcMessage.error("文件夹没有变化,无需点击。");
  168. }
  169. electronicMedicalRecordSequencing(temp).then(() => {
  170. drag.clear();
  171. });
  172. },
  173. disabled: drag.size === 0,
  174. icon: h(ElIcon, {}, () => h(Sort)),
  175. },
  176. {
  177. label: "修改文件夹名称",
  178. onClick: async () => {
  179. const name = await getDirName("重命名");
  180. await rename(name, data.id);
  181. emits("refresh");
  182. },
  183. disabled: disabledChangeDir,
  184. },
  185. {
  186. label: "删除文件夹",
  187. onClick: async () => {
  188. await ElMessageBox.confirm(
  189. "是否删除该文件夹,里面的病历并不会删除",
  190. "删除",
  191. { type: "error" }
  192. );
  193. await delDir(data.id);
  194. emits("refresh");
  195. },
  196. disabled: disabledChangeDir,
  197. },
  198. ],
  199. });
  200. };
  201. const contextmenuItem = (event, data) => {
  202. let tempData = {
  203. id: data.emrDocumentId,
  204. code: data.emrCategoryCode,
  205. patNo: data.patNo,
  206. times: data.times,
  207. name: data.name ? data.name : data.emrName,
  208. parent: data.parent,
  209. type: data.type,
  210. folderId: data.emrDocumentId,
  211. data: data,
  212. };
  213. contextmenuItemV2(event, tempData);
  214. };
  215. const nullToEmpty = val => {
  216. return stringIsBlank(val) ? "" : " \\ " + val;
  217. };
  218. const fileName = val => {
  219. return val.name + nullToEmpty(val.createName) + nullToEmpty(val.createDate);
  220. };
  221. </script>
  222. <template>
  223. <div class="layout_container">
  224. <div>
  225. <el-input
  226. ref="inputRef"
  227. v-model="inputValue"
  228. @input="inputChange"
  229. clearable
  230. />
  231. </div>
  232. <div>
  233. <el-tree
  234. class="down-tree"
  235. :data="props.treeData"
  236. :props="defaultProps"
  237. @node-click="handleNodeClick"
  238. node-key="emrDocumentId"
  239. ref="treeRef"
  240. draggable
  241. @node-drag-end="dragEnd"
  242. :allow-drag="allowDrag"
  243. :allow-drop="dragLimit"
  244. @node-contextmenu="contextmenuItem"
  245. highlight-current
  246. :filter-node-method="filterNode"
  247. default-expand-all
  248. >
  249. <template #default="{ node, data }">
  250. <el-icon v-if="data.submit">
  251. <Lock />
  252. </el-icon>
  253. <component :is="isItAFolder(data)" />
  254. <span :title="fileName(data)">{{ fileName(data) }}</span>
  255. </template>
  256. </el-tree>
  257. </div>
  258. </div>
  259. </template>