mlchen 3 дней назад
Родитель
Сommit
d7933b0831

+ 0 - 9
.claude/settings.local.json

@@ -1,9 +0,0 @@
-{
-  "permissions": {
-    "allow": [
-      "Bash(python *)",
-      "Bash(node *)",
-      "mcp__ide__getDiagnostics"
-    ]
-  }
-}

+ 6 - 0
README.md

@@ -1,2 +1,8 @@
 node版本----v16.20.0
 
+## 客户审批详情
+
+`contact_approve` 未配置流程表单路由时,待办使用
+`src/views/bpm/handleTask/components/contact/contactDetailDialog.vue` 和
+`src/views/bpm/handleTask/components/contact/submit.vue`;已办及抄送使用同一详情组件。
+客户详情以只读方式展示当前档案版本。

+ 1 - 0
src/BIZComponents/outdetails.vue

@@ -686,6 +686,7 @@
                     categoryName: productItem.categoryName,
                     categoryCode: productItem.categoryCode,
                     bizNo: productItem.bizNo,
+                    deliveryDetailId: productItem.deliveryDetailId,
                     materialDetailList: packingItem.materialDetailList.map(
                       (materialItem) => {
                         return {

+ 1 - 1
src/api/bpm/components/offerApproval/index.js

@@ -87,7 +87,7 @@ export function adaptHiringApproval(raw = {}) {
 
 /** 根据业务主键获取录用审批详情(审批页 businessId) */
 export async function getHiringApprovalById(id) {
-  const res = await request.get(`/recruit/hiring-approvals/${id}`);
+  const res = await request.get(`/hr/recruit/hiring-approvals/${id}`);
   if (res.data.code == 0) {
     return adaptHiringApproval(res.data.data || {});
   }

+ 5 - 5
src/api/hr/hiringApproval.js

@@ -318,29 +318,29 @@ export function serializeHiringApprovalQuery(params = {}) {
 /** 录用审批分页查询 GET /recruit/hiring-approvals */
 export const getHiringApprovalPage = async (params = {}) =>
   unwrap(
-    await request.get("/recruit/hiring-approvals", {
+    await request.get("/hr/recruit/hiring-approvals", {
       params: serializeHiringApprovalQuery(params),
     }),
   );
 
-/** 录用审批详情查询 GET /recruit/hiring-approvals/{id},id 为列表主键 */
+/** 录用审批详情查询 GET /hr/recruit/hiring-approvals/{id},id 为列表主键 */
 export const getHiringApprovalById = async (id) => {
   const hiringId = keepBigIntId(id);
   if (hiringId == null || hiringId === "") {
     return Promise.reject(new Error("缺少录用审批主键"));
   }
   return adaptHiringApproval(
-    await unwrap(await request.get(`/recruit/hiring-approvals/${hiringId}`)),
+    await unwrap(await request.get(`/hr/recruit/hiring-approvals/${hiringId}`)),
   );
 };
 
 /**
- * 保存录用审批草稿 POST /recruit/hiring-approvals
+ * 保存录用审批草稿 POST /hr/recruit/hiring-approvals
  * 成功返回主键 id;前端发起时再调 BPM create
  */
 export const createHiringApproval = async (data) => {
   const payload = serializeHiringApproval(data || {});
-  return unwrap(await request.post("/recruit/hiring-approvals", payload));
+  return unwrap(await request.post("/hr/recruit/hiring-approvals", payload));
 };
 
 /** 与 create 同一接口(文档无独立修改接口) */

+ 101 - 31
src/components/addDoc/file-edit.vue

@@ -151,7 +151,7 @@
         codeType: ''
       };
       return {
-        activeName: '1',
+        activeName: '2',
 
         templateVisible: false,
         folderList: [],
@@ -208,17 +208,58 @@
       setFolderList(this.folderList); //权限过滤
     },
     methods: {
+      // 递归在树中按 name 查找节点
+      findNodeByName(tree, name) {
+        if (!tree || !tree.length) return null;
+        for (const node of tree) {
+          if (node.name === name) return node;
+          if (node.sonDirectoryList && node.sonDirectoryList.length) {
+            const found = this.findNodeByName(node.sonDirectoryList, name);
+            if (found) return found;
+          }
+        }
+        return null;
+      },
+      // 默认 编码分类 = "文档"(在树中按名称查找,未找到时回退到 options[0].parentId)
+      applyDefaultCodeType() {
+        const docNode = this.findNodeByName(this.list, '文档');
+        if (docNode) {
+          this.form.codeType = docNode.id;
+        } else if (this.options && this.options.length > 0) {
+          this.form.codeType = this.options[0].parentId;
+        }
+      },
+      // 默认 编码方案 = "通用文档编码"(在 options 中按名称查找,未找到时回退到第一个)
+      applyDefaultBusinessCode() {
+        if (!this.options || this.options.length === 0) {
+          this.form.businessCodeId = '';
+          return;
+        }
+        const generalDoc = this.options.find(
+          (opt) => opt.name === '通用文档编码'
+        );
+        this.form.businessCodeId = generalDoc
+          ? generalDoc.id
+          : this.options[0].id;
+      },
       handleClick() {
         if (this.activeName == 2) {
           this.folderList = this.myFolderList;
         } else {
           this.folderList = this.allFolderList;
         }
-        // 切换 tab 时使用新 tab 的默认值(文档位置 / 编码分类 / 编码方案)联动更新
-        // 其他字段(type / 备注 / 已上传文件等)保留不动
+        // 切换 tab 时只更新 directoryId / options,保留用户已选的 codeType / businessCodeId
         if (this.folderList && this.folderList.length > 0) {
-          this.form.directoryId = this.folderList[0].id;
-          this.initFileCode();
+          const firstFolder = this.folderList[0];
+          this.form.directoryId = firstFolder.id;
+          // 显式传 nodeData,避开 cascader 时序问题
+          this.initFileCode({
+            id: firstFolder.id,
+            parentId:
+              firstFolder.parentId != null && firstFolder.parentId !== 0
+                ? firstFolder.parentId
+                : ''
+          });
         }
       },
       async getTreeCode() {
@@ -234,29 +275,66 @@
             nodeData[0]?.data?.parentId != 0 ? nodeData[0]?.data?.parentId : ''
         };
       },
-      async initFileCode() {
-        await this.getTreeCode();
-        this.options = this.nodeData.id ? await listCode(this.nodeData) : [];
-        if (this.options.length > 0) {
-          this.form.businessCodeId = this.options[0].id;
-          this.form.codeType = this.options[0].parentId;
+      async initFileCode(forceNodeData) {
+        if (forceNodeData && forceNodeData.id) {
+          // 外部显式传入 nodeData,绕过 cascader 时序问题
+          this.nodeData = forceNodeData;
         } else {
-          this.form.businessCodeId = '';
+          await this.getTreeCode();
+        }
+        // 保留用户已选 codeType,只刷新 options(open() 中会先设默认值)
+        if (this.form.codeType) {
+          // 用 codeType 触发 listParentId 加载 options(与 typeChange 数据源一致)
+          await this.loadOptionsByCodeType(this.form.codeType);
+        } else {
+          // 回退:使用 listCode(按目录加载)
+          this.options = this.nodeData.id ? await listCode(this.nodeData) : [];
+        }
+        // 如果当前 businessCodeId 不在新的 options 中,重置为"通用文档编码"(默认)
+        if (this.options.find((opt) => opt.id === this.form.businessCodeId)) {
+          // 保留用户已选
+        } else {
+          this.applyDefaultBusinessCode();
         }
       },
+      // 按 编码分类 加载对应的 编码方案 options
+      async loadOptionsByCodeType(codeType) {
+        let obj = {
+          pageNum: 1,
+          size: 100,
+          parentId: codeType
+        };
+        if (this.activeName == 1) {
+          obj['objId'] = this.nodeData?.id;
+          obj['objParentId'] = this.nodeData?.parentId;
+        }
+        let data = await listParentId(obj);
+        this.options = data.list.filter((item) => item.type == 2);
+      },
       async open() {
-        // 每次重新打开都回到"文档工作区"标签
-        this.activeName = '1';
-        this.folderList = this.allFolderList;
+        // 默认进入"个人文档"标签
+        this.activeName = '2';
+        this.folderList = this.myFolderList;
         this.showEditFlag = true;
         this.$nextTick(async () => {
           this.list = await selectTreeList();
-          // 强制使用 文档工作区 的默认值(避免 cancel() 保留的旧 directoryId 导致默认值缺失)
+          // 默认 编码分类 = "文档"(仅在 open 时设置默认值,后续切 tab/改目录时保留用户选择)
+          this.applyDefaultCodeType();
+          // 强制使用 个人文档 的默认值(避免 cancel() 保留的旧 directoryId 导致默认值缺失)
           if (this.folderList && this.folderList.length > 0) {
-            this.form.directoryId = this.folderList[0].id;
+            const firstFolder = this.folderList[0];
+            this.form.directoryId = firstFolder.id;
+            // 显式构造 nodeData 并传入 initFileCode,避开 cascader getCheckedNodes 的时序问题
+            await this.initFileCode({
+              id: firstFolder.id,
+              parentId:
+                firstFolder.parentId != null && firstFolder.parentId !== 0
+                  ? firstFolder.parentId
+                  : ''
+            });
+          } else {
+            await this.initFileCode();
           }
-          // 触发 initFileCode,根据选中的文档位置默认填充 编码分类 / 编码方案
-          await this.initFileCode();
           // 文档类型默认值
           if (!this.form.type) {
             this.form.type = '1';
@@ -276,18 +354,10 @@
         });
       },
       async typeChange(val) {
-        let obj = {
-          pageNum: 1,
-          size: 100,
-          parentId: val
-        };
-        if (this.activeName == 1) {
-          obj['objId'] = this.nodeData?.id;
-          obj['objParentId'] = this.nodeData?.parentId;
-        }
-        let data = await listParentId(obj);
-        this.options = data.list.filter((item) => item.type == 2);
-        this.form.businessCodeId = '';
+        // 切换 编码分类 后重新加载 options
+        await this.loadOptionsByCodeType(val);
+        // 默认选中"通用文档编码"(若存在),用户可自行修改
+        this.applyDefaultBusinessCode();
       },
 
       fileChange(file) {

+ 166 - 1
src/views/bpm/carbonCopy/index.vue

@@ -70,7 +70,26 @@
         :columns="columns"
         :datasource="datasource"
         cache-key="systemRoleTable5"
+        :selection.sync="selection"
       >
+        <template v-slot:toolbar>
+          <el-button
+            type="primary"
+            class="el-icon-printer"
+            size="small"
+            @click="handlePrint"
+          >
+            打印
+          </el-button>
+          <!-- <el-button
+            type="warning"
+            class="el-icon-document"
+            size="small"
+            @click="handleMockPrint"
+          >
+            测试打印(mock)
+          </el-button> -->
+        </template>
         <template v-slot:result="{ row }">
           <el-tag
             size="medium"
@@ -117,6 +136,8 @@
       </ele-pro-table>
     </el-card>
     <detail ref="detailRef"></detail>
+    <!-- 隐藏的打印服务实例:复用 collaborative/detail 的 printRows 批量打印 -->
+    <formDetail ref="printService" v-show="false"></formDetail>
   </div>
 </template>
 
@@ -125,6 +146,7 @@
   import { ccPage, ccReaderTask } from '@/api/bpm/task';
   import { getDate } from '@/utils/dateUtils';
   import detail from '@/views/bpm/done/detailDialog.vue';
+  import formDetail from '@/views/bpm/collaborative/detail.vue';
 
   // 默认表单数据
   const defaultParams = {
@@ -133,15 +155,24 @@
   };
   export default {
     name: 'BpmDoneTask',
-    components: { detail },
+    components: { detail, formDetail },
     mixins: [dictMixins],
     data() {
       return {
         // 遮罩层
         loading: true,
         params: { ...defaultParams },
+        // 表格勾选项
+        selection: [],
         statusList: [],
         columns: [
+          {
+            type: 'selection',
+            columnKey: 'selection',
+            width: 55,
+            align: 'center',
+            fixed: 'left'
+          },
           {
             columnKey: 'index',
             label: '序号',
@@ -302,6 +333,8 @@
             id: row.processInstanceId
           },
           businessId:row.vals.businessId,
+          businessKey: String(row.processDefinitionId || '').split(':')[0],
+          processDefinitionKey: String(row.processDefinitionId || '').split(':')[0],
           pcViewRouter: row.pcViewRouter
         }
         // this.$refs.detailRef.open({
@@ -313,6 +346,138 @@
         // });
         this.$refs.detailRef.open(params);
       },
+      /* 勾选记录批量打印:复用 collaborative/detail 的 printRows */
+      handlePrint() {
+        if (!this.selection || this.selection.length === 0) {
+          this.$message.warning('请先勾选需要打印的记录');
+          return;
+        }
+        if (!this.$refs.printService) {
+          this.$message.error('打印组件未就绪');
+          return;
+        }
+        this.$refs.printService.printRows(this.selection);
+      },
+      /* 测试打印:使用本地 mock 数据,不依赖列表/接口 */
+      handleMockPrint() {
+        if (!this.$refs.printService) {
+          this.$message.error('打印组件未就绪');
+          return;
+        }
+        // 构造两条 mock 流程记录,验证多记录批量打印 + 表单模板渲染
+        const mockRows = this.buildMockPrintRows();
+        this.$refs.printService.printRows(mockRows);
+      },
+      /**
+       * 构造测试用 mock 数据
+       * - id:用于 loadTasks 拉审批记录(mock 时会失败,被 catch 吞掉)
+       * - formJson.makingJson:表单模板 JSON,含 list(控件列表) + config.dataSource(数据源)
+       * - valueJson:表单值,键名与控件 model 对应
+       */
+      buildMockPrintRows() {
+        const makingJson = {
+          list: [
+            {
+              type: 'input',
+              model: 'applicant',
+              label: '申请人'
+            },
+            {
+              type: 'input',
+              model: 'department',
+              label: '部门'
+            },
+            {
+              type: 'textarea',
+              model: 'reason',
+              label: '事由'
+            },
+            {
+              type: 'blank',
+              model: 'blank_adopzrdd',
+              label: '报销明细',
+              tableRows: []
+            }
+          ],
+          config: {
+            platform: 'pc',
+            dataSource: []
+          }
+        };
+        const baseRow = {
+          processInstanceId: 'mock-pi-001',
+          processDefinitionId: 'mock_pd:1:mock',
+          processTypeName: '报销流程',
+          taskName: '财务复核',
+          name: '2024年第三季度差旅报销(mock)',
+          vals: {
+            businessCode: 'BX-2024-001',
+            businessName: '差旅报销',
+            businessType: '财务',
+            businessId: 'mock-biz-001',
+            userName: '张三'
+          },
+          result: '1',
+          processResult: '1',
+          createTime: '2024-09-15 09:30:00',
+          endTime: '2024-09-18 17:45:00',
+          durationInMillis: 86400000 * 3,
+          isRead: 0,
+          id: 'mock-cc-001',
+          pcViewRouter: '',
+          formJson: {
+            makingJson: JSON.stringify(makingJson)
+          },
+          valueJson: {
+            applicant: '张三',
+            department: '研发中心 / 前端组',
+            reason: '赴北京参加高端制造业数字化转型峰会,并拜访清华大学工业工程系相关教授。',
+            blank_adopzrdd: [
+              {
+                remark: '2024年第三季度跨省差旅,含交通、住宿、餐饮、市内交通等费用',
+                price: 8866.5,
+                file: [
+                  { name: '出差审批单-登机牌-酒店水单-发票合订版.pdf', url: 'https://example.com/file1.pdf' },
+                  { name: '出租车发票-餐费发票-打车小票-打包扫描.pdf', url: 'https://example.com/file3.pdf' }
+                ]
+              },
+              {
+                remark: '客户答谢晚宴及产品发布会场地、餐饮、灯光、礼仪等费用',
+                price: 32680,
+                file: [
+                  { name: '场地租赁合同正本(盖章版).pdf', url: 'https://example.com/file4.pdf' },
+                  { name: '增值税专用发票_2024-09-15_001.jpg', url: 'https://example.com/file5.jpg' }
+                ]
+              }
+            ]
+          }
+        };
+        // 第二条用来验证批量打印分页
+        const second = JSON.parse(JSON.stringify(baseRow));
+        second.id = 'mock-cc-002';
+        second.processInstanceId = 'mock-pi-002';
+        second.name = '2024年市场推广活动费用报销(mock)';
+        second.vals = {
+          ...baseRow.vals,
+          businessCode: 'BX-2024-002',
+          businessName: '市场推广费',
+          userName: '李四'
+        };
+        second.valueJson = {
+          ...baseRow.valueJson,
+          applicant: '李四',
+          department: '市场部 / 品牌组',
+          reason: '组织新产品发布会及客户答谢晚宴。',
+          blank_adopzrdd: [
+            {
+              remark: '发布会场地、舞台、灯光、音响设备租赁费用',
+              price: 15800,
+              file: [{ name: '场地租赁发票.pdf', url: 'https://example.com/scene.pdf' }]
+            }
+          ]
+        };
+        return [baseRow, second];
+      },
       getTimelineItemType(result) {
         if (result === '通过') {
           return 'success';

+ 12 - 1
src/views/bpm/done/detailDialog.vue

@@ -254,7 +254,18 @@ import businessTripComponent from '@/BIZComponents/processSubmitDialog/component
         if (this.isStockBatchChange() && (!router || invalidViewRouter)) {
           return '/bpm/handleTask/components/stockBatchChange/detailDialog.vue';
         }
-        const path = String(router || '').trim();
+        const businessKey = String(
+          this.form.businessKey ||
+            this.form.processDefinitionKey ||
+            this.form.processInstance?.processDefinitionId?.split(':')[0] ||
+            this.form.processInstance?.processDefinition?.key ||
+            ''
+        );
+        const fallback =
+          businessKey === 'contact_approve'
+            ? '/bpm/handleTask/components/contact/contactDetailDialog.vue'
+            : '';
+        const path = String(!router || router === '/' ? fallback : router).trim();
         if (!path || path === '/' || !path.startsWith('/')) return '';
         return path;
       },

+ 7 - 1
src/views/bpm/handleTask/components/contact/contactDetailDialog.vue

@@ -40,6 +40,11 @@
                 <el-input v-model="form.simpleName" disabled></el-input>
               </el-form-item>
             </el-col>
+            <el-col :span="8" v-if="form.version != null">
+              <el-form-item label="档案版本:" prop="version">
+                <el-input v-model="form.version" disabled></el-input>
+              </el-form-item>
+            </el-col>
 
             <el-col :span="8">
               <el-form-item label="法定代表人:" prop="legalPerson">
@@ -335,7 +340,8 @@
         legalPerson: '',
         registeredCapital: '',
         type: 1,
-        unifiedSocialCreditCode: ''
+        unifiedSocialCreditCode: '',
+        version: null
       };
       let otherFormDef = {
         settlementMode: '',

+ 19 - 5
src/views/bpm/handleTask/components/financialManage/invoiceManage/components/addOrEditDialog.vue

@@ -78,6 +78,7 @@
                 :controls="false"
                 style="width: 100%"
                 :min="0"
+                class="amount-input"
               ></el-input-number>
             </el-form-item>
           </el-col>
@@ -85,7 +86,7 @@
             <el-form-item label="发票号" prop="invoiceNos">
               <el-input
                 v-model="form.invoiceNos"
-                disabled
+                :disabled="!isFinanceUser"
               ></el-input>
             </el-form-item>
           </el-col>
@@ -244,7 +245,7 @@
       @setPrice="setPrice"
     ></table-info> -->
     <div style="margin-top: 20px; margin-bottom: 20px;">
-        <headerTitle title="收款计划信息"></headerTitle>
+        <headerTitle title="来源清单"></headerTitle>
         <!-- <el-form ref="form" :model="tableForm"> -->
           <ele-pro-table
             ref="table"
@@ -370,6 +371,12 @@
     },
     computed: {
       ...mapGetters(['user']),
+      // 是否属于是“财务主管”角色
+      // 仅在角色下的用户可编辑"发票号"字段
+      isFinanceUser() {
+        const isFinanceUser = this.user?.info?.roleId.some(id => id === "1881600883548532738");
+        return isFinanceUser;
+      },
       columns() {
         return [
           {
@@ -534,8 +541,9 @@
       },
       //获取详情
       async getInfo(id) {
-        this.form = await invoiceApplyInfoV2API(id);
-        this.form.relatedList = [this.form.relatedVO];
+        this.form = (await invoiceApplyInfoV2API(id)) || {};
+        //来源清单没有数据时给空数组,避免表格因空行报错
+        this.form.relatedList = this.form.relatedVO ? [this.form.relatedVO] : [];
         this.$nextTick(() => {
           // 根据来源类型设置数据源:99 用 tableInfo(setValue),非 99 用 tableInfoNew(putValue)
           if (this.form.sourceType == 99) {
@@ -620,4 +628,10 @@
     }
   };
 </script>
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+  .amount-input {
+    ::v-deep .el-input__inner {
+      text-align: left;
+    }
+  }
+</style>

+ 8 - 0
src/views/bpm/handleTask/components/financialManage/payableManage/components/detailDialog.vue

@@ -25,6 +25,7 @@
             style="margin-bottom: 22px"
           >
             <el-input-number
+              class="amount-input"
               disabled
               v-model="form.payableTotalPrice"
               :precision="2"
@@ -233,6 +234,13 @@
     }
   }
 
+  // 应付金额:金额输入框内文本靠左显示
+  .amount-input {
+    ::v-deep .el-input__inner {
+      text-align: left;
+    }
+  }
+
   .headbox {
     display: flex;
     justify-content: flex-start;

+ 8 - 8
src/views/bpm/handleTask/components/purchaseOrder/accountstatement/payablesInfo.vue

@@ -187,14 +187,14 @@
             align: 'center',
             showOverflowTooltip: true
           },
-          {
-            prop: 'fundType',
-            minWidth: 140,
-            label: '款项类型',
-            align: 'center',
-            slot: 'fundType',
-            headerSlot: 'columnRequired'
-          },
+          // {
+          //   prop: 'fundType',
+          //   minWidth: 140,
+          //   label: '款项类型',
+          //   align: 'center',
+          //   slot: 'fundType',
+          //   headerSlot: 'columnRequired'
+          // },
           {
             prop: 'transactionMode',
             minWidth: 140,

+ 7 - 7
src/views/bpm/handleTask/components/saleOrder/accountstatement/receivableInfo.vue

@@ -176,13 +176,13 @@
             align: 'center',
             showOverflowTooltip: true
           },
-          {
-            prop: 'fundType',
-            minWidth: 140,
-            label: '款项类型',
-            align: 'center',
-            slot: 'fundType'
-          },
+          // {
+          //   prop: 'fundType',
+          //   minWidth: 140,
+          //   label: '款项类型',
+          //   align: 'center',
+          //   slot: 'fundType'
+          // },
           {
             prop: 'transactionMode',
             minWidth: 140,

+ 19 - 9
src/views/bpm/handleTask/components/saleOrder/returnGoods/addReturnGoodsDialog.vue

@@ -713,21 +713,31 @@
           this.$refs.outboundDetailsDialogRef.init(this.form, list);
         });
       },
-      saveDate(data) {
-        data.forEach((item) => {
+      saveDate(data, closeDialog) {
+        const priceSources = data.map((item) =>
+          item.deliveryDetailId
+            ? this.productList.find(
+                (val) => String(val.sendProductId || val.id) === String(item.deliveryDetailId)
+              )
+            : null
+        );
+        if (priceSources.some((source) => !source)) {
+          this.$message.error('未找到对应的发货明细,无法确定退货单价');
+          return;
+        }
+        data.forEach((item, index) => {
           item['totalCount'] = item.measureQuantity;
           item['orderId'] = this.orderOption[0]?.orderId;
           item['orderNo'] = this.orderOption[0]?.orderNo;
-          this.productList.forEach((val) => {
-            if (item.productCode == val.productCode) {
-              item['singlePrice'] = val.singlePrice;
-              item['clientCode'] = item.clientCode || val.customerMark;
-              item['pricingWay'] = item.pricingWay || val.pricingWay;
-            }
-          });
+          const priceSource = priceSources[index];
+          item['sendProductId'] = priceSource.sendProductId || priceSource.id;
+          item['singlePrice'] = priceSource.singlePrice;
+          item['clientCode'] = item.clientCode || priceSource.customerMark;
+          item['pricingWay'] = item.pricingWay || priceSource.pricingWay;
         });
         this.$refs.inventoryTableref &&
           this.$refs.inventoryTableref.putTableValue(data);
+        if (closeDialog) closeDialog();
       },
       cancel() {
         this.$nextTick(() => {

+ 2 - 3
src/views/bpm/handleTask/components/saleOrder/returnGoods/outboundDetailsDialog.vue

@@ -58,10 +58,9 @@ export default {
   
     handleSave() {
       const list = this.$refs.outboundDetailsDialogRef.getValue()||[]
-      if (list.lenght==0) return this.$message.warning('请选择退货明细')
+      if (list.length==0) return this.$message.warning('请选择退货明细')
 
-      this.$emit('saveDate',list)
-      this.cancel()
+      this.$emit('saveDate',list, () => this.cancel())
     },
     cancel() {
       this.$emit('update:outboundDetailsDialogFlag', false)

+ 11 - 2
src/views/bpm/handleTask/index.vue

@@ -487,7 +487,7 @@
           this.parseFormRouteConfig(this.listData.formKey) ||
           this.parseFormRouteConfig(this.listData.formCustomCreatePath) ||
           {};
-        // 录用审批 / 劳动合同:流程未配路由时使用本项目约定路径
+        // 已有审批组件的流程未配路由时使用本项目约定路径
         const businessKey = String(
           def.key ||
             this.listData.businessKey ||
@@ -495,7 +495,13 @@
             ''
         );
         const formFallback =
-          businessKey === 'hr_recruit_hiring_approval'
+          businessKey === 'contact_approve'
+            ? {
+                pcView:
+                  '/bpm/handleTask/components/contact/contactDetailDialog.vue',
+                pcHandle: '/bpm/handleTask/components/contact/submit.vue'
+              }
+            : businessKey === 'hr_recruit_hiring_approval'
             ? {
                 pcView:
                   '/bpm/handleTask/components/hiringApproval/hiringApprovalDetail.vue',
@@ -573,6 +579,9 @@
         this.$refs.bziRef.save(data);
       },
       open(data) {
+        // 先清空上一条流程,让业务表单组件在新数据渲染前卸载,
+        // 避免旧流程的组件带着新流程的 businessId 重新取数(串 id 请求)
+        this.processInstance = {};
         this.listData = data;
         console.log('实例流程~~~~~~', data);
         this.dialogVisible = true;

+ 7 - 7
src/views/bpm/outgoingManagement/components/AssetsDialog.vue

@@ -533,8 +533,8 @@
         return String(product.productId) === String(row.categoryId)
           && String(product.receiveProductId) === String(row.sourceReceiveProductId)
           && String(product.warehouseId) === String(row.warehouseId)
-          && String(product.batchNo || '') === String(row.batchNo || '')
-          && String(product.systemBatchNo || '') === String(row.systemBatchNo || '');
+          && (!product.batchNo || String(product.batchNo) === String(row.batchNo || ''))
+          && (!product.systemBatchNo || String(product.systemBatchNo) === String(row.systemBatchNo || ''));
       },
       formatMeasureQuantity(row) {
         return this.dimension == 2
@@ -634,11 +634,11 @@
           if (this.bizType === 10 && receiveProductIds.length !== legacyProducts.length) {
             this.tableData = [];
             this.total = 0;
-            this.$message.error('退货产品缺少采购收货明细,无法查询不良库存');
+            this.$message.error('退货产品缺少采购收货明细,无法查询库存');
             return;
           }
           const params = this.bizType === 10
-            ? { ...this.searchForm, sourceReceiveProductIds: receiveProductIds.join(','), result: 2 }
+            ? { ...this.searchForm, sourceReceiveProductIds: receiveProductIds.join(',') }
             : this.searchForm;
           const data = await storageApi.getBatchList(params);
           for (let i = 0; i < data.list.length; i++) {
@@ -667,7 +667,7 @@
                 ...(packageSources.length ? {
                   packageNos: [...new Set(packageSources.map((source) => source.packageNo))].join(',')
                 } : {}),
-                result: 2 }
+                ...(packageSources.length ? { result: 2 } : {}) }
             : this.searchForm;
           const data = await storageApi.getPackingList(params);
           const rows = this.bizType === 10
@@ -805,7 +805,7 @@
           data = await storageApi.getHierarchyList({
             ids: this.selectionList.map((item) => item.id).join(','),
             type: this.dimension,
-            result: this.bizType === 10 ? 2 : null
+            result: this.bizType === 10 && this.returnPackages().length ? 2 : null
           });
           if (this.bizType === 10) {
             data.forEach((detail) => {
@@ -830,7 +830,7 @@
           const returnProducts = this.returnProductList;
           data = await storageApi.getHierarchyFifo({
             type: this.dimension,
-            result: this.bizType === 10 ? 2 : null,
+            result: null,
             ids: this.selectionList.map((item) => item.id),
             builders: this.selectionList.map((item) => {
               return {

+ 19 - 22
src/views/bpm/processInstance/detailNew.vue

@@ -57,29 +57,26 @@
                 >
                   {{ getDateStar(item.durationInMillis) }}
                 </label>
-                <label
-                  v-if="item.result != 1"
-                  style="margin-left: 30px; color: #8a909c; font-weight: normal"
-                >
-                  签名:
-                </label>
-                <label v-if="item.result != 1">
-                  <el-image
-                    style="
-                      width: 50px;
-                      height: 50px;
-                      position: absolute;
-                      transform: translate(0px, 0px);
-                    "
-                    :src="item?.assigneeUser?.signature[0]?.url"
-                  >
-                    <div slot="error" class="image-slot">
-                      <!--                        <i class="el-icon-picture-outline"></i>-->
-                      <span></span>
-                    </div>
-                  </el-image>
-                </label>
 
+                <div
+                    v-if="item.result != 1"
+                    style="margin-top: 6px; display: flex; align-items: center;"
+                  >
+                    <label style="color: #8a909c; font-weight: normal; margin-right: 8px;">
+                      签名:
+                    </label>
+                    <el-image
+                      v-if="item?.assigneeUser?.signature[0]?.url"
+                      style="width: 50px; height: 50px;"
+                      :src="item?.assigneeUser?.signature[0]?.url"
+                      :preview-src-list="item?.assigneeUser?.signature?.map(s => s.url)"
+                      fit="contain">
+                      <div slot="error" class="image-slot">
+                        <span></span>
+                      </div>
+                    </el-image>
+                </div>
+                
                 <p v-if="item.reason && item.result != 4">
                   审批意见:<div :class="getTimelineItemType(item)">{{
                     item.reason

+ 2 - 2
vue.config.js

@@ -38,7 +38,7 @@ module.exports = {
         // target: 'http://124.71.68.31:50001', // 测试环境
         // target: 'http://124.71.68.31:50001',
         // target: 'http://192.168.1.105:18086',
-        target: 'http://localhost:18090',
+        // target: 'http://localhost:18090',
         // target: 'http://192.168.1.116:18086',
         // target: 'http://192.168.1.3:18086',
         // target: 'http://192.168.1.251:18186',
@@ -46,7 +46,7 @@ module.exports = {
         // target: 'http://192.168.1.3:18086',
         // target: 'http://aiot.zoomwin.com.cn:51001/api',
         // target: 'http://192.168.1.3:18086',
-        // target: 'http://192.168.1.147:18086', // 陈兵
+        target: 'http://192.168.1.147:18086', // 陈兵
         // target: 'http://192.168.1.116:18086', // 赵沙金
         // target: 'http://192.168.1.113:18086', // 郭丹