Z 1 год назад
Родитель
Сommit
05024bf406

+ 74 - 0
src/BIZComponents/processSubmitDialog/api.js

@@ -0,0 +1,74 @@
+import request from "@/utils/request";
+
+export async function getProcessDefinitionBpmnXML(id) {
+  const res = await request({
+    url: '/bpm/process-definition/get-bpmn-xml?id=' + id,
+    method: 'get'
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getTaskAssignRuleList(query) {
+  const res = await request({
+    url: '/bpm/task-assign-rule/list',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function processInstanceCreateAPI(data) {
+  const res = await request({
+    url: '/bpm/process-instance/create',
+    method: 'post',
+    data
+  },data)
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getModelPage(query) {
+  const res = await request({
+    url: '/bpm/model/page',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getProcessDefinitionInfo(params) {
+  const res = await request({
+    url: `/bpm/process-definition/get`,
+    method: 'get',
+    params
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function listAllUserBind() {
+  const res = await request.get('/main/user/listAllUserBind');
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+// 获取用户组精简信息列表
+export async function listSimpleUserGroups() {
+  const res = await request({
+    url: '/bpm/user-group/list-all-simple',
+    method: 'get'
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 409 - 0
src/BIZComponents/processSubmitDialog/processSubmitDialog.vue

@@ -0,0 +1,409 @@
+<template>
+  <ele-modal
+    custom-class="ele-dialog-form long-dialog-form"
+    :centered="true"
+    :visible="processSubmitDialogFlag"
+    title="提交审核"
+    append-to-body
+    :close-on-click-modal="false"
+    width="60%"
+    :before-close="cancel"
+  >
+    <el-steps :active="active" align-center style="transform: scale(0.8)">
+      <el-step title="流程发起"></el-step>
+      <el-step title="流程通知"></el-step>
+    </el-steps>
+
+    <el-form
+      ref="form"
+      :rules="rules"
+      class="el-form-box"
+      :model="form"
+      label-width="80px"
+    >
+      <el-row v-show="active == 0">
+        <el-col :span="12">
+          <el-form-item label="流程分类">
+            <ele-tree-select
+              @change="changeLCFL"
+              clearable
+              ref="processTypeRef"
+              filterable
+              :data="LCFLList"
+              v-model="form.LCFL"
+              childrenKey="children"
+              valueKey="id"
+              labelKey="name"
+              placeholder="请选择"
+              default-expand-all
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="发起流程">
+            <el-select
+              filterable
+              v-model="form.FQLC"
+              @change="changeFQLC"
+              style="width: 100%"
+            >
+              <el-option
+                v-for="item in processList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row v-show="active == 0">
+        <el-col :span="12">
+          <el-form-item label="流程名称">
+            <el-input v-model="form.name" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="流程标识">
+            <el-input v-model="form.key" disabled></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <headerTitle
+        v-show="active == 0"
+        title="流程执行人/流程图"
+        style="margin-top: 30px"
+      ></headerTitle>
+      <el-table
+        v-show="active == 0"
+        :data="datasource"
+        border
+        style="margin-bottom: 10px"
+      >
+        <el-table-column
+          label="任务节点"
+          align="center"
+          prop="taskDefinitionName"
+          width="140"
+          fixed
+        />
+        <el-table-column
+          label="执行人"
+          align="center"
+          prop="options"
+          min-width="140px"
+        >
+          <template v-slot="scope">
+            <div v-if="scope.row.type !== 60 && scope.row.options.length > 0">
+              <el-tag
+                size="medium"
+                :key="option"
+                v-for="option in scope.row.options"
+              >
+                {{ getAssignRuleOptionName(scope.row, option) }}
+              </el-tag>
+            </div>
+
+            <el-tag size="medium" v-if="scope.row.type === 60">
+              {{ getAssignRuleOptionName(scope.row) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+      </el-table>
+      <my-process-viewer
+        v-show="active == 0"
+        style="min-width: 100%; height: 300px"
+        key="designer"
+        v-model="bpmnXML"
+      />
+      <el-table v-show="active == 1" :data="form.noticeScope" border>
+        <el-table-column
+          label="类型"
+          align="center"
+          prop="taskDefinitionName"
+          width="140"
+          fixed
+        />
+        <el-table-column
+          label="结果"
+          align="center"
+          prop="options"
+          min-width="140px"
+        >
+        </el-table-column>
+      </el-table>
+    </el-form>
+
+    <div slot="footer">
+      <el-button
+        type="primary"
+        size="small"
+        v-if="active == 0"
+        @click="() => (active = 1)"
+        >下一步
+      </el-button>
+      <el-button
+        type="primary"
+        size="small"
+        v-if="active == 1"
+        @click="() => (active = 0)"
+        >上一步
+      </el-button>
+      <el-button type="primary" size="small" @click="submit">完成</el-button>
+      <el-button size="small" @click="cancel">关闭</el-button>
+    </div>
+  </ele-modal>
+</template>
+<script>
+  import {
+    getModelPage,
+    getProcessDefinitionBpmnXML,
+    getProcessDefinitionInfo,
+    getTaskAssignRuleList,
+    processInstanceCreateAPI,
+    listAllUserBind,
+    listSimpleUserGroups
+  } from './api';
+  import { treeClassifyCodeEnum } from '@/enum/dict';
+  import { listRoles } from '@/api/system/role';
+  import { listOrganizations } from '@/api/system/organization';
+  import dictMixins from '@/mixins/dictMixins';
+  import { getByCode } from '@/api/system/dictionary-data';
+  import { getProduceTreeByCode } from '@/api/saleManage/quotation';
+
+  export default {
+    name: 'processSubmitDialog',
+    mixins: [dictMixins],
+    props: {
+      processSubmitDialogFlag: {
+        type: Boolean,
+        default: false
+      }
+    },
+    data() {
+      return {
+        form: {
+          LCFL: '',
+          FQLC: '',
+          processDefinitionId: '',
+          name: '',
+          businessId: '',
+          noticeScope: [],
+          businessKey: ''
+        },
+        active: 0,
+        bpmnXML: null,
+        LCFLList: [],
+        processList: [],
+        datasource: [],
+        roleOptions: [],
+        deptOptions: [],
+        deptTreeOptions: [],
+        postOptions: [],
+        userOptions: [],
+        userGroupOptions: [],
+        dictList: {},
+        rules: {}
+      };
+    },
+    async created() {
+      let typeObj = await getProduceTreeByCode(
+        treeClassifyCodeEnum['PROCESSTYPE']
+      );
+      this.LCFLList = typeObj[0].children;
+
+      // 获得角色列表
+      this.roleOptions = [];
+      listRoles({
+        current: 1,
+        size: 9999
+      }).then((data) => {
+        this.roleOptions.push(...data.list);
+      });
+      // 获得部门列表
+      this.deptOptions = [];
+      this.deptTreeOptions = [];
+      listOrganizations().then((data) => {
+        this.deptOptions.push(...data);
+        this.deptTreeOptions.push(...this.handleTree(data, 'id'));
+      });
+      // 获得岗位列表 暂无岗位概念
+      this.postOptions = [];
+      /*listSimplePosts().then(response => {
+    this.postOptions.push(...response.data);
+  });*/
+      // 获得用户列表
+      this.userOptions = [];
+      listAllUserBind().then((data) => {
+        this.userOptions.push(...data);
+      });
+      // 获得用户组列表
+      this.userGroupOptions = [];
+      listSimpleUserGroups().then((response) => {
+        this.userGroupOptions.push(...response);
+      });
+
+      //this.dictEnum['工作流任务分配自定义脚本']
+      await this.getDictList(this.dictEnum['工作流任务分配自定义脚本']);
+      await this.getDictList(this.dictEnum['工种类型']);
+    },
+    methods: {
+      init(row = {}) {
+        this.form.businessId = row.id;
+        this.form.businessKey = row.businessKey;
+        this.getDefaultInfo(row.businessKey);
+      },
+      async getDefaultInfo(businessKey) {
+        let info = await getProcessDefinitionInfo({ code: businessKey });
+        this.form.LCFL = info?.category;
+        this.form.FQLC = info?.modelId;
+        this.form.name = info?.name;
+        this.form.key = info?.key;
+        this.form.processDefinitionId = info?.id;
+        if (this.form.LCFL) await this.getProcessList(this.form.LCFL);
+        if (this.form.FQLC) {
+          await this.getProcessDefinitionBpmnXMLInfo(info.id);
+          await this.getTaskAssignRuleListInfo({
+            modelId: info.modelId,
+            processDefinitionId: info.id
+          });
+        }
+      },
+      async changeLCFL(val) {
+        this.bpmnXML = null;
+        this.form.processDefinitionId = null;
+        this.form.FQLC = null;
+        await this.getProcessList(val);
+      },
+      async getProcessList(val) {
+        let params = {
+          pageNo: 1,
+          pageSize: 999,
+          processTypeId: val
+        };
+        const { list } = await getModelPage(params);
+        this.processList = list.filter((item) => item.processDefinition);
+      },
+      async changeFQLC(val) {
+        if (!val) return;
+        let find = this.processList.find((item) => item.id === val) || {};
+        this.form.name = find.name;
+        this.form.key = find.key;
+        this.form.processDefinitionId = find.processDefinition.id;
+        await this.getProcessDefinitionBpmnXMLInfo(find.processDefinition.id);
+        await this.getTaskAssignRuleListInfo({
+          modelId: find.id,
+          processDefinitionId: find.processDefinition.id
+        });
+      },
+      async getProcessDefinitionBpmnXMLInfo(val) {
+        // 加载流程图
+        this.bpmnXML = await getProcessDefinitionBpmnXML(val);
+      },
+      async getTaskAssignRuleListInfo(find) {
+        this.datasource = await getTaskAssignRuleList({
+          modelId: find.modelId,
+          processDefinitionId: find.processDefinitionId
+        });
+      },
+      getAssignRuleOptionName(row, option) {
+        if (row.type == 10) {
+          for (const roleOption of this.roleOptions) {
+            if (roleOption.id === option) {
+              return roleOption.name;
+            }
+          }
+        } else if (row.type === 20 || row.type === 21) {
+          for (const deptOption of this.deptOptions) {
+            if (deptOption.id === option) {
+              return deptOption.name;
+            }
+          }
+        } else if (row.type === 22) {
+          option = option + ''; // 转换成 string
+          return this.getDictV(this.dictEnum['工种类型'], option);
+        } else if (row.type === 30 || row.type === 31 || row.type === 32) {
+          for (const userOption of this.userOptions) {
+            if (userOption.id === option) {
+              return userOption.nickname || userOption.name;
+            }
+          }
+        } else if (row.type === 40) {
+          for (const userGroupOption of this.userGroupOptions) {
+            if (userGroupOption.id === option) {
+              return userGroupOption.name;
+            }
+          }
+        } else if (row.type === 50) {
+          option = option + ''; // 转换成 string
+          return this.getDictV(
+            this.dictEnum['工作流任务分配自定义脚本'],
+            option
+          );
+        } else if (row.type === 60) {
+          return row.variableName;
+        }
+        return '未知(' + option + ')';
+      },
+      /**
+       * 构造树型结构数据
+       * @param {*} data 数据源
+       * @param {*} id id字段 默认 'id'
+       * @param {*} parentId 父节点字段 默认 'parentId'
+       * @param {*} children 孩子节点字段 默认 'children'
+       * @param {*} rootId 根Id 默认 0
+       */
+      handleTree(data, id, parentId, children, rootId) {
+        id = id || 'id';
+        parentId = parentId || 'parentId';
+        children = children || 'children';
+        rootId =
+          rootId ||
+          Math.min.apply(
+            Math,
+            data.map((item) => {
+              return item[parentId];
+            })
+          ) ||
+          0;
+        //对源数据深度克隆
+        const cloneData = JSON.parse(JSON.stringify(data));
+        //循环所有项
+        const treeData = cloneData.filter((father) => {
+          let branchArr = cloneData.filter((child) => {
+            //返回每一项的子级数组
+            return father[id] == child[parentId];
+          });
+          branchArr.length > 0 ? (father.children = branchArr) : '';
+          //返回第一层
+          return father[parentId] == rootId;
+        });
+        return treeData !== '' ? treeData : data;
+      },
+      getDictV(code, val) {
+        if (!this.dictList[code]) return '';
+        return this.dictList[code].find((item) => item.value == val)?.label;
+      },
+      async getDictList(code) {
+        let { data: res } = await getByCode(code);
+        this.dictList[code] = res.map((item) => {
+          let values = Object.keys(item);
+          return {
+            value: values[0],
+            label: item[values[0]]
+          };
+        });
+      },
+      async submit() {
+        await processInstanceCreateAPI(this.form);
+        this.$message('提交审核成功');
+        this.$emit('reload');
+        this.cancel();
+      },
+      cancel() {
+        this.$emit('update:processSubmitDialogFlag', false);
+      }
+    }
+  };
+</script>
+<style scoped lang="scss"></style>

+ 32 - 0
src/api/bpm/index.js

@@ -48,3 +48,35 @@ export async function getProcessDefinitionBpmnXML(id) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+export async function getTaskAssignRuleList(query) {
+  const res = await request({
+    url: '/bpm/task-assign-rule/list',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function processInstanceCreateAPI(data) {
+  const res = await request({
+    url: '/bpm/process-instance/create',
+    method: 'post'
+  },data)
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getModelPage(query) {
+  const res = await request({
+    url: '/bpm/model/page',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 1 - 0
src/api/main/index.js

@@ -24,3 +24,4 @@ export async function assetPageAPI (params) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+

+ 18 - 0
src/api/system/user/index.js

@@ -221,3 +221,21 @@ export async function checkExistence (field, value, id) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+export async function listAllUserBind () {
+  const res = await request.get('/main/user/listAllUserBind');
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+// 获取用户组精简信息列表
+export async function listSimpleUserGroups() {
+  const res = await request({
+    url: '/bpm/user-group/list-all-simple',
+    method: 'get'
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

BIN
src/assets/logo.jpg


+ 1 - 1
src/components/bpmnProcessDesigner/package/designer/ProcessViewer.vue

@@ -305,7 +305,7 @@ export default {
           }
           html = `<p>审批人:${task.assigneeUser.nickname}</p>
                   <p>部门:${task.assigneeUser.deptName}</p>
-                  <p>结果:${this.getDictValue('流程实例的结果', task.result)}</p>
+                  <p>结果:${this.getDictValue('流程实例的结果', task.result+'')||''}</p>
                   <p>创建时间:${task.createTime}</p>`;
           if (task.endTime) {
             html += `<p>结束时间:${task.endTime}</p>`

+ 8 - 2
src/enum/dict.js

@@ -46,7 +46,9 @@ export default {
   供应商级别: 'contact_level',
   生产类型: 'productionType',
   包装强度: 'packagingStrength',
-  包装密度: 'packagingDensity'
+  包装密度: 'packagingDensity',
+  工作流任务分配规则的类型: 'bpm_task_assign_rule_type',
+  工作流任务分配自定义脚本: 'bpm_task_assign_script',
 };
 
 export const numberList = [
@@ -155,7 +157,11 @@ export const businessStatus = [
   { code: 5, label: '巡点检' }
 ];
 //主数据分类关联对应常量
-export const treeClassifyCodeEnum = { CHACHE: 'w10-2', producerType: 'W10' };
+export const treeClassifyCodeEnum = {
+  CHACHE: 'w10-2',
+  producerType: 'W10',
+  PROCESSTYPE: 'PROCESS001'
+};
 
 export const sceneStateEnum = [
   { code: 1, label: '生产入库', documentsName: '生产工单' },

+ 1 - 3
src/store/getters.js

@@ -13,11 +13,9 @@ export default {
     ) || {},
   // 根据字典enumName  和 dictCode 获取字典 值(名称
   getDictValue: (state) => (enumName, dictCode) => {
-    console.log(state,dictEnum[enumName],'00000000000')
     const obj = (state.dict[dictEnum[enumName]] || []).find((item) => {
-      return item.dictCode === dictCode;
+      return item.dictCode == dictCode;
     });
-
     return obj && obj.dictValue;
   }
 };

+ 3 - 3
src/views/saleManage/saleOrder/index.vue

@@ -43,7 +43,6 @@
                 >
                   新建
                 </el-button>
-
                 <el-button
                   size="small"
                   type="danger"
@@ -190,6 +189,7 @@
       @done="commitBtn"
     />
   </div>
+
 </template>
 
 <script>
@@ -206,10 +206,9 @@ import customerReturnOrder from './customerReturnOrder/index.vue';
 import returnGoods from './returnGoods/index.vue';
 import {getTableList, deleteInformation, submit} from '@/api/saleManage/saleorder';
 import dictMixins from '@/mixins/dictMixins';
-import {progressStatusEnum, reviewStatus, saleOrderProgressStatusEnum} from '@/enum/dict';
+import {reviewStatus, saleOrderProgressStatusEnum} from '@/enum/dict';
 import entrustedReceive from "@/views/saleManage/saleOrder/entrustedReceive/index.vue";
 import autogenerateDialog from "@/BIZComponents/autogenerateDialog.vue";
-
 export default {
   mixins: [dictMixins],
   components: {
@@ -241,6 +240,7 @@ export default {
       selection: [], //单选中集合
       delVisible: false, //批量删除弹框状态
       loading: false, // 加载状态
+      processSubmitDialogFlag: false, // 加载状态
       columns: [
         {
           width: 45,

+ 1 - 1
src/views/saleManage/saleOrder/invoice/components/inventoryTable.vue

@@ -33,7 +33,7 @@
               :key="link.id"
               type="primary"
               :underline="false"
-              @click="downloadFile(link)"
+              @click="download(link)"
             >
               {{ link.name }}
             </el-link

+ 344 - 0
src/views/saleManage/saleOrder/invoice/components/print-template.vue

@@ -0,0 +1,344 @@
+<template>
+  <ele-modal
+    title="出库单"
+    :visible.sync="QRvisible"
+    v-if="QRvisible"
+    width="90%"
+  >
+    <div
+      id="printSection"
+      style="
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        flex-direction: column;
+      "
+    >
+      <table
+        cellspacing="0"
+        style="
+          width: 100%;
+          table-layout: fixed;
+          word-break: break-all;
+          word-wrap: break-word;
+          font-size: 12px;
+        "
+      >
+        <tbody>
+          <tr align="center">
+            <td colspan="3">
+              <div
+                style="
+                  display: flex;
+                  align-items: center;
+                  justify-content: center;
+                "
+              >
+                <img
+                  style="width: 80px; height: 80px"
+                  src="@/assets/logo.jpg"
+                  alt=""
+                />
+                <div>
+                  <div style="font-size: 14px">湖南宝悦新型建材有限公司</div>
+                  <div style="font-size: 10px"
+                    >HUNAN BAO YUE BULIDING MATERAIL</div
+                  >
+                </div>
+              </div>
+            </td>
+            <td colspan="3" style="font-size: 24px">
+              <div style="border-bottom: 2px solid #000; width: 150px"
+                >销 售 发 货 单</div
+              >
+            </td>
+            <td colspan="3"><svg id="print"></svg></td>
+          </tr>
+          <tr>
+            <td colspan="3" style="padding: 2.5px">
+              发货单号:{{ formData.docNo }}
+            </td>
+            <td colspan="3" style="padding: 2.5px">
+              项目: {{ formData.projectName }}</td
+            >
+            <td colspan="3" style="padding: 2.5px">
+              日期:{{ getNowFormatDate() }}
+            </td>
+          </tr>
+          <tr>
+            <td colspan="3" style="padding: 2.5px 2.5px 10px 2.5px">
+              客户:{{ formData.linkName }}</td
+            >
+            <td colspan="3" style="padding: 2.5px 2.5px 10px 2.5px">
+              联系电话: {{ formData.linkPhone }}</td
+            >
+            <td colspan="3" style="padding: 2.5px 2.5px 10px 2.5px">
+              车牌号: {{ formData.carNo }}</td
+            >
+          </tr>
+        </tbody>
+      </table>
+      <table
+        border
+        cellspacing="0"
+        style="
+          width: 100%;
+          table-layout: fixed;
+          word-break: break-all;
+          word-wrap: break-word;
+          font-size: 12px;
+        "
+      >
+        <tbody>
+          <tr align="center">
+            <td style="padding: 2.5px"> 产品编码 </td>
+            <td style="padding: 2.5px"> 产品名称 </td>
+            <td style="padding: 2.5px"> 规格型号 </td>
+            <td style="padding: 2.5px"> 单位 </td>
+            <td style="padding: 2.5px"> 数量 </td>
+            <td style="padding: 2.5px"> 强度 </td>
+            <td style="padding: 2.5px"> 密度 </td>
+            <td style="padding: 2.5px" v-if="isPrintPrice"> 单价(元) </td>
+            <td style="padding: 2.5px" v-if="isPrintPrice"> 金额(元) </td>
+            <td style="padding: 2.5px"> 备注 </td>
+          </tr>
+          <tr align="center" v-for="(item, index) in codeList">
+            <td style="padding: 2.5px">{{ item.productCode }}</td>
+            <td style="padding: 2.5px">{{ item.productName }}</td>
+            <td style="padding: 2.5px"
+              >{{ item.specification }}{{ item.modelType }}</td
+            >
+            <td style="padding: 2.5px">{{ item.measuringUnit }}</td>
+            <td style="padding: 2.5px">{{ item.totalCount }}</td>
+            <td style="padding: 2.5px">{{ item.extField.packagingStrength }}</td>
+            <td style="padding: 2.5px">{{ item.extField.packagingDensity }}</td>
+            <td style="padding: 2.5px" v-if="isPrintPrice">{{ item.discountSinglePrice }}</td>
+            <td style="padding: 2.5px" v-if="isPrintPrice">{{ item.discountTotalPrice }}</td>
+            <td style="padding: 2.5px">{{ item.remark }}</td>
+          </tr>
+          <tr align="center">
+            <td colspan="4" style="padding: 2.5px"> 合计 </td>
+            <td colspan="1" style="padding: 2.5px">
+              {{
+                codeList.reduce((total, item) => {
+                  return (total += item.totalCount);
+                }, 0)
+              }}
+            </td>
+            <td style="padding: 2.5px"></td>
+            <td colspan="2" style="padding: 2.5px"></td>
+            <td colspan="1" style="padding: 2.5px" v-if="isPrintPrice">
+              {{
+                codeList.reduce((total, item) => {
+                  return (total += item.discountTotalPrice);
+                }, 0)
+              }}
+            </td>
+            <td colspan="1" v-if="isPrintPrice" style="padding: 2.5px"></td>
+          </tr>
+          <tr align="center">
+            <td colspan="3" style="padding: 2.5px">
+              注意事项:遗失托盘赔偿260/个
+            </td>
+            <td colspan="2" style="padding: 2.5px"> 装车托盘数:</td>
+            <td style="padding: 2.5px"></td>
+            <td style="padding: 2.5px"> 带回托盘数: </td>
+            <td :colspan="isPrintPrice?3:1" style="padding: 2.5px"></td>
+          </tr>
+        </tbody>
+      </table>
+      <table
+        cellspacing="0"
+        style="
+          width: 100%;
+          table-layout: fixed;
+          word-break: break-all;
+          word-wrap: break-word;
+          font-size: 12px;
+        "
+      >
+        <tbody>
+          <tr>
+            <td colspan="3" style="padding: 2.5px">
+              承运人:{{ this.formData.driverNames }}</td
+            >
+            <td colspan="3" style="padding: 2.5px"> 装卸工:</td>
+            <td colspan="3" style="padding: 2.5px"> 签收人:</td>
+            <td colspan="3" style="padding: 2.5px">
+              制单人:{{ this.formData.markerName }}</td
+            >
+          </tr>
+          <tr>
+            <td colspan="8" style="padding: 2.5px"
+              >物流服务评价:<input
+                type="checkbox"
+                id="vehicle1"
+                name="vehicle1"
+                value="Bike"
+                style="padding: 1px 2.5px"
+              />
+              <span>满意</span>
+
+              <input
+                type="checkbox"
+                id="vehicle2"
+                name="vehicle2"
+                value="Car"
+                style="padding: 1px 2.5px"
+              /><span>不满意</span>
+            </td>
+            <td colspan="4" style="padding: 2.5px"
+              >打印时间:{{ getNowFormatDate() }}</td
+            >
+          </tr>
+          <tr>
+            <td colspan="2" style="padding: 2.5px"> (白)记账联 </td>
+            <td colspan="2" style="padding: 2.5px"> (红)客户留存联 </td>
+            <td colspan="2" style="padding: 2.5px"> (蓝)结算联 </td>
+            <td colspan="2" style="padding: 2.5px"> (绿)运费联 </td>
+            <td colspan="2" style="padding: 2.5px"> (黄)装卸联 </td>
+            <td colspan="2" style="padding: 2.5px"> (白)出门联 </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <div slot="footer">
+      <el-button @click="print">打印预览</el-button>
+      <el-button @click="close">关闭</el-button>
+    </div>
+  </ele-modal>
+</template>
+
+<script>
+  import { mapGetters } from 'vuex';
+  import JsBarcode from 'jsbarcode';
+  import {getSendSaleOrderrecordDetail} from "@/api/saleManage/saleordersendrecord";
+  export default {
+    name: 'print',
+    computed: {
+      ...mapGetters(['user'])
+    },
+    props:{
+    },
+    data() {
+      return {
+        checked: '',
+        QRvisible: false,
+        isPrintPrice: false,
+        codeList: [],
+        formData: {}
+      };
+    },
+
+    methods: {
+      open(id,isPrintPrice) {
+        this.isPrintPrice = isPrintPrice
+        getSendSaleOrderrecordDetail(id).then((data) => {
+          if (JSON.stringify(data) != '{}') {
+            this.formData = data;
+            this.formData.projectName = data.saleOrder?.projectName;
+            this.formData.driverNames = data.logisticTrakListNoteVOList
+              .map((item) => item.driverName)
+              .join(','); // 承运人
+            this.codeList = data.productList
+            this.formData.markerName = this.user.info.name;
+          }
+          this.QRvisible = true;
+          this.$nextTick(() => {
+            JsBarcode('#print', '1234567822222', {
+              format: 'CODE39', //选择要使用的条形码类型
+              width: 0.8, //设置条之间的宽度
+              height: 50, //高度
+              displayValue: false, //是否在条形码下方显示文字
+              text: '', //覆盖显示的文本
+              fontOptions: 'bold italic', //使文字加粗体或变斜体
+              font: 'fantasy', //设置文本的字体
+              textAlign: 'right', //设置文本的水平对齐方式
+              textPosition: 'top', //设置文本的垂直位置
+              textMargin: 1, //设置条形码和文本之间的间距
+              fontSize: 15, //设置文本的大小
+              background: '#fff', //设置条形码的背景
+              lineColor: '#000', //设置条和文本的颜色。
+              margin: 2 //设置条形码周围的空白边距
+            });
+          });
+        });
+      },
+      close() {
+        this.QRvisible = false;
+      },
+      //获取当前日期函数
+      getNowFormatDate() {
+        let date = new Date(),
+          obj = {
+            year: date.getFullYear(), //获取完整的年份(4位)
+            month: date.getMonth() + 1, //获取当前月份(0-11,0代表1月)
+            strDate: date.getDate(), // 获取当前日(1-31)
+            week: '星期' + '日一二三四五六'.charAt(date.getDay()), //获取当前星期几(0 ~ 6,0代表星期天)
+            hour: date.getHours(), //获取当前小时(0 ~ 23)
+            minute: date.getMinutes(), //获取当前分钟(0 ~ 59)
+            second: date.getSeconds() //获取当前秒数(0 ~ 59)
+          };
+        Object.keys(obj).forEach((key) => {
+          if (obj[key] < 10) obj[key] = `0${obj[key]}`;
+        });
+        // return `${obj.year}年${obj.month}月${obj.strDate}日 ${obj.hour}:${obj.minute}:${obj.second}`;
+        return `${obj.year}-${obj.month}-${obj.strDate}`;
+      },
+      print() {
+        const printSection = document.getElementById('printSection');
+        // 创建打印任务
+        const printWindow = window.open('', '_blank');
+        printWindow.document.open();
+        printWindow.document.write('<html><head><title>打印预览</title>');
+        printWindow.document.write(
+          '<link rel="stylesheet" href="your-stylesheet-url.css" type="text/css" />'
+        );
+        printWindow.document.write('</head><body>');
+        printWindow.document.write(printSection.innerHTML);
+        printWindow.document.write('</body></html>');
+        printWindow.document.close();
+        printWindow.onload = function () {
+          printWindow.print();
+        };
+      }
+    }
+  };
+</script>
+
+<style lang="scss">
+  #printSection {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+    .table_box {
+      width: 70%;
+      table-layout: fixed;
+      word-break: break-all;
+      word-wrap: break-word;
+    }
+    th,
+    td {
+      padding: 20px;
+      position: relative;
+    }
+    .barCode {
+      position: absolute;
+      right: 10px;
+      top: 50%;
+      transform: translateY(-50%);
+    }
+    .person {
+      width: 70%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin-bottom: 10px;
+      > div {
+        flex: 1;
+      }
+    }
+  }
+</style>

+ 250 - 223
src/views/saleManage/saleOrder/invoice/index.vue

@@ -2,7 +2,7 @@
   <div class="ele-body">
     <el-card shadow="never" v-loading="loading">
       <div class="ele-border-lighter form-content" v-loading="loading">
-        <search-table @search="reload"> </search-table>
+        <search-table @search="reload"></search-table>
 
         <!-- 数据表格 -->
         <ele-pro-table
@@ -31,13 +31,22 @@
             <el-button
               size="small"
               type="danger"
-              el-icon-delete
-              class="ele-btn-icon"
+
+              class="el-icon-delete"
               @click="allDelBtn"
               :disabled="selection?.length === 0"
             >
               批量删除
             </el-button>
+            <el-button
+              size="small"
+              type="primary"
+              class="el-icon-printer"
+              @click="handlePrint"
+              :disabled="selection?.length === 0"
+            >
+              打印发货单
+            </el-button>
           </template>
 
           <!-- 查看详情列 -->
@@ -48,7 +57,8 @@
               :underline="false"
               @click="openorderDetail(row, 'docNo')"
             >
-              {{ row.docNo }}</el-link
+              {{ row.docNo }}
+            </el-link
             >
           </template>
           <template v-slot:orderNo="{ row }">
@@ -57,7 +67,8 @@
               :underline="false"
               @click="openorderDetail(row, 'orderNo')"
             >
-              {{ row.orderNo }}</el-link
+              {{ row.orderNo }}
+            </el-link
             >
           </template>
           <!-- 操作列 -->
@@ -124,245 +135,261 @@
       content="是否确定删除?"
       @done="commitBtn"
     />
+    <print-template ref="printTemplateRef"></print-template>
   </div>
 </template>
 
 <script>
-  import searchTable from './components/searchTable.vue';
-  import addInvoiceDialog from './components/addInvoiceDialog.vue';
-  import detailDialog from './components/detailDialog.vue';
-  import popModal from '@/components/pop-modal';
-  import { reviewStatusEnum } from '@/enum/dict';
-  import orderDetailDialog from '@/views/saleManage/saleOrder/components/detailDialog.vue';
-  import addReturnGoodsDialog from '@/views/saleManage/saleOrder/returnGoods/components/addReturnGoodsDialog';
+import searchTable from './components/searchTable.vue';
+import addInvoiceDialog from './components/addInvoiceDialog.vue';
+import detailDialog from './components/detailDialog.vue';
+import popModal from '@/components/pop-modal';
+import {reviewStatusEnum} from '@/enum/dict';
+import orderDetailDialog from '@/views/saleManage/saleOrder/components/detailDialog.vue';
+import addReturnGoodsDialog from '@/views/saleManage/saleOrder/returnGoods/components/addReturnGoodsDialog';
+import printTemplate from "@/views/saleManage/saleOrder/invoice/components/print-template.vue";
+import {deleteSendInformation, getSendTableList, submit} from '@/api/saleManage/saleordersendrecord';
+import dictMixins from '@/mixins/dictMixins';
 
-  import {
-    getSendTableList,
-    submit,
-    deleteSendInformation
-  } from '@/api/saleManage/saleordersendrecord';
-  import dictMixins from '@/mixins/dictMixins';
+export default {
+  mixins: [dictMixins],
+  components: {
+    printTemplate,
+    searchTable,
+    popModal,
+    addReturnGoodsDialog,
+    orderDetailDialog,
+    addInvoiceDialog,
+    detailDialog
+  },
+  data() {
+    return {
+      activeComp: 'saleorder',
+      tabOptions: [
+        {key: 'saleorder', name: '销售订单'},
+        {key: 'invoice', name: '发货单'},
+        {key: 'returnorder', name: '退货单'}
+      ],
+      selection: [], //单选中集合
+      delVisible: false, //批量删除弹框状态
+      loading: false, // 加载状态
+      columns: [
+        {
+          width: 45,
+          type: 'selection',
+          columnKey: 'selection',
+          align: 'center'
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true,
+          fixed: 'left'
+        },
+        {
+          prop: 'docNo',
+          label: '发货单编码',
+          align: 'center',
+          sortable: true,
+          slot: 'docNo',
+          showOverflowTooltip: true,
+          minWidth: 200
+        },
 
-  export default {
-    mixins: [dictMixins],
-    components: {
-      searchTable,
-      popModal,
-      addReturnGoodsDialog,
-      orderDetailDialog,
-      addInvoiceDialog,
-      detailDialog
-    },
-    data() {
-      return {
-        activeComp: 'saleorder',
-        tabOptions: [
-          { key: 'saleorder', name: '销售订单' },
-          { key: 'invoice', name: '发货单' },
-          { key: 'returnorder', name: '退货单' }
-        ],
-        selection: [], //单选中集合
-        delVisible: false, //批量删除弹框状态
-        loading: false, // 加载状态
-        columns: [
-          {
-            width: 45,
-            type: 'selection',
-            columnKey: 'selection',
-            align: 'center'
-          },
-          {
-            columnKey: 'index',
-            label: '序号',
-            type: 'index',
-            width: 55,
-            align: 'center',
-            showOverflowTooltip: true,
-            fixed: 'left'
-          },
-          {
-            prop: 'docNo',
-            label: '发货单编码',
-            align: 'center',
-            sortable: true,
-            slot: 'docNo',
-            showOverflowTooltip: true,
-            minWidth: 200
-          },
-
-          {
-            prop: 'orderNo',
-            label: '销售订单编码',
-            align: 'center',
-            sortable: true,
-            slot: 'orderNo',
-            showOverflowTooltip: true,
-            minWidth: 200
-          },
-          {
-            prop: 'productNames',
-            label: '产品名称',
-            align: 'center',
-            showOverflowTooltip: true,
-            minWidth: 140
-          },
-          {
-            prop: 'contactName',
-            label: '客户名称',
-            align: 'center',
-            showOverflowTooltip: true,
-            minWidth: 180
-          },
-          {
-            prop: 'replied',
-            label: '是否回执',
-            align: 'center',
-            showOverflowTooltip: true,
-            minWidth:120,
-            formatter: (_row, _column, cellValue) => {
-              return _row.replied == 1 ? '是' : '否';
-            }
-          },
-          {
-            prop: 'reviewStatus',
-            label: '状态',
-            align: 'center',
-            showOverflowTooltip: true,
-            minWidth: 100,
-            formatter: (_row, _column, cellValue) => {
-              return reviewStatusEnum[_row.reviewStatus].label;
-            }
-          },
-          {
-            prop: 'createTime',
-            label: '创建时间',
-            align: 'center',
-            showOverflowTooltip: true,
-            minWidth: 170
-          },
-          {
-            columnKey: 'action',
-            label: '操作',
-            width: 230,
-            align: 'center',
-            resizable: false,
-            slot: 'action',
-            showOverflowTooltip: true,
-            fixed: 'right'
+        {
+          prop: 'orderNo',
+          label: '销售订单编码',
+          align: 'center',
+          sortable: true,
+          slot: 'orderNo',
+          showOverflowTooltip: true,
+          minWidth: 200
+        },
+        {
+          prop: 'productNames',
+          label: '产品名称',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 140
+        },
+        {
+          prop: 'contactName',
+          label: '客户名称',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 180
+        },
+        {
+          prop: 'replied',
+          label: '是否回执',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 120,
+          formatter: (_row, _column, cellValue) => {
+            return _row.replied == 1 ? '是' : '否';
           }
-        ]
-      };
-    },
-    computed: {},
+        },
+        {
+          prop: 'reviewStatus',
+          label: '状态',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 100,
+          formatter: (_row, _column, cellValue) => {
+            return reviewStatusEnum[_row.reviewStatus].label;
+          }
+        },
+        {
+          prop: 'createTime',
+          label: '创建时间',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 170
+        },
+        {
+          columnKey: 'action',
+          label: '操作',
+          width: 230,
+          align: 'center',
+          resizable: false,
+          slot: 'action',
+          showOverflowTooltip: true,
+          fixed: 'right'
+        }
+      ]
+    };
+  },
+  computed: {},
 
-    methods: {
-      /* 表格数据源 */
-      datasource({ page, limit, where, order }) {
-        return getSendTableList({
-          pageNum: page,
-          size: limit,
-          ...where
-        });
-      },
+  methods: {
+    /* 表格数据源 */
+    datasource({page, limit, where, order}) {
+      return getSendTableList({
+        pageNum: page,
+        size: limit,
+        ...where
+      });
+    },
 
-      //创建退货单
-      creatReturnGoods(type, row) {
-        this.$refs.addReturnGoodsRef.open(type, {}, row.id);
-      },
+    //创建退货单
+    creatReturnGoods(type, row) {
+      this.$refs.addReturnGoodsRef.open(type, {}, row.id);
+    },
 
-      /* 刷新表格 */
-      reload(where) {
-        this.$refs.table.reload({ page: 1, where });
-      },
+    /* 刷新表格 */
+    reload(where) {
+      this.$refs.table.reload({page: 1, where});
+    },
 
-      //新增编辑
-      openEdit(type, row) {
-        this.$refs.invoiceDialogRef.open(type, row);
-        this.$refs.invoiceDialogRef.$refs.form &&
-          this.$refs.invoiceDialogRef.$refs.form.clearValidate();
-      },
+    //新增编辑
+    openEdit(type, row) {
+      this.$refs.invoiceDialogRef.open(type, row);
+      this.$refs.invoiceDialogRef.$refs.form &&
+      this.$refs.invoiceDialogRef.$refs.form.clearValidate();
+    },
 
-      //批量删除
-      allDelBtn() {
-        if (this.selection.length === 0) return;
-        let flag =  this.selection.some(item=>[1,2].includes(item.reviewStatus))
-        if(flag) return this.$message.warning('抱歉已审核、审核中的数据不能删除,请检查')
-        this.delVisible = true;
-      },
+    //批量删除
+    allDelBtn() {
+      if (this.selection.length === 0) return;
+      let flag = this.selection.some(item => [1, 2].includes(item.reviewStatus))
+      if (flag) return this.$message.warning('抱歉已审核、审核中的数据不能删除,请检查')
+      this.delVisible = true;
+    },
+    handlePrint() {
+      if (this.selection.length > 1) return this.$message.warning('请选择一条');
+      this.$confirm('是否需要打印价格', '提示', {
+        type: 'warning',
+        cancelButtonText: '否',
+        confirmButtonText: '是',
+      }).then(res => {
+        this.$refs.printTemplateRef.open(this.selection[0].id, true)
+      }).catch(err => {
+        this.$refs.printTemplateRef.open(this.selection[0].id, false)
+      })
 
-      //删除接口
-      remove(delData) {
-        deleteSendInformation(delData).then((res) => {
-          this.$message.success('删除成功!');
-          this.reload();
-        });
-      },
+    },
+    //删除接口
+    remove(delData) {
+      deleteSendInformation(delData).then((res) => {
+        this.$message.success('删除成功!');
+        this.reload();
+      });
+    },
 
-      //删除弹框确定
-      commitBtn() {
-        const dataId = this.selection.map((v) => v.id);
-        this.remove(dataId);
-      },
-      sendSubmit(res) {
-        submit({
-          businessId:res.id
-        }).then((res) => {
-          this.$message.success('提交成功!');
-          this.reload();
-        });
-      },
-      //查看详情
-      openorderDetail(row, type) {
-        if (type === 'docNo') {
-          this.$refs.DetailDialogRef.open(row);
-        }
-        if (type === 'orderNo') {
-          this.$refs.orderDetailDialogRef.open({ id: row.orderId });
-        }
+    //删除弹框确定
+    commitBtn() {
+      const dataId = this.selection.map((v) => v.id);
+      this.remove(dataId);
+    },
+    sendSubmit(res) {
+      submit({
+        businessId: res.id
+      }).then((res) => {
+        this.$message.success('提交成功!');
+        this.reload();
+      });
+    },
+    //查看详情
+    openorderDetail(row, type) {
+      if (type === 'docNo') {
+        this.$refs.DetailDialogRef.open(row);
+      }
+      if (type === 'orderNo') {
+        this.$refs.orderDetailDialogRef.open({id: row.orderId});
       }
     }
-  };
+  }
+};
 </script>
 
 <style lang="scss" scoped>
-  .ele-body {
-    padding-top: 0;
-    padding-bottom: 0;
-  }
-  :deep .el-card__body {
-    padding: 0;
-  }
-  :deep(.el-link--inner) {
-    margin-left: 0px !important;
-  }
+.ele-body {
+  padding-top: 0;
+  padding-bottom: 0;
+}
 
-  .sys-organization-list {
-    height: calc(100vh - 264px);
-    box-sizing: border-box;
-    border-width: 1px;
-    border-style: solid;
-    overflow: auto;
-  }
-  .sys-organization-list :deep(.el-tree-node__content) {
-    height: 40px;
-    & > .el-tree-node__expand-icon {
-      margin-left: 10px;
-    }
-  }
+:deep .el-card__body {
+  padding: 0;
+}
 
-  .switch_left ul .active {
-    border-top: 4px solid var(--color-primary);
-    color: var(--color-primary-5);
-  }
-  .switch {
-    padding-bottom: 20px;
-  }
+:deep(.el-link--inner) {
+  margin-left: 0px !important;
+}
 
-  .el-dropdown-link {
-    cursor: pointer;
-    color: var(--color-primary-5);
-  }
-  .el-icon-arrow-down {
-    font-size: 12px;
+.sys-organization-list {
+  height: calc(100vh - 264px);
+  box-sizing: border-box;
+  border-width: 1px;
+  border-style: solid;
+  overflow: auto;
+}
+
+.sys-organization-list :deep(.el-tree-node__content) {
+  height: 40px;
+
+  & > .el-tree-node__expand-icon {
+    margin-left: 10px;
   }
+}
+
+.switch_left ul .active {
+  border-top: 4px solid var(--color-primary);
+  color: var(--color-primary-5);
+}
+
+.switch {
+  padding-bottom: 20px;
+}
+
+.el-dropdown-link {
+  cursor: pointer;
+  color: var(--color-primary-5);
+}
+
+.el-icon-arrow-down {
+  font-size: 12px;
+}
 </style>