YzTableV3.vue 13 KB

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