Просмотр исходного кода

打印包装标签(80mm*50mm)
打印药品清单(72mm)
打印处方笺(A5)

kumu 1 год назад
Родитель
Сommit
d5d9ebed6c

+ 180 - 0
src/components/print/recipe_A5.vue

@@ -0,0 +1,180 @@
+<script>
+import CLodop from '@/libs/print/CLodop';
+import {getDevice} from '@/tools/print.tool';
+import {templateA5} from '@/components/print/template';
+import {selectOrderDetail} from '@/api/prescription/prescriptionAudit';
+
+export default {
+  name: 'print_recipe_a5',
+  props: {
+    id: {type: [String, Number], required: true},
+  },
+  data() {
+    return {
+      loaded: false,
+      preview: false,
+      total: 1,
+
+      tag: `print-preview_${Date.now()}`,
+      paper: '中药处方笺',
+
+      model: null,
+      device: null,
+      printing: false,
+    };
+  },
+  computed: {
+    loading() {
+      return !this.loaded || !this.preview;
+    },
+  },
+  mounted() {
+    this.print(true);
+  },
+  methods: {
+    async print(preview = false) {
+      const instance = await CLodop();
+      const model = await this.getModel();
+
+      templateA5.call(instance, model, `${this.paper}`);
+
+      this.device = await getDevice(`paper:${this.paper}`);
+      if (this.device) instance['SET_PRINT_PAGESIZE'](1, 0, 0, this.paper);
+      else {
+        this.device = await getDevice({width: 559, height: 794}, `name:Canon LBP`);
+        if (this.device) instance['SET_PRINT_PAGESIZE'](1, '148mm', '210mm', 'CreateCustomPage');
+      }
+      if (this.device) instance['SET_PRINTER_INDEX'](this.device.index);
+
+      instance['SET_PRINT_COPIES'](this.total);
+
+      if (preview) {
+        instance['SET_PREVIEW_WINDOW'](1, 1, 1, 0, 0, `${this.paper}.开始打印`);
+        instance['SET_SHOW_MODE']('PREVIEW_IN_BROWSE', true);
+        instance['SET_PRINT_MODE']('AUTO_CLOSE_PREWINDOW', true);
+        instance['PREVIEW'](this.tag);
+      } else if (this.device) {
+        if (instance['PRINT']()) this.complete();
+        else this.$message.warning(`请检查打印机 (${this.device.name})`);
+      } else {
+        if (instance['PRINTA']()) this.complete();
+        else this.$message.warning(`请检查打印机`);
+      }
+      this.printing = false;
+      this.loaded = true;
+    },
+    delay() {
+      setTimeout(() => {this.preview = true;}, 500);
+    },
+    complete() {
+      this.$message.success(`开始打印`);
+      this.$emit('close', true);
+    },
+
+    async getModel() {
+      if (this.model) return this.model;
+
+      return selectOrderDetail({id: this.id}).then((res) => {
+        const data = res.data;
+        const sign = (index) => { try {return data.operateList[index].operater;} catch (e) {} };
+        this.model = {
+          patient: {
+            name: `${data['name']}`,
+            gender: `${data['sex']}`,
+            arg: `${data['args']}岁`,
+            birthday: `${data['patientBirthday']}`,
+            phone: `${data['contactNumber']}`,
+          },
+          recipe: {
+            date: `${data['prescriptionTime']}`,
+            type: {1: '中药处方', 2: '中药制剂'}[data.type] || '',
+            count: data['number'],
+            category: data['dosageForm'],
+            method: data['prescriptionusage'],
+            volume: data['concentration'] && `每次${data['concentration']}`,
+            frequency: data['frequency'],
+            frequencyTime: data['medicationTime'],
+            decoction: data['isBehalf'] === '1' || +data['daijianNumber'] > 0,
+
+            delivery: data['expressExecutor'],
+            address: [data['province'], data['city'], data['region'], data['address']].filter(Boolean).join(''),
+
+            medicineFees: data['prescriptionSum'],
+            decoctionFees: data['daijianCost'],
+            deliveryFees: data['distributionCost'],
+            totalFees: data['prescriptionTotalSum'],
+
+            medicines: Array.isArray(data['detailList']) ? data['detailList'].map(item => {
+              return {
+                name: item['matName'],
+                dosage: item['matDose'],
+                unit: item['matUnitName'],
+                usage: item['matUsageName'],
+              };
+            }) : [],
+          },
+          department: [data['department']].filter(Boolean).join(' '),
+          diagnosis: [data['disName']].filter(Boolean).join(' '),
+          record: {
+            title: `${data['yljgName']}(处方笺)`,
+            date: `${data['printTime']}`,
+            no: `${data['preNo']}`,
+            category: data['preMzZy'] === '1' ? '门诊' : '住院',
+            remark: '1、本处方当日有效;\r\n2、取药时请您当面核对;\r\n3、延长处方用量时间原由:慢性病 其他老年病 外地 其他',
+          },
+          recordNo: data['recordNo'],
+          bedNo: data['bedNo'],
+          sign: {
+            field1: sign(1 - 1),
+            field2: sign(2 - 1),
+            field3: sign(3 - 1),
+            field4: sign(4 - 1),
+            field5: sign(5 - 1),
+            field6: sign(6 - 1),
+            field7: sign(7 - 1),
+            field8: sign(8 - 1),
+          },
+          field1: {1: '配药', 2: '煎药', 3: '发药', 4: '配送'}[data.state],
+        };
+        if (this.model.field1) this.model.field1 = `当前处方状态:${this.model.field1}`;
+        return this.model;
+      });
+    },
+  },
+};
+</script>
+
+<template>
+  <div class="print-preview" v-loading="loading">
+    <div class="top" :style="{backgroundColor: preview ? '#f0f0f0' : 'transparent'}"></div>
+    <iframe :id="tag" @load="delay"></iframe>
+    <div style="display: flex; align-items: center; justify-content: space-evenly;">
+      <el-button style="width: 100%;" type="primary" :loading="printing" @click="printing = true;print()">
+        打印
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.print-preview {
+  margin: auto;
+  width: 559px + 100px;
+  display: flex;
+  flex-direction: column;
+
+  .top {
+    height: 40px;
+  }
+
+  iframe {
+    border: none;
+    width: 100%;
+    flex: auto;
+  }
+
+  > div {
+    flex: none;
+  }
+}
+</style>

+ 133 - 0
src/components/print/tag_80_50.vue

@@ -0,0 +1,133 @@
+<script>
+import CLodop from '@/libs/print/CLodop';
+import {getDevice} from '@/tools/print.tool';
+import {template80_50} from '@/components/print/template';
+import {selectOrderDetail} from '@/api/prescription/prescriptionAudit';
+
+export default {
+  name: 'print_tag_80_50',
+  props: {
+    id: {type: [String, Number], required: true},
+  },
+  data() {
+    return {
+      loaded: false,
+      preview: false,
+      total: 1,
+
+      tag: `print-preview_${Date.now()}`,
+      paper: '包装标签',
+
+      model: null,
+      device: null,
+      printing: false,
+    };
+  },
+  computed: {
+    loading() {
+      return !this.loaded || !this.preview;
+    },
+  },
+  mounted() {
+    this.print(true);
+  },
+  methods: {
+    async print(preview = false) {
+      const instance = await CLodop();
+      const model = await this.getModel();
+
+      template80_50.call(instance, model, `${this.paper}`);
+
+      this.device = await getDevice(`paper:${this.paper}`);
+      if (this.device) instance['SET_PRINT_PAGESIZE'](1, 0, 0, this.paper);
+      else {
+        this.device = await getDevice({width: 60, height: 40}, `name:Gprinter GP-1324D`);
+        if (this.device) instance['SET_PRINT_PAGESIZE'](1, '60mm', '40mm', 'CreateCustomPage');
+      }
+      if (this.device) instance['SET_PRINTER_INDEX'](this.device.index);
+
+      instance['SET_PRINT_COPIES'](this.total);
+
+      if (preview) {
+        instance['SET_PREVIEW_WINDOW'](1, 1, 1, 0, 0, `${this.paper}.开始打印`);
+        instance['SET_SHOW_MODE']('PREVIEW_IN_BROWSE', true);
+        instance['SET_PRINT_MODE']('AUTO_CLOSE_PREWINDOW', true);
+        instance['PREVIEW'](this.tag);
+      } else if (this.device) {
+        if (instance['PRINT']()) this.complete();
+        else this.$message.warning(`请检查打印机 (${this.device.name})`);
+      } else {
+        if (instance['PRINTA']()) this.complete();
+        else this.$message.warning(`请检查打印机`);
+      }
+      this.printing = false;
+      this.loaded = true;
+    },
+    delay() {
+      setTimeout(() => {this.preview = true;}, 500);
+    },
+    complete() {
+      this.$message.success(`开始打印`);
+      this.$emit('close', true);
+    },
+
+    async getModel() {
+      if (this.model) return this.model;
+
+      return selectOrderDetail({id: this.id}).then((res) => {
+        const data = res.data;
+        this.model = {
+          patient: {
+            name: data['name'],
+            gender: data['sex'],
+            birthday: data['patientBirthday'],
+            phone: data['contactNumber'],
+          },
+          recipe: {
+            count: data['number'],
+            method: data['prescriptionusage'],
+            decoction: data['isBehalf'] === '1' ? '是' : '否',
+          },
+          department: [data['department']].filter(Boolean).join(' '),
+          record: {
+            title: `${data['yljgName']}(中药房)`,
+            date: data['printTime'],
+            no: data['preNo'],
+            category: data['preMzZy'] === '1' ? '门诊' : '住院',
+            remark: [data['pharmacistsremarks']].filter(Boolean).join(','),
+          },
+        };
+
+        return this.model;
+      });
+    },
+  },
+};
+</script>
+
+<template>
+  <div class="print-preview" v-loading="loading">
+    <div class="top" :style="{backgroundColor: preview ? '#f0f0f0' : 'transparent'}"></div>
+    <iframe :id="tag" @load="delay"></iframe>
+    <div style="display: flex; align-items: center; justify-content: space-evenly;">
+      <el-button style="width: 100%;" type="primary" :loading="printing" @click="printing = true;print()">打印</el-button>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.print-preview {
+  margin: auto;
+  width: 360px;
+
+  .top {
+    height: 40px;
+  }
+
+  iframe {
+    border: none;
+    width: 100%;
+    height: 280px;
+  }
+}
+</style>

+ 390 - 0
src/components/print/template.js

@@ -60,3 +60,393 @@ export function template60_40(model = {}, title = '') {
   y += h - 1;
   this.ADD_PRINT_TEXT(y, x, w, h, template(`备注:{{record.remark}}`));
 }
+
+export function template80_50(model = {}, title = '') {
+  const template = renderTemplate.bind(this, model);
+
+  const width = 302;
+  const height = 189;
+  const margin = 12;
+
+  this.PRINT_INITA(0, 0, width, height, title);
+  // 宽度按纸张的整宽缩放
+  this.SET_PRINT_MODE('PRINT_PAGE_PERCENT', 'Full-Width');
+  // 设置输出位置以纸张边缘为基点
+  this.SET_PRINT_MODE('POS_BASEON_PAPER', true);
+
+  let x = margin, y = margin;
+  let w = 0, h = 0;
+
+  y -= 2;
+  w = width - margin * 2;
+  h = 30;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`{{record.title}}`));
+  this.SET_PRINT_STYLEA(0, 'Bold', 1);
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+
+  w = 70;
+  h = 20;
+  y = 40;
+  x = width - w - margin;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`{{record.category}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+  w = x - margin;
+  this.ADD_PRINT_TEXT(y, margin, w, h, template(`{{patient.name}},{{patient.gender}},{{patient.birthday}}`));
+
+  x = margin;
+  y += h;
+  if (model.record.no) this.ADD_PRINT_BARCODE(y, x, 145, 60, '128Auto', model.record.no);
+  this.SET_PRINT_STYLEA(0, 'FontSize', 6);
+
+  this.SET_PRINT_STYLE('FontSize', 8);
+  w = 130;
+  x = width - w - margin;
+  y += 4;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`手机号:{{patient.phone}}`));
+  y += h;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`处方用法:{{recipe.method}}`));
+  y += h;
+  w = 55;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`剂数:{{recipe.count}}`));
+  x += w;
+  this.ADD_PRINT_TEXT(y, x, width - x - margin, h, template(`是否代煎:{{recipe.decoction}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+  this.SET_PRINT_STYLE('FontSize', 9);
+
+  x = margin;
+  y += h;
+  w = width - margin * 2;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`科室/病房:{{department}}`));
+  y += h;
+  h += 6;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`药师备注:{{record.remark}}`));
+  y += h;
+  h = 20;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`打印时间:{{record.date}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+}
+
+/**
+ * A5 纸 96 PPI (559px * 794px)
+ *
+ * @param model
+ * @param title
+ */
+export function templateA5(model = {}, title = '') {
+  const template = renderTemplate.bind(this, model);
+
+  const width = 559;
+  const height = 794;
+  const margin = 12;
+
+  this.PRINT_INITA(0, 0, width, height, title);
+  // 宽度按纸张的整宽缩放
+  this.SET_PRINT_MODE('PRINT_PAGE_PERCENT', 'Full-Height');
+  // 设置输出位置以纸张边缘为基点
+  this.SET_PRINT_MODE('POS_BASEON_PAPER', true);
+
+  const chunks = (function (array, size = 32) {
+    let result = [];
+    for (let i = 0; i < array.length; i += size) {result.push(array.slice(i, i + size));}
+    return result;
+  })(model.recipe['medicines']);
+  if (!chunks.length) chunks.push([]);
+  const single = chunks.length === 1;
+
+  for (const chunk of chunks) {
+    // 绘制头部
+    this.ADD_PRINT_TEXT(margin, margin, 310, 30, template(`{{record.title}}`));
+    this.SET_PRINT_STYLEA(0, 'FontSize', 16);
+    this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+    this.SET_PRINT_STYLEA(0, 'Bold', 1);
+    this.ADD_PRINT_TEXT(47, 12, 310, 20, template(`{{recipe.type}}`));
+    this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+    if (model.record.no) this.ADD_PRINT_BARCODE(12, 331, 216, 60, '128Auto', model.record.no);
+
+    // 绘制顶部信息
+    let x = 0, y = 80, h = 20;
+    let rows = [
+      [
+        {left: margin, width: 138, height: 20, text: template(`姓名:{{patient.name}}`)},
+        {width: 60, height: 20, text: template(`性别:{{patient.gender}}`)},
+        {width: 80, height: 20, text: template(`年龄:{{patient.age}}`)},
+        {width: 120, height: 20, text: template(`电话:{{patient.phone}}`)},
+      ],
+      [
+        {left: margin, width: 198, height: 20, text: template(`就诊科室:{{department}}`)},
+        {width: 80, height: 20, text: template(`床号:{{bedNo}}`)},
+        {width: 120, height: 20, text: template(`病历号:{{recordNo}}`)},
+      ],
+      [
+        {left: margin, width: 350, height: 20, text: template(`临床诊断:{{diagnosis}}`)},
+        {leftOffset: 2, width: 350, height: 20, text: template(`开方时间:{{recipe.date}}`)},
+      ],
+      [
+        {left: margin, width: 400, height: 20, text: template(`联系地址:{{recipe.address}}`)},
+        {
+          leftOffset: 2, width: 134, height: 20, text: template(`{{recipe.delivery}}`), style: {
+            Alignment: 3,
+          },
+        },
+      ],
+    ];
+    rows.forEach((row, r) => {
+      row.forEach((col, c) => {
+        if (col.top != null) y = col.top;
+        else if (r && c === 0) { y += (h + 2); }
+
+        if (col.left != null) x = col.left;
+        else if (c) {x += row[c - 1].width + (col.leftOffset || 0);}
+
+        this.ADD_PRINT_TEXT(y, x, col.width, col.height, col.text);
+        for (const [prop, value] of Object.entries(col.style || {})) {
+          this.SET_PRINT_STYLEA(0, prop, value);
+        }
+      });
+    });
+
+    this.ADD_PRINT_TEXT(80, 410, 128, 42, template(`{{field1}}`));
+    this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+    this.SET_PRINT_STYLEA(0, 'Italic', 1);
+    this.SET_PRINT_STYLEA(0, 'Underline', 1);
+
+
+    // 分割线
+    this.ADD_PRINT_SHAPE(4, 178, 12, 535, 1, 0, 1, '#000000');
+    // RP
+    this.ADD_PRINT_TEXT(192, 12, 50, 30, 'Rp:');
+    this.SET_PRINT_STYLEA(0, 'FontSize', 16);
+    this.SET_PRINT_STYLEA(0, 'Bold', 1);
+
+    // 绘制药品
+    y = 230 - 20 - 2;
+    chunk.forEach((medicine, i) => {
+      const template = renderTemplate.bind(this, medicine);
+      if (i % 2) {
+        x = 291;
+      } else {
+        y += 20 + 2;
+        x = 48;
+      }
+      this.ADD_PRINT_TEXT(y, x, 120, 20, template(`{{name}}`));
+      this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+      x += 120;
+      this.ADD_PRINT_TEXT(y, x, 50, 20, template(`{{dosage}}{{unit}}`));
+      this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+      x += 50 + 2;
+      this.ADD_PRINT_TEXT(y, x, 48, 16, template(`{{usage}}`));
+      this.SET_PRINT_STYLEA(0, 'FontSize', 6);
+    });
+
+    // 绘制处方信息
+    y = Math.max(560, y);
+    y += 20 + 8 * 2;
+    this.SET_PRINT_STYLE('Alignment', 2);
+    this.ADD_PRINT_TEXT(y, margin, 111, 20, template(`剂数:{{recipe.count}} ${model.recipe.decoction ? '(代煎)' : ''}`));
+    this.ADD_PRINT_TEXT(y, 120, 100, 20, template(`{{recipe.category}} ${model.recipe.method ? '({{recipe.method}})' : ''}`));
+    this.ADD_PRINT_TEXT(y, 229, 100, 20, template(`{{recipe.volume}}`));
+    this.ADD_PRINT_TEXT(y, 339, 100, 20, template(`{{recipe.frequency}}`));
+    this.ADD_PRINT_TEXT(y, 448, 100, 20, template(`{{recipe.frequencyTime}}`));
+    this.ADD_PRINT_SHAPE(4, 619, 12, 535, 1, 0, 1, '#000000');
+
+    // 绘制矩形
+    this.ADD_PRINT_RECT(620 + 12, 12, 240, 70, 0, 1);
+    // 第一横行线
+    this.ADD_PRINT_SHAPE(4, 654, 12, 240, 1, 0, 1, '#000000');
+    // 第二横行线
+    this.ADD_PRINT_SHAPE(4, 679, 12, 240, 1, 0, 1, '#000000');
+    // 第二行 竖线
+    this.ADD_PRINT_SHAPE(4, 655, 132, 1, 24, 0, 1, '#000000');
+    this.ADD_PRINT_TEXT(637, 13, 238, 12, template(`药品金额:¥{{recipe.medicineFees}}`));
+    this.ADD_PRINT_TEXT(661, 13, 118, 12, template(`煎药费:¥{{recipe.decoctionFees}}`));
+    this.ADD_PRINT_TEXT(661, 133, 118, 12, template(`配送费:¥{{recipe.deliveryFees}}`));
+    this.ADD_PRINT_TEXT(685, 13, 238, 12, template(`总金额:¥{{recipe.totalFees}}`));
+    this.SET_PRINT_STYLE('Alignment', 1);
+    this.SET_PRINT_STYLE('FontSize', 8);
+
+    y = 620 + 12 + 4;
+    rows = [
+      [
+        {left: 266, text: template(`开方:{{sign.field1}}`)},
+        {left: 408, text: template(`审核:{{sign.field2}}`)},
+      ],
+      [
+        {left: 266, text: template(`调配:{{sign.field3}}`)},
+        {left: 408, text: template(`复核:{{sign.field4}}`)},
+      ],
+      [
+        {left: 266, text: template(`浸泡:{{sign.field5}}`)},
+        {left: 408, text: template(`煎煮:{{sign.field6}}`)},
+      ],
+      [
+        {left: 266, text: template(`打包:{{sign.field7}}`)},
+        {left: 408, text: template(`发药:{{sign.field8}}`)},
+      ],
+    ];
+    for (const row of rows) {
+      for (const col of row) {
+        this.ADD_PRINT_TEXT(y, col.left, 140, 16, col.text);
+        this.ADD_PRINT_TEXT(y, col.left, 140, 16, col.text);
+      }
+      y += 16 + 2;
+    }
+    this.SET_PRINT_STYLE('FontSize', 9);
+    this.ADD_PRINT_SHAPE(4, 714, 12, 535, 1, 0, 1, '#000000');
+
+    if (model.record.remark) {
+      this.ADD_PRINT_TEXT(725, 12, 40, 16, '注:');
+      this.SET_PRINT_STYLEA(0, 'Bold', 1);
+      this.SET_PRINT_STYLEA(0, 'Vorient', 1);
+      this.ADD_PRINT_TEXT(725, 48, 500, 55, template(`{{record.remark}}`));
+      this.SET_PRINT_STYLEA(0, 'Vorient', 1);
+    }
+
+    if (!single) this.NEWPAGEA();
+  }
+
+}
+
+/**
+ * 药品明细清单
+ * @param model
+ * @param title
+ */
+export function template72(model, title) {
+  const template = renderTemplate.bind(this, model);
+
+  const width = 272;
+  const height = 794;
+  const margin = 0;
+
+  this.PRINT_INITA(0, 0, width, height, title);
+  // 宽度按纸张的整宽缩放
+  this.SET_PRINT_MODE('PRINT_PAGE_PERCENT', 'Full-Width');
+  // 设置输出位置以纸张边缘为基点
+  this.SET_PRINT_MODE('POS_BASEON_PAPER', true);
+
+  let x = margin, y = margin;
+  let w = 0, h = 0;
+
+  w = 70;
+  h = 20;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`{{recipe.delivery}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+  this.SET_PRINT_STYLEA(0, 'Bold', 1);
+  x += w;
+  h = 30;
+  this.ADD_PRINT_TEXT(y, x, width - x - margin, h, template(`{{record.title}}`));
+  this.SET_PRINT_STYLEA(0, 'FontSize', 16);
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+  this.SET_PRINT_STYLEA(0, 'Bold', 1);
+  x = margin;
+  y += h;
+  h = 36;
+  this.ADD_PRINT_TEXT(y, x, width - x - margin, h, template(`{{recipe.address}}`));
+  this.SET_PRINT_STYLEA(0, 'Underline', 1);
+  x = margin;
+  y += h;
+  w = 118;
+  h = 20;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`序号:{{bedNo}}`));
+  x += w;
+  this.ADD_PRINT_TEXT(y, x, width - x - margin, h, template(`处方号:{{record.no}}`));
+
+  y += h;
+  x = margin;
+
+  this.ADD_PRINT_TEXT(y, x, 118, h, template(`姓名:{{patient.name}}`));
+  this.ADD_PRINT_TEXT(y, 118, 75, 20, template(`{{recipe.method}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+  this.SET_PRINT_STYLEA(0, 'Underline', 1);
+  this.ADD_PRINT_TEXT(y, 197, 75, 20, template(`{{recipe.decoction}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+  this.SET_PRINT_STYLEA(0, 'Underline', 1);
+
+  y += h;
+  x = margin;
+  h = 96;
+  if (model.record.no) this.ADD_PRINT_BARCODE(y, x, h, h, 'QRCode', model.record.no);
+
+  x = 94;
+  h = 45 * 2;
+  this.ADD_PRINT_TEXT(y, x, 178, h, template(`备注:{{record.remark}}`));
+
+  x = margin;
+  y += h;
+
+  w = 72;
+  h = 20;
+  this.ADD_PRINT_TEXT(y, x, w, h, `药味:${model.recipe.medicines.length}}`);
+  x += w;
+  w = 100;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`帖重:{{recipe.unitWeight}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+  x += w;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`总重:{{recipe.totalWeight}}`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+
+  x = margin;
+  y += h;
+  w = 100;
+  this.ADD_PRINT_TEXT(y, x, w, h, template(`开单:{{doctor}}`));
+  x += w;
+  this.ADD_PRINT_TEXT(y, x, width - x - margin, h, template(`共 {{recipe.count}}帖 {{recipe.total}}包`));
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+
+  x = margin;
+  y += h;
+  w = 178;
+  h = 20;
+  this.ADD_PRINT_TEXT(y, x, w, h, [
+    template(`{{recipe.volume}}`),
+    template(`{{recipe.frequency}}`),
+    template(`{{recipe.frequencyTime}}`),
+  ].filter(Boolean).join(','));
+
+  w = 100;
+  this.ADD_PRINT_TEXT(y, width - w - margin, w, h, '装量:  ML');
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+
+  this.ADD_PRINT_SHAPE(4, 255, 0, 278, 1, 0, 1, '#000000');
+  this.ADD_PRINT_TEXT(265, 0, 60, 20, '库位号');
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+  this.ADD_PRINT_TEXT(265, 53, 86, 20, '药品名');
+  this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+  this.ADD_PRINT_TEXT(265, 136, 36, 20, '单帖');
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+  this.ADD_PRINT_TEXT(265, 166, 36, 20, '总量');
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+  this.ADD_PRINT_TEXT(265, 196, 36, 20, '单位');
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+  this.ADD_PRINT_TEXT(265, 228, 44, 20, '特煎');
+  this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+  this.ADD_PRINT_SHAPE(4, 285, 0, 278, 1, 0, 1, '#000000');
+  y = 285 + 12;
+
+  this.SET_PRINT_STYLE('FontSize', 8);
+  model.recipe.medicines.forEach((medicine, i) => {
+    const template = renderTemplate.bind(this, medicine);
+
+    x = margin;
+    w = 54;
+    h = 30;
+    this.ADD_PRINT_TEXT(y, x, w, h, template(`{{mark}}`));
+    x += w - 1;
+    w = 86;
+    this.ADD_PRINT_TEXT(y, x, w, h, template(`{{name}}`));
+    this.SET_PRINT_STYLEA(0, 'Alignment', 2);
+    this.ADD_PRINT_TEXT(y, 132, 40, h, template(`{{dosage}}`));
+    this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+    this.ADD_PRINT_TEXT(y, 164, 40, h, template(`{{total}}`));
+    this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+    this.ADD_PRINT_TEXT(y, 200, 22, h, template(`{{unit}}`));
+    this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+    this.ADD_PRINT_TEXT(y, 222, 50, h, template(`{{usage}}`));
+    this.SET_PRINT_STYLEA(0, 'Alignment', 3);
+    y += h + 2;
+  });
+  this.SET_PRINT_STYLE('FontSize', 9);
+  y += 12;
+  this.ADD_PRINT_SHAPE(4, y, 0, 278, 1, 0, 1, '#000000');
+
+}

+ 179 - 0
src/components/print/ticket_72.vue

@@ -0,0 +1,179 @@
+<script>
+import CLodop from '@/libs/print/CLodop';
+import {getDevice} from '@/tools/print.tool';
+import {template72} from '@/components/print/template';
+import {selectOrderDetail} from '@/api/prescription/prescriptionAudit';
+import {bignumber, chain, multiply} from 'mathjs';
+
+export default {
+  name: 'print_ticket_72',
+  props: {
+    id: {type: [String, Number], required: true},
+  },
+  data() {
+    return {
+      loaded: false,
+      preview: false,
+      total: 1,
+
+      tag: `print-preview_${Date.now()}`,
+      paper: '药品明细',
+
+      model: null,
+      device: null,
+      printing: false,
+    };
+  },
+  computed: {
+    loading() {
+      return !this.loaded || !this.preview;
+    },
+  },
+  mounted() {
+    this.print(true);
+  },
+  methods: {
+    async print(preview = false) {
+      const instance = await CLodop();
+      const model = await this.getModel();
+
+      template72.call(instance, model, `${this.paper}`);
+
+      this.device = await getDevice(`paper:${this.paper}`);
+      if (this.device) instance['SET_PRINT_PAGESIZE'](3, 0, 0, this.paper);
+      else {
+        this.device = await getDevice(`name:GP-C80180 Series`);
+        instance['SET_PRINT_PAGESIZE'](3, '72mm', 0, 'CreateCustomPage');
+      }
+      if (this.device) instance['SET_PRINTER_INDEX'](this.device.index);
+
+      instance['SET_PRINT_COPIES'](this.total);
+
+      if (preview) {
+        instance['SET_PREVIEW_WINDOW'](1, 1, 1, 0, 0, `${this.paper}.开始打印`);
+        instance['SET_SHOW_MODE']('PREVIEW_IN_BROWSE', true);
+        instance['SET_PRINT_MODE']('AUTO_CLOSE_PREWINDOW', true);
+        instance['PREVIEW'](this.tag);
+      } else if (this.device) {
+        if (instance['PRINT']()) this.complete();
+        else this.$message.warning(`请检查打印机 (${this.device.name})`);
+      } else {
+        if (instance['PRINTA']()) this.complete();
+        else this.$message.warning(`请检查打印机`);
+      }
+      this.printing = false;
+      this.loaded = true;
+    },
+    delay() {
+      setTimeout(() => {this.preview = true;}, 500);
+    },
+    complete() {
+      this.$message.success(`开始打印`);
+      this.$emit('close', true);
+    },
+
+    async getModel() {
+      if (this.model) return this.model;
+
+      return selectOrderDetail({id: this.id}).then((res) => {
+        const data = res.data;
+        const count = bignumber(data['number'] || 0);
+        this.model = {
+          patient: {
+            name: `${data['name']}`,
+            gender: `${data['sex']}`,
+            arg: `${data['args']}岁`,
+            birthday: `${data['patientBirthday']}`,
+            phone: `${data['contactNumber']}`,
+          },
+          recipe: {
+            date: `${data['prescriptionTime']}`,
+            type: {1: '中药处方', 2: '中药制剂'}[data.type] || '',
+            count: data['number'],
+            total: data['packageNumber'],
+            category: data['dosageForm'],
+            method: data['prescriptionusage'],
+            volume: data['concentration'] && `每次${data['concentration']}`,
+            frequency: data['frequency'],
+            frequencyTime: data['medicationTime'],
+            decoction: (data['isBehalf'] === '1' || +data['daijianNumber'] > 0) ? '代煎' : '自煎',
+
+            delivery: data['expressExecutor'],
+            address: [data['province'], data['city'], data['region'], data['address']].filter(Boolean).join(''),
+
+            unitWeight: '',
+            totalWeight: '',
+
+            medicines: Array.isArray(data['detailList']) ? data['detailList'].map(item => {
+              return {
+                mark: item['locatorNum'],
+                name: item['matName'],
+                dosage: item['matDose'],
+                unit: item['matUnitName'],
+                usage: item['matUsageName'],
+                total: +multiply(count, bignumber(item['matDose'] || 0)).toFixed(2),
+              };
+            }) : [],
+          },
+          doctor: data['doctor'],
+          department: [data['department']].filter(Boolean).join(' '),
+          diagnosis: [data['disName']].filter(Boolean).join(' '),
+          record: {
+            title: `${data['yljgName']}`,
+            date: data['printTime'],
+            no: data['preNo'],
+            category: data['preMzZy'] === '1' ? '门诊' : '住院',
+            remark: [data['pharmacistsremarks']].filter(Boolean).join(','),
+          },
+          recordNo: data['recordNo'],
+          bedNo: data['bedNo'],
+        };
+
+        let unitWeight = chain(bignumber(0));
+        for (const medicine of this.model.recipe.medicines) {
+          unitWeight = unitWeight.add(bignumber(medicine.dosage));
+        }
+
+        this.model.recipe.unitWeight = +unitWeight.valueOf().toFixed(2);
+        this.model.recipe.totalWeight = +unitWeight.multiply(count).valueOf().toFixed(2);
+
+        return this.model;
+      });
+    },
+  },
+};
+</script>
+
+<template>
+  <div class="print-preview" v-loading="loading">
+    <div class="top" :style="{backgroundColor: preview ? '#f0f0f0' : 'transparent'}"></div>
+    <iframe :id="tag" @load="delay"></iframe>
+    <div style="display: flex; align-items: center; justify-content: space-evenly;">
+      <el-button style="width: 100%;" type="primary" :loading="printing" @click="printing = true;print()">打印
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.print-preview {
+  margin: auto;
+  width: 360px;
+  display: flex;
+  flex-direction: column;
+
+  .top {
+    height: 40px;
+  }
+
+  iframe {
+    border: none;
+    width: 100%;
+    flex: auto;
+  }
+
+  > div {
+    flex: none;
+  }
+}
+</style>

+ 13 - 2
src/views/rescription/prescriptionCore/index.vue

@@ -255,7 +255,7 @@
             size="mini"
             class="printbtn"
             style="width: 40px"
-            @click="consoleBtn(scope.row)"
+            @click="showPrintContentMethod(scope.row)"
             v-if="scope.row.checkState == 1"
             >打印</el-button
           >
@@ -1306,6 +1306,10 @@
     <el-dialog :fullscreen="true" title="新增处方" :visible.sync="recipeEditOpen" width="1180px" append-to-body size="mini" destroy-on-close>
       <edit @close="recipeEditOpen = false;$event && getList()"></edit>
     </el-dialog>
+
+    <el-dialog title="打印预览" :visible.sync="showPrint" @closed="showPrintId = ''">
+      <print-container v-if="showPrintId" :id="showPrintId"></print-container>
+    </el-dialog>
   </div>
 </template>
 
@@ -1342,6 +1346,7 @@ import Pres from "@/components/Pres/index.vue";
 import JsBarcode from "@/components/JsBarcode/index.vue";
 import Edit from "./edit.vue";
 import dayjs from "dayjs";
+import PrintContainer from '@/views/rescription/prescriptionCore/printContainer.vue';
 
 // import JsBarcode from "jsbarcode"
 
@@ -1362,7 +1367,7 @@ function debounce(fn, delay) {
 
 export default {
   name: "PrescriptionCore",
-  components: { Pres, JsBarcode, Edit },
+  components: {PrintContainer, Pres, JsBarcode, Edit },
   data() {
     const now = dayjs().format("YYYY-MM-DD");
     return {
@@ -1470,6 +1475,8 @@ export default {
       open: false,
       // print相关
       openPrint: false,
+      showPrint: false, // 打印页面弹窗
+      showPrintId: '', // 打印页面弹窗
       activePrint: "标签",
       printTypeList: [
         { name: "标签", id: 0 },
@@ -1829,6 +1836,10 @@ export default {
       this.dialogFormVisible = false;
     },
     //打印
+    showPrintContentMethod(row) {
+      this.showPrint = true;
+      this.showPrintId = row.id;
+    },
     consoleBtn(row) {
       this.reset();
       this.activePrint = "标签";

+ 78 - 0
src/views/rescription/prescriptionCore/printContainer.vue

@@ -0,0 +1,78 @@
+<script>
+import Print_tag_80_50 from '@/components/print/tag_80_50.vue';
+import Print_recipe_a5 from '@/components/print/recipe_A5.vue';
+import Print_ticket_72 from '@/components/print/ticket_72.vue';
+import {updatePrintStatus} from '@/api/prescription/prescriptionCore';
+
+export default {
+  name: 'printContainer',
+  props: {
+    id: {type: [String, Number], required: true},
+  },
+  components: {Print_ticket_72, Print_recipe_a5, Print_tag_80_50},
+  data() {
+    return {
+      selected: 0,
+    };
+  },
+  computed: {
+    height() { return `${window.innerHeight * 0.8}`; },
+    style() { return {height: `${this.height}px`}; },
+  },
+  watch: {
+    selected(index) {
+      try {
+        const el = this.$refs[`print${index}`];
+        el['print'](true);
+      } catch (e) {}
+    },
+  },
+  methods: {
+    async print() {
+      const keys = Object.keys(this.$refs).filter(key => key.startsWith('print'));
+      try {
+        for (const key of keys) {
+          const el = this.$refs[key];
+          await el['print']();
+        }
+        this.$message.success(`组合打印成功`);
+      } catch (e) {
+        this.$message.error(`出错了,请重试`);
+      }
+    },
+    update(type) {
+      if (type === 0 || type === 1) {
+        updatePrintStatus({
+          isPrint: '1',
+          id: this.id,
+        });
+      }
+    },
+  },
+};
+</script>
+
+<template>
+  <div>
+    <el-button class="pin" type="primary" @click="print">组合打印</el-button>
+    <el-tabs tab-position="left" :style="style" v-model="selected">
+      <el-tab-pane label="标签" name="0">
+        <print_tag_80_50 ref="print0" :id="id" @click="$event && update(0)"></print_tag_80_50>
+      </el-tab-pane>
+      <el-tab-pane label="处方笺" name="1">
+        <print_recipe_a5 ref="print1" :id="id" :style="style" @click="$event && update(1)"></print_recipe_a5>
+      </el-tab-pane>
+      <el-tab-pane label="药品清单" name="2">
+        <print_ticket_72 ref="print2" :id="id" :style="style" @click="$event && update(2)"></print_ticket_72>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.pin {
+  position: absolute;
+  top: 12px;
+  right: 60px;
+}
+</style>