YzTableV3.vue 13 KB

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