Purpose 5 mēneši atpakaļ
vecāks
revīzija
b537c56ca7

+ 3 - 3
src/api/TagApplyApi.js

@@ -1,9 +1,9 @@
 import BaseCurdApi from './BaseCurdApi'
+import QsUtil from '@@/utils/QsUtil'
 
 class TagApplyApi extends BaseCurdApi {
   importData = (data) => {
     const formData = new FormData()
-    formData.append('providerId', data.providerId)
     formData.append('file', data.file)
     return this.api({
       url: `${this.basePath}/data/import`,
@@ -15,8 +15,8 @@ class TagApplyApi extends BaseCurdApi {
     })
   }
 
-  downloadTemplate = () => this.api({
-    url: `${this.basePath}/download/template`,
+  downloadTemplate = (query) => this.api({
+    url: `${this.basePath}/download/template${QsUtil.stringify(query)}`,
     method: 'GET',
     responseType: 'blob'
   })

+ 17 - 0
src/api/TagCodeApi.js

@@ -1,6 +1,23 @@
 import BaseCurdApi from './BaseCurdApi'
 
 class TagCodeApi extends BaseCurdApi {
+  getQrData = (tagId) => this.api({
+    url: `${this.basePath}/qr/${tagId}`,
+    method: 'GET'
+  })
+
+  batchPrint = (data) => this.api({
+    url: `${this.basePath}/qr/batch-print`,
+    method: 'POST',
+    data
+  })
+
+  print = data => this.api({
+    url: `${this.basePath}/qr/print`,
+    method: 'POST',
+    data
+  })
+
   constructor () {
     super('/tag/code')
   }

+ 79 - 0
src/assets/main.scss

@@ -71,3 +71,82 @@
 .height-text-color {
   color: var(--height-text-color);
 }
+
+.tag {
+  --scalc: 2;
+  --border-color: #DBDBDB;
+  --empty-color: #ABABAB;
+
+  width: calc(90mm * var(--scalc));
+  background: #ffffff;
+
+  .tag-info {
+    margin: calc(2mm * var(--scalc)) calc(5mm * var(--scalc)) calc(2.5mm * var(--scalc));
+  }
+
+  .tag-logo {
+    width: calc(16mm * var(--scalc));
+    height: calc(5mm * var(--scalc));
+  }
+
+  .tag-materiel-info {
+    font-size: calc(10px * var(--scalc));
+    height: calc(8.5mm * var(--scalc));
+    margin-top: calc(2mm * var(--scalc));
+
+    &:empty {
+      border: 1px dashed var(--border-color);
+
+      &:before {
+        content: "物料名称,";
+        margin-left: calc(5mm * var(--scalc));
+        color: var(--empty-color);
+      }
+
+      &:after {
+        content: "(包装数量)";
+        margin-left: calc(2.15mm * var(--scalc));
+        color: var(--empty-color);
+      }
+    }
+  }
+
+  .tag-provider-name {
+    font-size: calc(9px * var(--scalc));
+    height: calc(4mm * var(--scalc));
+    margin-top: calc(6mm * var(--scalc));
+
+    &:empty {
+      border: 1px dashed var(--border-color);
+      &:before {
+        content: "供应商名称";
+        color: var(--empty-color);
+      }
+    }
+  }
+
+  .tag-qr {
+    width: calc(27mm * var(--scalc));
+    height: calc(27mm * var(--scalc));
+    margin: calc(1.5mm * var(--scalc)) calc(2.5mm * var(--scalc));
+
+    &:empty {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      border: 1px dashed var(--border-color);
+      color: var(--empty-color);
+      background: #F2F2F2;
+
+      &:before {
+        content: "二维码";
+      }
+    }
+  }
+
+  // > .logo {
+  //   width: 32mm;
+  //   height: 10mm;
+  //   margin: 1mm 0;
+  // }
+}

+ 0 - 40
src/components/DialogImport.vue

@@ -9,28 +9,9 @@
       ref="form"
       :model="uploadData"
       label-width="80px"
-      style="height:200px"
     >
       <el-collapse v-model="activeCollapseName" class="form-collapse">
         <el-collapse-item title="采购信息" name="1" class="form-collapse-item">
-          <el-row :gutter="0">
-            <el-col :span="24">
-              <el-form-item prop="providerId" label="供应商" :rules="[{
-                required: true,
-                message: '请选择供应商'
-              }]">
-                <div style="padding-right:70px">
-                  <my-select
-                    v-model="uploadData.providerId"
-                    :options="providers"
-                    filterable
-                    placeholder="输入供应商全名或缩写"
-                  ></my-select>
-                </div>
-              </el-form-item>
-            </el-col>
-          </el-row>
-
           <el-row :gutter="0">
             <el-col :span="24">
               <el-form-item prop="file" label="上传文件" :rules="[{
@@ -84,25 +65,18 @@ export default {
     api: {
       required: true,
       type: Function
-    },
-    providers: {
-      required: true,
-      type: Array
     }
   },
   data: () => ({
     visible: false,
     activeCollapseName: '1',
     uploadData: {
-      providerId: null,
       file: null
     }
   }),
   methods: {
     open () {
-      const { providers } = this
       this.uploadData = {
-        providerId: providers.length > 0 ? providers[0].id : null,
         file: null
       }
 
@@ -147,20 +121,6 @@ export default {
     onCloseBtnClick () {
       this.close()
     }
-  },
-  watch: {
-    providers (providers) {
-      if (providers.length === 0 || !this.visible) {
-        return
-      }
-
-      const { uploadData } = this
-      if (uploadData.providerId !== null) {
-        return
-      }
-
-      uploadData.providerId = providers[0].id
-    }
   }
 }
 </script>

+ 117 - 0
src/components/DialogTagApplyTemplateExport.vue

@@ -0,0 +1,117 @@
+<template>
+  <el-dialog
+    :visible.sync="visible"
+    title="导出模版"
+    append-to-body
+    width="500px"
+  >
+    <el-form
+      ref="form"
+      :model="downloadData"
+      label-width="80px"
+    >
+      <el-collapse v-model="activeCollapseName" class="form-collapse">
+        <el-collapse-item title="模版信息" name="1" class="form-collapse-item">
+          <el-row :gutter="0">
+            <el-col :span="24">
+              <el-form-item prop="providerId" label="供应商" :rules="[{
+                required: true,
+                message: '请选择供应商'
+              }]">
+                <div style="padding-right:70px">
+                  <my-select
+                    v-model="downloadData.providerId"
+                    :options="providers"
+                    filterable
+                    placeholder="输入供应商全名或缩写"
+                  ></my-select>
+                </div>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-collapse-item>
+      </el-collapse>
+    </el-form>
+
+    <template #footer>
+      <div class="flex center dialog-footer">
+        <el-button
+          @click="onCloseBtnClick"
+        >取消</el-button>
+
+        <el-button
+          type="primary"
+          @click="onExportTemplateBtnClick"
+        >确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+import BasePage from '@@/utils/BasePage'
+
+export default {
+  name: 'DialogTagApplyTemplateExport',
+  extends: BasePage,
+  props: {
+    api: {
+      required: true,
+      type: Function
+    },
+    providers: {
+      required: true,
+      type: Array
+    }
+  },
+  data: () => ({
+    visible: false,
+    activeCollapseName: '1',
+    downloadData: {
+      providerId: null
+    }
+  }),
+  methods: {
+    open () {
+      const { providers } = this
+      if (providers.length > 0) {
+        const { downloadData } = this
+        if (!downloadData.providerId) {
+          downloadData.providerId = providers[0].id
+        }
+      }
+
+      this.visible = true
+    },
+    onExportTemplateBtnClick () {
+      this.$$request(this.api, this.downloadData).then((flag) => {
+        if (flag) {
+          this.close()
+        }
+      }).catch(console.error).finally(() => {})
+    },
+    close () {
+      this.visible = false
+    },
+    onCloseBtnClick () {
+      this.close()
+    }
+  },
+  watch: {
+    providers (providers) {
+      if (providers.length === 0 || !this.visible) {
+        return
+      }
+
+      const { downloadData } = this
+      if (downloadData.providerId !== null) {
+        return
+      }
+
+      downloadData.providerId = providers[0].id
+    }
+  }
+}
+</script>
+
+<style lang="scss"></style>

+ 16 - 15
src/components/QrCodeView.vue

@@ -1,7 +1,6 @@
 <template>
   <div class="wrapper relative">
-    <canvas ref="qrCodeCanvas" />
-    <slot v-if="!text" name="empty" />
+    <img :src="dataUrl" width="100%" height="100%" />
   </div>
 </template>
 
@@ -11,24 +10,27 @@ import QrCode from 'qrcode'
 export default {
   name: 'QrCodeView',
   props: {
-    text: {
+    code: {
       type: String,
       required: true
     }
   },
+  data: () => ({
+    dataUrl: ''
+  }),
   methods: {
-    setSize () {
-      const el = this.$el
-      const qrCodeCanvas = this.$refs.qrCodeCanvas
-      qrCodeCanvas.width = el.offsetWidth
-      qrCodeCanvas.height = el.offsetHeight
-    },
     genQRCode () {
       const { qrCodeCanvas } = this.$refs
-      const { text } = this
-      if (text) {
-        QrCode.toCanvas(qrCodeCanvas, text, error => {
-          if (error) console.error(error)
+      const { code } = this
+      if (code) {
+        QrCode.toDataURL(code, {
+          margin: 0
+        }, (error, url) => {
+          if (error) {
+            console.error(error)
+          } else {
+            this.dataUrl = url
+          }
         })
       } else {
         const ctx = qrCodeCanvas.getContext('2d')
@@ -36,7 +38,6 @@ export default {
       }
     },
     render () {
-      this.setSize()
       this.genQRCode()
     },
     clearResizeTimer () {
@@ -50,7 +51,7 @@ export default {
     }
   },
   watch: {
-    text () {
+    code () {
       this.genQRCode()
     }
   },

+ 6 - 0
src/entries/TagApply.js

@@ -34,8 +34,14 @@ export default class TagApply extends BaseCurdEntry {
     return ''
   }
 
+  materielName
+
+  goodsName
+
   tagTypeName
 
+  minimumPackingUnit
+
   number
 
   amount

+ 32 - 22
src/mock/index.js

@@ -105,25 +105,37 @@ const API_BASE_PATH = Constant.REQUEST_PREFIX
 //   data: true
 // })
 
-// Mock.mock(/\/tag\/code\/page/, 'post', {
+Mock.mock(/\/tag\/code\/page/, 'post', {
+  type: 'success',
+  data: {
+    'count|10-40': 1,
+    'list|1-20': [{
+      'id|1-20': 1,
+      dataTime: '2024/01/01 00:00:00',
+      orderNo: '123456',
+      expectedReceiptDate: '2024/01/02 00:00:00',
+      serialNumberRange: ['0000001', '000800'],
+      'providerName|1': ['人民电器集团有限公司', '上海东生供用电器材厂', '上海辉电电力设备工程有限公司'],
+      'tagTypeName|1': ['高性能不干胶标签', '柔性抗金属标签', '扎带标签'],
+      number: 123,
+      'amount|1-100.1-2': 30,
+      'tagApplyStateName|1': ['待支付', '待发货', '已发货'],
+      operatorName: '操作员',
+      applicantName: '申领人',
+      'unitPrice|1-100.1-2': 10,
+      createTime: '2024/01/01 00:00:00'
+    }]
+  }
+})
+
+// Mock.mock(/\/tag\/serial\/page/, 'post', {
 //   type: 'success',
 //   data: {
-//     'count|10-40': 1,
+//     'count|20-40': 1,
 //     'list|1-20': [{
 //       'id|1-20': 1,
-//       dataTime: '2024/01/01 00:00:00',
-//       orderNo: '123456',
-//       expectedReceiptDate: '2024/01/02 00:00:00',
-//       serialNumberRange: ['0000001', '000800'],
-//       'providerName|1': ['人民电器集团有限公司', '上海东生供用电器材厂', '上海辉电电力设备工程有限公司'],
-//       'tagTypeName|1': ['高性能不干胶标签', '柔性抗金属标签', '扎带标签'],
-//       number: 123,
-//       'amount|1-100.1-2': 30,
-//       'tagApplyStateName|1': ['待支付', '待发货', '已发货'],
-//       operatorName: '操作员',
-//       applicantName: '申领人',
-//       'unitPrice|1-100.1-2': 10,
-//       createTime: '2024/01/01 00:00:00'
+//       'serialNumber|1-1000000': 1,
+//       'chipCode|1-10000000': 1
 //     }]
 //   }
 // })
@@ -142,15 +154,13 @@ Mock.mock(`${API_BASE_PATH}/data/statistics/tag/type/count`, 'get', {
   }]
 })
 
-Mock.mock(/\/tag\/serial\/page/, 'post', {
+Mock.mock(/\/tag\/code\/qr/, 'get', {
   type: 'success',
   data: {
-    'count|20-40': 1,
-    'list|1-20': [{
-      'id|1-20': 1,
-      'serialNumber|1-1000000': 1,
-      'chipCode|1-10000000': 1
-    }]
+    'materielName|1': ['低压熔断器', '高压熔断器'],
+    'number|1-800': 1,
+    'providerName|1': ['人民电器集团有限公司', '上海东生供用电器材厂', '上海辉电电力设备工程有限公司'],
+    'code|1': ['https://www.baidu.com', 'https://www.jianshu.com', 'https://github.com']
   }
 })
 

+ 8 - 5
src/utils/apiCreator.js

@@ -30,14 +30,17 @@ export default (baseApiPath) => {
     const { config, data } = response
     if (/^(blob)$/i.test(config.responseType)) {
       if (data instanceof Blob) {
+        let filename = '下载'
         const match = /filename=([^;]+)/.exec(response.headers['content-disposition'])
         if (match) {
-          let filename = decodeURIComponent(match).split(',')
-          link.href = URL.createObjectURL(data)
-          link.download = filename ? filename[filename.length - 1] : ''
-          link.click()
-          URL.revokeObjectURL(link.href)
+          const split = decodeURIComponent(match).split(',')
+          filename = split[split.length - 1]
         }
+
+        link.href = URL.createObjectURL(data)
+        link.download = filename
+        link.click()
+        URL.revokeObjectURL(link.href)
         return resolve(response)
       }
 

+ 29 - 2
src/views/TagApply.vue

@@ -150,6 +150,20 @@
             header-align="center"
             align="left"
           ></el-table-column>
+          <el-table-column
+            label="物料名称"
+            prop="materielName"
+            min-width="120"
+            header-align="center"
+            align="left"
+          ></el-table-column>
+          <el-table-column
+            label="商品名称"
+            prop="goodsName"
+            min-width="120"
+            header-align="center"
+            align="left"
+          ></el-table-column>
           <el-table-column
             label="标签类型"
             prop="tagTypeName"
@@ -157,6 +171,13 @@
             header-align="center"
             align="left"
           ></el-table-column>
+          <el-table-column
+            label="最小包装单元"
+            prop="minimumPackingUnit"
+            min-width="100"
+            header-align="center"
+            align="right"
+          ></el-table-column>
           <el-table-column
             label="需求数量"
             prop="number"
@@ -198,10 +219,16 @@
 
   <dialog-import
     ref="dialogImport"
-    :providers="optionGroup.Provider ? optionGroup.Provider.list : []"
     :api="$$api.importData"
     @imported="onSaved"
   ></dialog-import>
+
+  <dialog-tag-apply-template-export
+    ref="dialogExportTemplate"
+    :api="$$api.downloadTemplate"
+    :providers="optionGroup.Provider ? optionGroup.Provider.list : []"
+  ></dialog-tag-apply-template-export>
+
   <dialog-tag-apply-order-export ref="orderExportDialog"></dialog-tag-apply-order-export>
 </div>
 </template>
@@ -227,7 +254,7 @@ export default {
       this.$refs.orderExportDialog.open(this.selectedData)
     },
     onExportTemplateBtnClick () {
-      this.$$api.downloadTemplate().then(() => {}).catch(console.error).finally(() => {})
+      this.$refs.dialogExportTemplate.open()
     },
     onConfirmPayBtnClick () {
       const { selectedList } = this

+ 23 - 4
src/views/TagCode.vue

@@ -144,7 +144,7 @@
             >
               <template v-slot="{ row }">
                 <div class="flex center layout-gap">
-                  <edit-button :data="row" :on-click="onOpenDetailEditorBtnClick" icon="el-icon-view">详情</edit-button>
+                  <edit-button :data="row" :on-click="onOpenDetailEditorBtnClick" icon="el-icon-tickets">详情</edit-button>
                   <edit-button :data="row" :on-click="onPreviewBtnClick" icon="el-icon-view">预览</edit-button>
                   <edit-button :data="row" :on-click="onPrintBtnClick" icon="el-icon-view">
                     <template #icon>
@@ -183,7 +183,21 @@
     >
       <div class="flex column center layout-gap">
         <div class="flex center">打印预览</div>
-        <div></div>
+        <div class="flex tag">
+          <div class="flex column flex-1 fit-size tag-info">
+            <div class="tag-logo"><img src="@/assets/imgs/logo.png" width="100%" height="100%" /></div>
+            <div class="flex column valign-center tag-materiel-info">
+              <template v-if="qrData">
+                <div>{{ qrData.materielName }},</div>
+                <div>{{ qrData.number }}</div>
+              </template>
+            </div>
+            <div class="flex center tag-provider-name">{{ qrData ? qrData.providerName : '' }}</div>
+          </div>
+          <div class="tag-qr">
+            <qr-code-view v-if="qrData" :code="qrData.code"></qr-code-view>
+          </div>
+        </div>
       </div>
     </el-card>
 
@@ -198,6 +212,9 @@ import TagCode from '@@/entries/TagCode'
 export default {
   name: 'TagCode',
   extends: BaseCurdList(TagCode),
+  data: () => ({
+    qrData: null
+  }),
   methods: {
     onOpenDetailEditorBtnClick (data) {
       this.onOpenEditorBtnClick(data, true)
@@ -208,8 +225,10 @@ export default {
     onBatchPrintBtnClick () {
 
     },
-    onPreviewBtnClick () {
-
+    onPreviewBtnClick (data) {
+      this.$$request(this.$$api.getQrData, data[this.$$Target.$$idProp]).then(data => {
+        this.qrData = Object.freeze(data)
+      }).catch(console.error).finally(() => {})
     },
     onPrintBtnClick () {