YzTableV3.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. <script setup lang="ts">
  2. import {stringIsBlank, stringNotBlank} from "@/utils/blank-utils";
  3. import {
  4. associateOrders,
  5. clearAssociate,
  6. jsQueryYzData,
  7. openDrugManual,
  8. OrderBy,
  9. queryParam,
  10. tempYzData,
  11. yiZhuData,
  12. yzMitt,
  13. YzType
  14. } from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
  15. import {h, nextTick, reactive, ref} from 'vue'
  16. import {VxeTable, VxeTablePropTypes} from 'vxe-table'
  17. import {nullToEmpty} from '@/utils/public'
  18. import {getFormatDatetime} from '@/utils/date'
  19. import {getServerDateApi} from '@/api/public-api'
  20. import {updateOrderInstruction} from '@/api/zhu-yuan-yi-sheng/yi-zhu-lu-ru'
  21. import sleep from "../../../../../../utils/sleep";
  22. import {ElButton, ElButtonGroup} from "element-plus";
  23. import {xcMessage} from "@/utils/xiaochan-element-plus";
  24. import XEUtils from "xe-utils";
  25. import {TablePublicMethods} from "vxe-table/types/table";
  26. const emits = defineEmits(['rowClick', 'voidOrders'])
  27. const tableRef = ref<TablePublicMethods>()
  28. const toggleAllCheckboxEvent = () => {
  29. const $table = tableRef.value
  30. if ($table) {
  31. $table!.toggleAllCheckboxRow()
  32. }
  33. }
  34. const toggleCheckboxEvent = (row) => {
  35. const $table = tableRef.value
  36. if ($table) {
  37. $table!.toggleCheckboxRow(row)
  38. }
  39. }
  40. interface MenuClick {
  41. $event: Event,
  42. row: YzType,
  43. menu: { code: string, name: string, disabled?: boolean },
  44. column: any
  45. }
  46. // 鼠标右键配置文件
  47. const menuConfig = reactive<VxeTablePropTypes.MenuConfig>({
  48. body: {
  49. options: [
  50. [
  51. {code: 'fee', name: '医嘱费用'}
  52. ],
  53. [
  54. {code: 'copy', name: '复制', prefixIcon: 'vxe-icon-copy'}
  55. ],
  56. [
  57. {
  58. code: 'paste',
  59. name: '粘贴',
  60. prefixIcon: 'vxe-icon-paste'
  61. }
  62. ],
  63. [
  64. {code: 'relevancy', name: '关联'}
  65. ],
  66. [
  67. {code: 'exitTheAssociation', name: '退出关联'}
  68. ],
  69. [
  70. {
  71. code: 'instructions',
  72. name: '说明书'
  73. }
  74. ],
  75. [
  76. {
  77. code: 'stopTime',
  78. name: '停止医嘱'
  79. }
  80. ]
  81. ],
  82. },
  83. visibleMethod({options, column, row}) {
  84. options.forEach(list => {
  85. list.forEach((item) => {
  86. item.disabled = true
  87. if (item.code === 'paste') {
  88. item.disabled = !yzMitt.emit('allowReplication')
  89. }
  90. if (row) {
  91. if (['copy', 'relevancy', 'exitTheAssociation'].includes(item.code)) {
  92. item.disabled = false
  93. }
  94. if (['relevancy', 'instructions'].includes(item.code)) {
  95. item.disabled = row.serial == '00'
  96. }
  97. if (item.code === 'fee') {
  98. item.disabled = false
  99. }
  100. if (item.code === 'stopTime') {
  101. item.disabled = !showEndTime(row)
  102. }
  103. }
  104. })
  105. })
  106. return true
  107. }
  108. })
  109. // 鼠标右键执行的函数
  110. const rightFunc = {
  111. "fee": (data: YzType) => {
  112. yzMitt.emit('queryFeeByOrderNo', data)
  113. },
  114. "copy": (data: YzType) => {
  115. yzMitt.emit('copy', data.actOrderNo, data.frequCode)
  116. },
  117. "paste": (data: YzType) => {
  118. yzMitt.emit('paste')
  119. },
  120. "relevancy": async (data: YzType) => {
  121. if (canBeAssociated(data, true)) {
  122. if (associateOrders.value.actOrderNo === null) {
  123. await jsQueryYzData()
  124. associateOrders.value.actOrderNo = data.actOrderNo
  125. } else {
  126. xcMessage.error('请先确认当前关联医嘱。')
  127. }
  128. }
  129. },
  130. "exitTheAssociation": (data: YzType) => {
  131. clearAssociate()
  132. },
  133. "instructions": (data: YzType) => {
  134. openDrugManual(data.orderCode, data.serial)
  135. },
  136. "stopTime": (data) => {
  137. if (tableRef.value.isCheckedByCheckboxRow(data)) {
  138. cancelStopTime(data)
  139. } else {
  140. setDefaultStopTime({row: data})
  141. }
  142. }
  143. }
  144. // 触发鼠标右键的事件
  145. const tableRightClick = (val: MenuClick) => {
  146. let {row, menu} = val
  147. try {
  148. rightFunc[menu.code](row)
  149. } catch {
  150. }
  151. }
  152. function canBeAssociated(data: YzType, showMessage = false) {
  153. function message(message) {
  154. showMessage && xcMessage.error(message);
  155. }
  156. if (stringNotBlank(data.parentNo)) {
  157. message('该医嘱已经有父医嘱了。');
  158. return false
  159. } else if (data.statusFlag !== '1') {
  160. message('不是录入状态的医嘱无法关联。');
  161. return false
  162. } else if (data.serial === '00') {
  163. message('项目无法关联。');
  164. return false
  165. }
  166. return true
  167. }
  168. const twinkleList = ref({})
  169. const rowClassName = ({row, rowIndex}) => {
  170. let data = row as YzType
  171. if (typeof twinkleList.value[data.actOrderNo] !== 'undefined') {
  172. sleep(3000).then(() => {
  173. delete twinkleList.value[data.actOrderNo]
  174. })
  175. return 'animation_hzfirst'
  176. } else if (typeof twinkleList.value[data.parentNo] !== 'undefined') {
  177. return 'animation_hzfirst'
  178. }
  179. // 父级
  180. if (data.associationFlag) {
  181. return 'parent_level'
  182. }
  183. // 子级
  184. if (associateOrders.value.actOrderNo === data.actOrderNo) {
  185. return 'child_level'
  186. }
  187. if (data.actOrderNo === yiZhuData.value.actOrderNo) {
  188. return 'activation'
  189. }
  190. if (data.statusFlag === '6') {
  191. return 'parent_level'
  192. }
  193. //判断行颜色 可以自己定义在家
  194. const classList = [];
  195. if(data.zxCount > 0){
  196. classList.push('row-normal');
  197. }
  198. return classList.join(' ');
  199. }
  200. const setDefaultStopTime = async (val) => {
  201. let {row} = val
  202. if (showEndTime(row)) {
  203. row.endTimeTemp = getFormatDatetime(await getServerDateApi(), 'YYYY-MM-DDTHH:mm')
  204. await tableRef.value.setCheckboxRow(row, true)
  205. }
  206. }
  207. const endTimeChange = (val, row) => {
  208. if (stringIsBlank(val)) {
  209. tableRef.value.setCheckboxRow(row, false)
  210. } else {
  211. tableRef.value.setCheckboxRow(row, true)
  212. }
  213. }
  214. const cancelStopTime = (row) => {
  215. if (row.parentNo) return
  216. row.endTimeTemp = ''
  217. tableRef.value.setCheckboxRow(row, false)
  218. }
  219. const instructionEnter = (row: YzType) => {
  220. updateOrderInstruction(row.actOrderNo, row.instruction)
  221. }
  222. function getYiZhuFlag(val) {
  223. if (stringIsBlank(val)) {
  224. return val
  225. }
  226. switch (val) {
  227. case '1':
  228. return h('span', {style: {color: '#05ff00'}}, 'R')
  229. case '2':
  230. return h('span', {style: {color: '#0000fb'}}, 'Q')
  231. case '3':
  232. return h('span', {style: {color: '#ff07f3'}}, 'Z')
  233. case '4':
  234. return h('span', {style: {color: '#ff07f3'}}, 'Z')
  235. case '5':
  236. return h('span', {style: {color: 'red'}}, 'T')
  237. case '-1':
  238. return h('span', {style: {color: '#00ffe0'}}, 'D')
  239. case '6':
  240. return h('span', {style: {color: '#ffffff'}}, 'E')
  241. default:
  242. return 'warning'
  243. }
  244. }
  245. const timeFomat = (val) => {
  246. return getFormatDatetime(val, 'YY-MM-DD HH:mm')
  247. }
  248. const showEndTime = (data: YzType) => {
  249. if (!data) {
  250. return false
  251. }
  252. return stringIsBlank(data.endTime) && stringIsBlank(data.parentNo) && data.frequCode !== 'ONCE';
  253. }
  254. const rowClick = ({row}) => {
  255. emits('rowClick', row)
  256. }
  257. const endDateStyle = (item) => {
  258. if (item.endTimeTemp && tableRef.value!.isCheckedByCheckboxRow(item)) {
  259. return {
  260. width: '140px',
  261. color: 'white',
  262. backgroundColor: 'red',
  263. border: 0,
  264. }
  265. } else {
  266. return {
  267. width: '140px',
  268. border: 0,
  269. }
  270. }
  271. }
  272. function groupConversion(value: string) {
  273. if (queryParam.value.sort === OrderBy.desc) {
  274. switch (value) {
  275. case '┌':
  276. return "└";
  277. case '└':
  278. return "┌";
  279. case "丨":
  280. return "丨"
  281. default:
  282. return value;
  283. }
  284. }
  285. return value
  286. }
  287. yzMitt.on('tableScroll', (val) => {
  288. tableRef.value!.scrollTo(0, val)
  289. })
  290. yzMitt.on('clearSelected', () => {
  291. tableRef.value!.clearSelected()
  292. })
  293. yzMitt.on('scrollEndAndTwinkle', async (val: any) => {
  294. twinkleList.value = val;
  295. await nextTick()
  296. await sleep(500)
  297. if (queryParam.value.sort === OrderBy.asc) {
  298. const tmp = tableRef.value!.getData()
  299. await tableRef.value!.scrollTo(0, tmp.length * 24)
  300. } else {
  301. await tableRef.value?.scrollTo(0, 0);
  302. }
  303. })
  304. yzMitt.on('setOrderNoTwinkle', async (val: number) => {
  305. twinkleList.value[val] = true;
  306. await nextTick()
  307. await sleep(500)
  308. if (queryParam.value.sort === OrderBy.asc) {
  309. const tmp = tableRef.value!.getData()
  310. tableRef.value!.scrollTo(0, tmp.length * 24)
  311. } else {
  312. await tableRef.value?.scrollTo(0, 0);
  313. }
  314. })
  315. yzMitt.on('getSelectedData', () => {
  316. return XEUtils.clone(tableRef.value!.getCheckboxRecords(), true) as YzType[]
  317. })
  318. yzMitt.on('getTableRef', () => tableRef.value)
  319. </script>
  320. <template>
  321. <vxe-table
  322. border
  323. height="auto"
  324. sync-resize
  325. auto-resize
  326. :menu-config="menuConfig"
  327. @menu-click="tableRightClick"
  328. @cell-dblclick="setDefaultStopTime"
  329. @cell-click="rowClick"
  330. :scroll-x="{gt: 40,enabled: false}"
  331. :scroll-y="{gt: 0,enabled: true}"
  332. :column-config="{resizable: true}"
  333. :row-config="{height: 24, isHover: true, keyField:'actOrderNo'}"
  334. class="vxe-padding_zero vxe-header-max_content hl-style"
  335. header-row-class-name="padding_zero"
  336. show-header-overflow
  337. show-overflow
  338. :row-class-name="rowClassName"
  339. ref="tableRef"
  340. :data="tempYzData">
  341. <vxe-column type="checkbox" width="20">
  342. <template #header="{ checked, indeterminate }">
  343. <span class="custom-checkbox" @click.stop="toggleAllCheckboxEvent">
  344. <i v-if="indeterminate" class="vxe-icon-square-minus-fill"></i>
  345. <i v-else-if="checked" class="vxe-icon-square-checked-fill"></i>
  346. <i v-else class="vxe-icon-checkbox-unchecked"></i>
  347. </span>
  348. </template>
  349. <template #checkbox="{ row, checked, indeterminate }">
  350. <span class="custom-checkbox" @click.stop="toggleCheckboxEvent(row)">
  351. <i v-if="indeterminate" class="vxe-icon-square-minus-fill"></i>
  352. <i v-else-if="checked" class="vxe-icon-square-checked-fill"></i>
  353. <i v-else class="vxe-icon-checkbox-unchecked"></i>
  354. </span>
  355. </template>
  356. </vxe-column>
  357. <vxe-column type="seq" width="30"/>
  358. <vxe-column field="orderGroup" title="组" width="20">
  359. <template #default={row}>
  360. <span>{{ groupConversion(row['orderGroup']) }}</span>
  361. </template>
  362. </vxe-column>
  363. <vxe-column field="statusFlag" width="20">
  364. <template #default="scope">
  365. <component :is="getYiZhuFlag(scope.row.statusFlag)"/>
  366. </template>
  367. </vxe-column>
  368. <vxe-column field="actOrderNo" title="医嘱号" width="70"/>
  369. <vxe-column field="orderName" title="医嘱名称" width="225"/>
  370. <vxe-column field="dose" title="剂量" width="65">
  371. <template #default="{row}">
  372. {{ nullToEmpty(row.dose) + ' ' + nullToEmpty(row.doseUnitName) }}
  373. </template>
  374. </vxe-column>
  375. <vxe-column field="frequCode" title="频率" width="75"/>
  376. <vxe-column field="supplyCodeName" title="给药方式" width="60"/>
  377. <vxe-column field="startTime" title="开始时间" width="100">
  378. <template #default="{row}">
  379. {{ timeFomat(row.startTime) }}
  380. </template>
  381. </vxe-column>
  382. <vxe-column field="endTime" title="结束时间" width="150">
  383. <template #default="scope">
  384. <input v-if="showEndTime(scope.row)"
  385. type='datetime-local'
  386. @click.stop
  387. @change="endTimeChange(scope.row.endTimeTemp, scope.row)"
  388. @contextmenu.stop.prevent="cancelStopTime(scope.row)"
  389. :style="endDateStyle(scope.row)"
  390. v-model="scope.row.endTimeTemp"/>
  391. <span v-else>{{ timeFomat(scope.row.endTime) }}</span>
  392. </template>
  393. </vxe-column>
  394. <vxe-column field="emergencyFlag" title="紧急" width="30">
  395. <template #default="{row}">
  396. {{ row.emergencyFlag === '1' ? '√' : '' }}
  397. </template>
  398. </vxe-column>
  399. <vxe-column field="ybSelfFlag" title="自费" width="30">
  400. <template #default="{row}">
  401. {{ row.ybSelfFlag === '1' ? '√' : '' }}
  402. </template>
  403. </vxe-column>
  404. <vxe-column field="physicianName" title="医生" width="65"/>
  405. <vxe-column field="selfBuyName" title="费用标志" width="60"/>
  406. <vxe-column field="execUnitName" title="执行科室" width="80"/>
  407. <vxe-column field="drugQuan" title="领量" width="30">
  408. <template #default="{row}">
  409. {{ nullToEmpty(row.drugQuan) + nullToEmpty(row.miniUnitName) }}
  410. </template>
  411. </vxe-column>
  412. <vxe-column field="groupNoName" title="药房" width="91"/>
  413. <vxe-column field="serial" title="序号" width="35"/>
  414. <vxe-column field="instruction" title="嘱托" width="180">
  415. <template #default="scope">
  416. <input v-if="scope.row.statusFlag === '1' || scope.row.statusFlag === '2' "
  417. :title="scope.row.instruction"
  418. v-model="scope.row.instruction"
  419. :maxlength="50"
  420. @keydown.enter="instructionEnter(scope.row)"
  421. />
  422. <span v-else>{{ scope.row.instruction }}</span>
  423. </template>
  424. </vxe-column>
  425. <vxe-column field="right" title="操作" width="95" fixed="right">
  426. <template #default="{row}">
  427. <el-button-group
  428. >
  429. <el-button text
  430. type="warning"
  431. @click.stop.prevent="emits('voidOrders', row)"
  432. >
  433. 作废
  434. </el-button>
  435. <el-button text
  436. type="danger"
  437. @click.stop.prevent="yzMitt.emit('deleteAnOrderByOrderNo', row, false)"
  438. >
  439. 删除
  440. </el-button>
  441. </el-button-group>
  442. </template>
  443. </vxe-column>
  444. </vxe-table>
  445. </template>
  446. <style lang="scss">
  447. @keyframes hzfirst {
  448. from {
  449. background-color: #95d475
  450. }
  451. to {
  452. background-color: white;
  453. }
  454. }
  455. .animation_hzfirst {
  456. td {
  457. animation: hzfirst 1s linear infinite
  458. }
  459. }
  460. .activation {
  461. background-color: #5376e7cc !important;
  462. color: white;
  463. }
  464. .parent_level {
  465. background-color: red;
  466. color: white;
  467. }
  468. .child_level {
  469. background-color: rgba(3, 255, 15);
  470. color: black;
  471. }
  472. .row-normal {
  473. background-color: #f6ffed;
  474. }
  475. </style>