PrintInfusionCard.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. <template>
  2. <page-layer>
  3. <template #header>
  4. <el-select v-model="queryParams.wardCode" style="width: 88px" @change="handleWardChange">
  5. <el-option v-for="item in userWards" :value="item.value" :label="item.label"></el-option>
  6. </el-select>
  7. <span class="ml12">
  8. 执行日期:<el-date-picker v-model="queryParams.executeDate" :clearable="false" style="width: 102px"></el-date-picker>
  9. </span>
  10. <span class="ml12">床位范围:
  11. <el-select v-model="queryParams.bedNoStart" style="width: 70px" value-key="bedNo"
  12. @change="handleStartBedChange">
  13. <el-option v-for="item in patientBedList" :key="item.bedNo" :value="item">
  14. {{ item.bedNoLabel }}
  15. <el-divider direction="vertical"></el-divider>
  16. <span class="green-text">{{ item.patNo }}</span>
  17. <el-divider direction="vertical"></el-divider>
  18. <span class="blue-text">{{ item.patName }}</span>
  19. </el-option>
  20. </el-select>
  21. <span style="margin: 0 4px; font-size: 11px">至</span>
  22. <el-select v-model="queryParams.bedNoEnd" style="width: 70px" value-key="bedNo" @change="handleEndBedChange">
  23. <el-option v-for="item in patientBedList" :key="item.bedNo" :value="item"
  24. :disabled="item.sortNo < queryParams.sortNoStart">
  25. {{ item.bedNoLabel }}
  26. <el-divider direction="vertical"></el-divider>
  27. <span :class="item.sortNo < queryParams.sortNoStart ? 'is-disabled' : 'green-text'">{{ item.patNo }}</span>
  28. <el-divider direction="vertical"></el-divider>
  29. <span :class="item.sortNo < queryParams.sortNoStart ? 'is-disabled' : 'blue-text'">{{ item.patName }}</span>
  30. </el-option>
  31. </el-select>
  32. </span>
  33. <span class="ml12">
  34. 频率:
  35. <el-select v-model="queryParams.frequency" style="width: 60px">
  36. <el-option value="ALL" label="全部"></el-option>
  37. <el-option value="ALWAYS" label="长期"></el-option>
  38. <el-option value="ONCE" label="临时"></el-option>
  39. </el-select>
  40. </span>
  41. <span class="ml12">
  42. 打印范围:
  43. <el-select v-model="queryParams.printRange" style="width: 76px">
  44. <el-option value="ALL" label="全部"></el-option>
  45. <el-option value="UNPRINTED" label="新增"></el-option>
  46. <el-option value="PRINTED" label="已打印"></el-option>
  47. </el-select>
  48. </span>
  49. <span class="ml12">
  50. 类型:
  51. <el-select v-model="queryParams.cardType" @change="handleTypeChange" style="width: 76px">
  52. <el-option value="INFUSION_CARD" label="输液卡"></el-option>
  53. <el-option value="BOTTLE_CARD" label="瓶贴"></el-option>
  54. </el-select>
  55. </span>
  56. <el-divider direction="vertical"></el-divider>
  57. <el-button type="primary" icon="Search" @click="executeQuery">检索</el-button>
  58. <el-button type="primary" icon="Printer" @click="execPrint">打印</el-button>
  59. </template>
  60. <template #main>
  61. <div style="display: flex; flex-wrap: wrap" id="infusion_list">
  62. <div v-if="queryParams.cardType === 'INFUSION_CARD'">
  63. <div v-for="(item, index) in patientInfusionInfos" :id="item.patInfo.patNo">
  64. <div class="crad-area"
  65. style="width: 90%; margin: 8px 12px; padding: 4px; border: 1px solid red ;display: flex;justify-content: center; align-items: center;">
  66. <infusion-card :drug-groups="item.drugGroups" :pat-info="item.patInfo" :yz-date="queryParams.executeDate"
  67. :print-date="printDate">
  68. </infusion-card>
  69. </div>
  70. </div>
  71. </div>
  72. <div v-else id="print-test">
  73. <div class="crad-area" v-for="(item, index) in patientInfusionInfos" :id="item.patInfo.patNo + '-' + index"
  74. style="width: 230px;display: inline-block;margin-right: 20px">
  75. <bottle-card :drugs="item.bottleCardDrugs" :pat-info="item.patInfo" :yz-date="queryParams.executeDate"
  76. :bottle-group="item.bottleGroup" style="margin-bottom: 10px;">
  77. </bottle-card>
  78. </div>
  79. </div>
  80. <!-- <div class="print-hiden-area" style="margin: 0px 12px">
  81. <div class="print-bottle-card-area" style="display: flex;margin: 0px;">
  82. <div v-for="(item, index) in printPatientBottleInfos" :id="item.patInfo.patNo + '-' + index" style="width: 240px;margin-right: 10px;">
  83. <bottle-card :drugs="item.bottleCardDrugs" :pat-info="item.patInfo" :yz-date="queryParams.executeDate"
  84. :bottle-group="item.bottleGroup" style="margin-bottom: 10px;">
  85. </bottle-card>
  86. </div>
  87. <div v-if="printPatientBottleInfos.length != 0 && printPatientBottleInfos.length % 2 != 0"
  88. style="width: 230px;">
  89. </div>
  90. </div>
  91. </div> -->
  92. </div>
  93. </template>
  94. </page-layer>
  95. </template>
  96. <script setup>
  97. import PageLayer from "@/layout/PageLayer.vue";
  98. import { formatDate, getDatetime, getDate } from "@/utils/date";
  99. import {
  100. getPatientBeds,
  101. queryInfusionCardOrBottleCard,
  102. updatePrintStatus
  103. } from '@/api/inpatient/nurse-module/print-infusion-card'
  104. import InfusionCard from "@/components/inpatient/nurse-module/InfusionCard.vue";
  105. import { ElMessage } from "element-plus";
  106. import { getLodop, initLodop } from "@/utils/c-lodop";
  107. import BottleCard from "@/components/inpatient/nurse-module/BottleCard.vue";
  108. import { getWardsApi } from "@/api/login";
  109. const userWards = ref([])
  110. const patientBedList = ref([])
  111. const printDate = ref(null)
  112. const handleWardChange = (wardCode) => {
  113. getPatientBeds(wardCode).then(res => {
  114. patientBedList.value = res
  115. if (res.length > 0) {
  116. queryParams.bedNoStart = res[0].bedNoLabel
  117. queryParams.sortNoStart = res[0].sortNo
  118. queryParams.bedNoEnd = res[res.length - 1].bedNoLabel
  119. queryParams.sortNoEnd = res[res.length - 1].sortNo
  120. } else {
  121. queryParams.bedNoStart = null
  122. queryParams.sortNoStart = null
  123. queryParams.bedNoEnd = null
  124. queryParams.sortNoEnd = null
  125. }
  126. })
  127. }
  128. const queryParams = reactive({
  129. executeDate: getDate(),
  130. wardCode: null,
  131. bedNoStart: null,
  132. bedNoEnd: null,
  133. sortNoStart: null,
  134. sortNoEnd: null,
  135. frequency: 'ALWAYS',
  136. printRange: 'UNPRINTED',
  137. cardType: 'INFUSION_CARD',
  138. bedNos: []
  139. })
  140. const patientInfusionInfos = ref([])
  141. const printPatientBottleInfos = ref([])
  142. const handleStartBedChange = (bed) => {
  143. queryParams.bedNoStart = bed.bedNoLabel
  144. queryParams.sortNoStart = bed.sortNo
  145. }
  146. const handleEndBedChange = (bed) => {
  147. queryParams.bedNoEnd = bed.bedNoLabel
  148. queryParams.sortNoEnd = bed.sortNo
  149. }
  150. const handleTypeChange = () => {
  151. patientInfusionInfos.value = []
  152. }
  153. const executeQuery = () => {
  154. queryParams.executeDate = formatDate(queryParams.executeDate)
  155. queryParams.bedNos = []
  156. patientBedList.value.forEach(bed => {
  157. if (bed.sortNo >= queryParams.sortNoStart && bed.sortNo <= queryParams.sortNoEnd) {
  158. queryParams.bedNos.push(bed.bedNo)
  159. }
  160. })
  161. queryInfusionCardOrBottleCard(queryParams).then(res => {
  162. patientInfusionInfos.value = res
  163. if (queryParams.cardType !== 'INFUSION_CARD' && patientInfusionInfos.value.length > 0) {
  164. //瓶贴分配药品组号
  165. patientInfusionInfos.value.forEach(item => {
  166. let group = patientInfusionInfos.value.filter(citem => citem.patInfo.patNo === item.patInfo.patNo)
  167. group.forEach((v, vindex) => {
  168. let target = patientInfusionInfos.value.find(citem => citem.bottleCardDrugs[0].actOrderNo === v.bottleCardDrugs[0].actOrderNo)
  169. target.bottleGroup = vindex + 1 + "/" + group.length
  170. // console.log("target",target)
  171. })
  172. // console.log("group",group)
  173. // console.log("index")
  174. })
  175. // console.log("patientInfusionInfo s",patientInfusionInfos.value)
  176. }
  177. // for (let i = 0; i < 2; i++) {
  178. // patientInfusionInfos.value = [...patientInfusionInfos.value, ...res]
  179. // }
  180. // printPatientBottleInfos.value = res
  181. printDate.value = getDatetime()
  182. }).catch(() => {
  183. patientInfusionInfos.value = []
  184. })
  185. }
  186. const execPrint = () => {
  187. const type = queryParams.cardType === 'INFUSION_CARD' ? '输液卡' : '瓶贴卡'
  188. if (patientInfusionInfos.value.length === 0) {
  189. ElMessage({
  190. message: `请先检索${type}。`,
  191. type: 'warning',
  192. duration: 2500,
  193. showClose: true
  194. })
  195. return
  196. }
  197. let LODOP = getLodop();
  198. if (queryParams.cardType == 'INFUSION_CARD') {
  199. let prntContent = document.getElementById("infusion_list").innerHTML
  200. // console.log("prntContent",prntContent)
  201. let printHTML = ""
  202. const prntStyle = `<style>td{border: 1px solid black;
  203. font-size: 12px;padding: 2px;}</style>`
  204. if (patientInfusionInfos.value.length == 1) {
  205. let id = queryParams.cardType === 'INFUSION_CARD'
  206. ? patientInfusionInfos.value[0].patInfo.patNo : patientInfusionInfos.value[0].patInfo.patNo + '-' + i
  207. printHTML = document.getElementById(id).innerHTML
  208. LODOP.PRINT_INIT('infusionCard')
  209. LODOP.SET_PRINT_PAGESIZE(1, '210mm', '297mm', '')
  210. LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', prntStyle + '<body style:"display: flex;justify-content: center;align-items: center;">' + printHTML + '</body>')
  211. LODOP.SET_PRINT_STYLE('ItemType', 3)
  212. LODOP.PREVIEW();
  213. } else {
  214. for (let i = 0; i < patientInfusionInfos.value.length; i++) {
  215. let info = patientInfusionInfos.value[i]
  216. let id = queryParams.cardType === 'INFUSION_CARD'
  217. ? info.patInfo.patNo : info.patInfo.patNo + '-' + i
  218. let printCount = queryParams.cardType === 'INFUSION_CARD'
  219. ? 3 : 6
  220. // console.log("printCount",printCount)
  221. if (i !== 0 && ((i + 1) % printCount == 0 || i == patientInfusionInfos.value.length - 1)) {
  222. // console.log("printIndex",i)
  223. printHTML = document.getElementById(id).innerHTML + printHTML
  224. let printFinal = prntStyle + '<body><div style:"width:600px;display: flex;justify-content: center;align-items: center;flex-wrap: wrap;">' + printHTML + '</div></body>';
  225. console.log("printFinal", printFinal)
  226. LODOP.PRINT_INIT('infusionCard')
  227. LODOP.SET_PRINT_PAGESIZE(1, '210mm', '297mm', '')
  228. // LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm',prntStyle + '<body style:"width:800px;display: flex;justify-content: center;align-items: center;flex-wrap: wrap;">' + printHTML + '</body>' )
  229. LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', printFinal)
  230. LODOP.SET_PRINT_STYLE('ItemType', 3)
  231. // LODOP.PREVIEW();
  232. LODOP.PRINT()
  233. printHTML = ""
  234. } else {
  235. printHTML += document.getElementById(id).innerHTML
  236. }
  237. }
  238. }
  239. updatePrintState()
  240. } else {
  241. printPatientBottleInfos.value = [...patientInfusionInfos.value]
  242. // console.log("patientInfusionInfos",patientInfusionInfos.value)
  243. // console.log("printPatientBottleInfos",printPatientBottleInfos.value)
  244. nextTick(() => {
  245. LODOP.PRINT_INIT('infusionCard');
  246. LODOP.SET_PRINT_PAGESIZE(1, '210mm', '297mm', '');
  247. const prntStyle = `<style>
  248. .card-container { display: inline-block; } /* 一行3个,预留间距 */
  249. td { border: 1px solid black; font-size: 12px; padding: 2px; }
  250. </style>`;
  251. const PAGE_MAX_HEIGHT = 1100; // 页面最大高度阈值(px)
  252. let currentPageHTML = prntStyle; // 当前页内容
  253. let currentPageTotalHeight = 0; // 当前页累计高度(px)
  254. let currentRowElements = []; // 当前行的元素
  255. let currentRowMaxHeight = 0; // 当前行最高元素高度(px)
  256. patientInfusionInfos.value.forEach((item, index) => {
  257. const printTarget = document.getElementById(`${item.patInfo.patNo}-${index}`);
  258. if (!printTarget) return;
  259. // 获取当前元素高度(px)
  260. const itemHeight = printTarget.offsetHeight;
  261. // 1. 处理当前行:添加元素并更新行最高高度
  262. currentRowElements.push(printTarget);
  263. currentRowMaxHeight = Math.max(currentRowMaxHeight, itemHeight);
  264. // 2. 当行内元素满3个时,计算行高并判断是否分页
  265. if (currentRowElements.length === 3) {
  266. // 计算当前行加入后,页面的总高度
  267. const totalHeightAfterAdd = currentPageTotalHeight + currentRowMaxHeight;
  268. // 3. 判断是否需要分页
  269. if (totalHeightAfterAdd > PAGE_MAX_HEIGHT) {
  270. // 先打印当前页已有的内容
  271. LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', currentPageHTML);
  272. LODOP.NewPage(); // 开启新页
  273. // 新页初始化:当前行作为新页的第一行
  274. currentPageHTML = prntStyle;
  275. currentPageTotalHeight = currentRowMaxHeight;
  276. } else {
  277. // 不需要分页,累加当前行高度
  278. currentPageTotalHeight = totalHeightAfterAdd;
  279. }
  280. // 4. 将当前行的3个元素添加到页面HTML
  281. currentRowElements.forEach(el => {
  282. currentPageHTML += `<div class="card-container">${el.outerHTML}</div>`;
  283. });
  284. // 5. 重置行状态,准备下一行
  285. currentRowElements = [];
  286. currentRowMaxHeight = 0;
  287. }
  288. });
  289. // 6. 处理最后一行未填满3个元素的情况
  290. if (currentRowElements.length > 0) {
  291. // 计算剩余行加入后的总高度
  292. const totalHeightAfterAdd = currentPageTotalHeight + currentRowMaxHeight;
  293. // 如果超出页面高度,单独分页
  294. if (totalHeightAfterAdd > PAGE_MAX_HEIGHT) {
  295. LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', currentPageHTML);
  296. LODOP.NewPage();
  297. currentPageHTML = prntStyle;
  298. currentPageTotalHeight = currentRowMaxHeight;
  299. } else {
  300. currentPageTotalHeight = totalHeightAfterAdd;
  301. }
  302. // 添加剩余元素到页面
  303. currentRowElements.forEach(el => {
  304. currentPageHTML += `<div class="card-container">${el.outerHTML}</div>`;
  305. });
  306. }
  307. // 7. 添加最后一页内容
  308. if (currentPageHTML) {
  309. LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', currentPageHTML);
  310. }
  311. LODOP.PREVIEW();
  312. // LODOP.PRINT_INIT('infusionCard')
  313. // LODOP.SET_PRINT_PAGESIZE(1, '210mm', '297mm', '')
  314. // let printHTML = "";
  315. // let pageHeightNow = 0;
  316. // let pageHeighInRow = 0;
  317. // let rowCount = 0;
  318. // let isWarp = false;
  319. // let isRowHeightNew = true
  320. // const prntStyle = `<style>td{border: 1px solid black;
  321. // font-size: 12px;padding: 2px;}</style>`
  322. // patientInfusionInfos.value.forEach((item, index) => {
  323. // let printTarget = document.getElementById(item.patInfo.patNo + '-' + index);
  324. // console.log("printTarget", printTarget.offsetHeight)
  325. // if (printTarget.offsetHeight > pageHeighInRow) {
  326. // pageHeighInRow = printTarget.offsetHeight;
  327. // isRowHeightNew = true;
  328. // }
  329. // // console.log("pageHeighInRow", pageHeighInRow)
  330. // // console.log("pageHeightNow", pageHeightNow)
  331. // // console.log("HeightAll", pageHeighInRow + pageHeightNow)
  332. // if (isWarp) {
  333. // isWarp = false
  334. // // if(isRowHeightNew)
  335. // if (isRowHeightNew && pageHeighInRow + pageHeightNow > 900) {
  336. // // console.log("11")
  337. // // 此时超出一页
  338. // // LODOP.PRINT()
  339. // rowCount = 0
  340. // LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', prntStyle + printHTML)
  341. // LODOP.NewPage(); // 关键:开启新页
  342. // printHTML = printTarget.outerHTML;
  343. // pageHeightNow = pageHeighInRow;
  344. // // isWarp = true
  345. // rowCount = 1; // 重置行计数
  346. // } else {
  347. // // console.log("2")
  348. // // pageHeightNow = pageHeighInRow + pageHeightNow;
  349. // printHTML += printTarget.outerHTML;
  350. // }
  351. // } else {
  352. // rowCount++;
  353. // isRowHeightNew = false
  354. // if (rowCount == 3) {
  355. // // 换行
  356. // isWarp = true;
  357. // rowCount = 0;
  358. // isRowHeightNew = true;
  359. // pageHeightNow = pageHeighInRow + pageHeightNow;
  360. // console.log('pageHeightNow',pageHeightNow)
  361. // }
  362. // printHTML += printTarget.outerHTML;
  363. // }
  364. // });
  365. // if (printHTML) {
  366. // LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', prntStyle + printHTML);
  367. // }
  368. // let prntContent1 = document.getElementById("print-test").innerHTML
  369. // let flexStyles = getStyleText('.print-bottle-card-area');
  370. // // const prntStyle = `<style>${flexStyles}td{border: 1px solid black;
  371. // // font-size: 12px;padding: 2px;}</style>`
  372. // // let printFinal = prntStyle + prntContent1
  373. // // console.log("prntContent1", prntContent1)
  374. // // LODOP.ADD_PRINT_HTML('0mm', '0mm', '210mm', '297mm', printFinal)
  375. // // LODOP.SET_PRINT_STYLE('ItemType', 3)
  376. // LODOP.PREVIEW();
  377. printPatientBottleInfos.value = []
  378. updatePrintState()
  379. });
  380. }
  381. }
  382. function updatePrintState() {
  383. const params = {
  384. cardType: queryParams.cardType,
  385. orderNos: []
  386. }
  387. patientInfusionInfos.value.forEach(info => {
  388. if (params.cardType === 'INFUSION_CARD') {
  389. info.drugGroups.forEach(group => {
  390. params.orderNos.push(group.mainDrug.actOrderNo)
  391. })
  392. } else {
  393. info.bottleCardDrugs.forEach(group => {
  394. params.orderNos.push(group.actOrderNo)
  395. })
  396. }
  397. })
  398. nextTick(() => {
  399. updatePrintStatus(params).then(res => {
  400. console.log(res)
  401. })
  402. })
  403. }
  404. function getStyleText(selector) {
  405. var sheets = document.styleSheets;
  406. var styleText = '';
  407. for (var i = 0; i < sheets.length; i++) {
  408. try {
  409. var rules = sheets[i].cssRules || sheets[i].rules;
  410. if (rules) {
  411. for (var j = 0; j < rules.length; j++) {
  412. if (rules[j].selectorText && rules[j].selectorText.includes(selector)) {
  413. styleText += rules[j].cssText + '\n';
  414. }
  415. }
  416. }
  417. } catch (e) { /* 可能跨域安全错误 */ }
  418. }
  419. return styleText;
  420. }
  421. // J,S,JS,SJ
  422. // -- 小输液卡
  423. // -- 已打印:print_comment like '%S%'
  424. // -- 未打印:isnull(print_comment,'') not like '%S%'
  425. //
  426. // -- 已打印:print_comment like '%J%'
  427. // -- 未打印:isnull(print_comment,'') not like '%J%'
  428. onMounted(() => {
  429. initLodop()
  430. getWardsApi().then(res => {
  431. userWards.value = res
  432. if (!queryParams.wardCode) {
  433. queryParams.wardCode = userWards.value[0].value
  434. }
  435. handleWardChange(queryParams.wardCode)
  436. })
  437. })
  438. </script>
  439. <style scoped>
  440. .ml12 {
  441. margin-left: 12px;
  442. }
  443. .green-text {
  444. color: green;
  445. }
  446. .blue-text {
  447. color: #1146f3
  448. }
  449. .is-disabled {
  450. color: #a8abb2;
  451. }
  452. .crad-area {
  453. width: 100%;
  454. display: flex;
  455. justify-content: center;
  456. align-items: center;
  457. }
  458. .print-bottle-card-area {
  459. width: 730px;
  460. display: flex;
  461. justify-content: space-between;
  462. align-items: center;
  463. flex-wrap: wrap;
  464. /* float: left; */
  465. padding: 15px 5px;
  466. }
  467. .print-hiden-area {
  468. /* display: none; */
  469. }
  470. @media print {
  471. .your-flex-container {
  472. display: flex !important;
  473. flex-direction: row !important;
  474. /* 其他必要flex属性 */
  475. }
  476. .flex-item {
  477. flex: 1 !important;
  478. }
  479. .print-bottle-card-area {
  480. width: 700px;
  481. display: flex;
  482. justify-content: center;
  483. /* align-items: center; */
  484. flex-wrap: wrap;
  485. }
  486. }
  487. </style>