Răsfoiți Sursa

fix:调整销售订单对账单中选择供应商与选择订单逻辑,使用对账池相关接口

xieyong 1 săptămână în urmă
părinte
comite
f166e71495

+ 15 - 1
src/views/saleManage/contact/components/contactSearch.vue

@@ -5,12 +5,26 @@
 <script>
   import { reviewStatusSelect } from '@/enum/dict';
   export default {
+    props: {
+      /**
+       * 自定义搜索项;不传用下面的默认客户档案搜索项。
+       * 列表数据源换接口时(可筛维度不一样)由调用方传,
+       * item.value 直接就是提交给接口的参数名。
+       */
+      fields: {
+        type: Array,
+        default: null
+      }
+    },
     data() {
       return {};
     },
     computed: {
-      // 表格列配置
       seekList() {
+        return this.fields || this.defaultSeekList;
+      },
+      // 默认搜索项:对接客户档案接口(/eom/contact/page2Usages)
+      defaultSeekList() {
         return [
         {
             label: '关键字:',

+ 115 - 5
src/views/saleManage/contact/components/parentList.vue

@@ -12,14 +12,17 @@
     :maxable="true"
   >
     <el-card shadow="never">
-      <contact-search @search="reload"></contact-search>
+      <contact-search :fields="searchFields" @search="reload"></contact-search>
 
       <ele-split-layout
-        width="244px"
+        :width="treeWidth"
         allow-collapse
         :right-style="{ overflow: 'hidden' }"
       >
-        <div class="ele-border-lighter split-layout-right-content">
+        <div
+          v-if="showCategoryTree"
+          class="ele-border-lighter split-layout-right-content"
+        >
           <AssetTree
             @handleNodeClick="handleNodeClick"
             id="17"
@@ -76,15 +79,75 @@ import tabMixins from '@/mixins/tableColumnsMixin';
       classType: {
         type: Number | String,
         default: 1
+      },
+      /**
+       * 自定义列表数据源。默认(不传)走客户档案 contactPageUsages;
+       * 一旦传入就完全接管请求,组件只负责拼装
+       * { pageNum, size, ...queryParams, ...paramMapper(where) }。
+       *
+       * 例:对账池发起对账时要选「有可对账数据的往来单位」,
+       * 传 getAccountPoolCustomerList(/eom/accountstatementpool/customers)。
+       */
+      dataSourceApi: {
+        type: Function,
+        default: null
+      },
+      /** 固定查询参数,如对账池接口必填的 type(1 销售订单 / 2 采购订单) */
+      queryParams: {
+        type: Object,
+        default: () => ({})
+      },
+      /** 把搜索表单的 where 映射成自定义接口认识的字段名;不传时原样透传 */
+      paramMapper: {
+        type: Function,
+        default: null
+      },
+      /** 左侧客户类别树。接口不支持 categoryId 过滤时传 false 隐藏 */
+      showCategoryTree: {
+        type: Boolean,
+        default: true
+      },
+      /**
+       * 自定义搜索项,透传给 contact-search;不传用默认的客户档案搜索项。
+       * 搜索项的 value 就是提交给列表接口的参数名。
+       */
+      searchFields: {
+        type: Array,
+        default: null
+      },
+      /** 自定义表格列(列对象浅拷贝,不污染父组件传入的数组);不传用默认客户档案列 */
+      tableColumns: {
+        type: Array,
+        default: null
+      }
+    },
+    computed: {
+      columns() {
+        this.columnsVersion; // mixin 改列宽/列显隐时触发重算
+        // 拷贝一层:applyColumnWidth 会往列对象上写 width
+        const cols = (this.tableColumns || this.defaultColumns).map((c) => ({
+          ...c
+        }));
+        return this.applyColumnWidth(cols);
+      },
+      cacheKeyUrl() {
+        // 两套列(客户档案列 / 调用方自定义列)必须用不同的缓存 key:
+        // mixin 的 columnsContrast 按 label 比对,共用一个 key 会把另一套列
+        // 当成「已删除列」洗掉(localStorage + 服务端 table-config 都按它存)
+        return this.tableColumns
+          ? 'views_saleManage_contact_components_parentList_custom'
+          : 'views_saleManage_contact_components_parentList';
+      },
+      treeWidth() {
+        return this.showCategoryTree ? '244px' : '0px';
       }
     },
     data() {
       return {
-      cacheKeyUrl: 'views_saleManage_contact_components_parentList',
         visible: false,
         current: null,
         pageList: new Map(),
-        columns: [
+        defaultColumns: [
           {
             action: 'action',
             slot: 'action',
@@ -202,6 +265,18 @@ import tabMixins from '@/mixins/tableColumnsMixin';
 
       /* 表格数据源 */
       datasource({ page, limit, where, order }) {
+        if (this.dataSourceApi) {
+          // 自定义数据源:由调用方决定请求字段(对账池客户列表等)
+          const mapped = this.paramMapper
+            ? this.paramMapper(where || {})
+            : where;
+          return this.dataSourceApi({
+            pageNum: page,
+            size: limit,
+            ...this.queryParams,
+            ...(mapped || {})
+          }).then((res) => this.normalizeList(res, page));
+        }
         return contactPageUsages({
           pageNum: page,
           size: limit,
@@ -216,6 +291,41 @@ import tabMixins from '@/mixins/tableColumnsMixin';
         });
       },
 
+      /**
+       * 字段归一化 + 分页字段兜底。
+       * 各接口的往来单位字段名不一样(对账池返回 contactId/contactCode/contactName,
+       * 客户档案返回 id/code/name),这里补齐成 id/code/name,
+       * 单选回显、open(item) 预选、父组件拿到的对象都不用改。
+       * count/total 两个分页字段名后端都用过,统一成 ele-pro-table 认的 count。
+       */
+      normalizeRow(row) {
+        return {
+          ...row,
+          id: row.id != null ? row.id : row.contactId,
+          code: row.code != null ? row.code : row.contactCode,
+          name: row.name != null ? row.name : row.contactName
+        };
+      },
+
+      normalizeList(res, page) {
+        // 对账池客户接口的响应字段后端文档没定义全,
+        // 原始响应打出来方便核对表格列该绑哪些字段名
+        console.log('[对账池] 客户列表原始响应:', res);
+        const list = ((res && res.list) || []).map((row) =>
+          this.normalizeRow(row)
+        );
+        if (!this.pageList.has(page)) {
+          this.pageList.set(page, list);
+        }
+        const count =
+          res && res.count != null
+            ? res.count
+            : res && res.total != null
+            ? res.total
+            : list.length;
+        return { ...res, list, count };
+      },
+
       /* 刷新表格 */
       reload(where) {
         this.$refs.table.reload({ page: 1, where });

+ 117 - 3
src/views/saleManage/saleOrder/accountstatement/components/orderListDialog.vue

@@ -11,7 +11,7 @@
     width="70%"
   >
     <el-card shadow="never">
-      <searchTable :needProduces="needProduces" @search="reload"></searchTable>
+      <searchTable :needProduces="needProduces" :fields="searchFields" @search="reload"></searchTable>
       <ele-pro-table
         ref="table"
         :columns="columns"
@@ -61,16 +61,58 @@ export default {
     needProduces: {
       type: String,
       default: ''
+    },
+    /**
+     * 自定义列表数据源。默认(不传)走销售订单列表 getTableList;
+     * 一旦传入就完全接管请求,组件只负责拼装
+     * { pageNum, size, ...queryParams, ...where }。
+     *
+     * 例:对账池发起对账时选「有可对账数据的订单」,
+     * 传 getAccountPoolOrderList(/eom/accountstatementpool/orders)。
+     */
+    dataSourceApi: {
+      type: Function,
+      default: null
+    },
+    /** 固定查询参数,如对账池接口必填的 type(1 销售订单 / 2 采购订单) */
+    queryParams: {
+      type: Object,
+      default: () => ({})
+    },
+    /** 自定义搜索项,透传给 searchTable;不传用默认的销售订单搜索项 */
+    searchFields: {
+      type: Array,
+      default: null
+    },
+    /** 自定义表格列(列对象浅拷贝,不污染父组件传入的数组);不传用默认销售订单列 */
+    tableColumns: {
+      type: Array,
+      default: null
+    }
+  },
+  computed: {
+    columns() {
+      this.columnsVersion; // mixin 改列宽/列显隐时触发重算
+      const cols = (this.tableColumns || this.defaultColumns).map((c) => ({
+        ...c
+      }));
+      return this.applyColumnWidth(cols);
+    },
+    cacheKeyUrl() {
+      // 两套列必须用不同的缓存 key,否则 mixin 的 columnsContrast
+      // 会把另一套列当成「已删除列」洗掉(localStorage + 服务端 table-config 都按它存)
+      return this.tableColumns
+        ? 'views_saleManage_saleOrder_accountstatement_components_orderListDialog_custom'
+        : 'views_saleManage_saleOrder_accountstatement_components_orderListDialog';
     }
   },
   data() {
     return {
-    cacheKeyUrl: 'views_saleManage_saleOrder_accountstatement_components_orderListDialog',
       visible: false,
       currentIndex: null,
       current: null,
       pageList: new Map(),
-      columns: [
+      defaultColumns: [
         {
           columnKey: 'index',
           label: '序号',
@@ -216,6 +258,16 @@ export default {
 
     /* 表格数据源 */
     datasource({ page, limit, where, order }) {
+      if (this.dataSourceApi) {
+        // 自定义数据源:由调用方决定请求字段(对账池订单列表等)
+        // 对账池不需要 contactData / needProduces 这两个销售订单专属条件
+        return this.dataSourceApi({
+          pageNum: page,
+          size: limit,
+          ...this.queryParams,
+          ...(where || {})
+        }).then((res) => this.normalizeList(res, page));
+      }
       if (this.contactData.id) {
         where['contactId'] = this.contactData.id;
       }
@@ -235,6 +287,68 @@ export default {
       });
     },
 
+    /**
+     * 字段归一化 + 分页字段兜底。
+     *
+     * ⚠️ /eom/accountstatementpool/orders 后端 schema 标的是 Pagination«object»,
+     *    没定义任何字段,所以这里对每个字段都做了多来源兜底:
+     *    销售订单列表返回 orderNo/partaId/partaName,对账池返回的是
+     *    往来单位那套 contactId/contactCode/contactName 和来源订单 sourceCode/sourceId。
+     *    归一化后统一成 id / orderNo / partaId / partaName / partaCode,
+     *    父组件的 getOrderInfo(obj) 和单选回显都不用改。
+     *
+     * count/total 两个分页字段名后端都用过,统一成 ele-pro-table 认的 count。
+     */
+    normalizeRow(row) {
+      return {
+        ...row,
+        id:
+          row.id != null
+            ? row.id
+            : row.sourceId != null
+            ? row.sourceId
+            : row.orderId,
+        orderNo: row.orderNo != null ? row.orderNo : row.sourceCode,
+        partaId: row.partaId != null ? row.partaId : row.contactId,
+        partaName: row.partaName != null ? row.partaName : row.contactName,
+        partaCode: row.partaCode != null ? row.partaCode : row.contactCode,
+        // 沿用原「应付金额」列:对账池没有应付金额概念,
+        // 用可对账金额 / 剩余可对账金额兜底
+        payAmount:
+          row.payAmount != null
+            ? row.payAmount
+            : row.accountableAmount != null
+            ? row.accountableAmount
+            : row.availableAmount,
+        // 时间列同理:优先 createTime,其次单据日期、最后最近更新时间
+        createTime:
+          row.createTime != null
+            ? row.createTime
+            : row.docDate != null
+            ? row.docDate
+            : row.updateTime
+      };
+    },
+
+    normalizeList(res, page) {
+      // /orders 的响应 schema 后端没定义(Pagination«object»),
+      // 原始响应打出来方便核对表格列该绑哪些字段名
+      console.log('[对账池] 订单列表原始响应:', res);
+      const list = ((res && res.list) || []).map((row) =>
+        this.normalizeRow(row)
+      );
+      if (!this.pageList.has(page)) {
+        this.pageList.set(page, list);
+      }
+      const count =
+        res && res.count != null
+          ? res.count
+          : res && res.total != null
+          ? res.total
+          : list.length;
+      return { ...res, list, count };
+    },
+
     /* 刷新表格 */
     reload(where) {
       this.$refs.table.reload({ pageNum: 1, where: where });

+ 320 - 1
src/views/saleManage/saleOrder/accountstatement/components/saleForm.vue

@@ -243,12 +243,24 @@
       </el-col>
     </el-row>
 
-    <parentList ref="parentRef" @changeParent="getCusInfo"></parentList>
+    <parentList
+      ref="parentRef"
+      :dataSourceApi="getAccountPoolCustomerList"
+      :queryParams="poolCustomerQuery"
+      :searchFields="poolCustomerSearch"
+      :showCategoryTree="false"
+      :tableColumns="poolCustomerColumns"
+      @changeParent="getCusInfo"
+    ></parentList>
     <orderListDialog
       ref="orderListDialogRef"
       @changeParent="getOrderInfo"
       :contactData="contactData"
       :needProduces="dataForm.queryDimension == 2 ? '4' : '0,1,2,5'"
+      :dataSourceApi="getAccountPoolOrderList"
+      :queryParams="poolOrderQuery"
+      :searchFields="poolOrderSearch"
+      :tableColumns="poolOrderColumns"
     ></orderListDialog>
     <contractListDialog
       ref="selectContractRef"
@@ -265,6 +277,10 @@
   // import fileMain from '@/components/addDoc/index.vue';
   import contractListDialog from '@/views/saleManage/saleOrder/components/contractListDialog.vue';
   import { accountStatementExportAPI } from '@/api/saleManage/accountstatement';
+  import {
+    getAccountPoolCustomerList,
+    getAccountPoolOrderList
+  } from '@/api/saleManage/accountPool';
 
   export default {
     name: 'saleForm',
@@ -326,6 +342,284 @@
         },
         startTime: '-01-01 00:00:00',
         endTime: '23:59:59',
+        /**
+         * 客户选择弹框的搜索项。
+         * 对账池客户接口只支持 客户编码 / 名称 / 订单编码 三个条件,
+         * value 直接就是接口参数名(contactCode/contactName/orderNo),
+         * 不需要字段名映射。默认那套(关键字/创建时间)对账池没有对应参数。
+         * 3 项以内 seekPage 会平铺显示,不会折进「更多选项」下拉。
+         */
+        poolCustomerSearch: [
+          {
+            label: '客户编码:',
+            value: 'contactCode',
+            type: 'input',
+            placeholder: '请输入'
+          },
+          {
+            label: '客户名称:',
+            value: 'contactName',
+            type: 'input',
+            placeholder: '请输入'
+          },
+          {
+            label: '订单编码:',
+            value: 'orderNo',
+            type: 'input',
+            placeholder: '请输入'
+          }
+        ],
+        // 客户选择弹框的列:沿用客户档案的列,接口给得出就有值、给不出就留空。
+        // 打开弹框看一眼哪些列全是空的,就知道哪些可以删。
+        poolCustomerColumns: [
+          {
+            action: 'action',
+            slot: 'action',
+            align: 'center',
+            label: '选择'
+          },
+          {
+            columnKey: 'index',
+            label: '序号',
+            type: 'index',
+            width: 55,
+            align: 'center',
+            showOverflowTooltip: true,
+            fixed: 'left'
+          },
+          {
+            prop: 'code',
+            label: '客户编码',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 140
+          },
+          {
+            prop: 'name',
+            label: '客户名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 200
+          },
+          {
+            prop: 'orderNo',
+            label: '关联订单编码',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 170
+          },
+          {
+            prop: 'serialNo',
+            label: '客户代号',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 130
+          },
+          {
+            prop: 'phone',
+            label: '客户电话',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 120
+          },
+          {
+            prop: 'addressName',
+            label: '单位地址',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 160,
+            formatter: (_row) =>
+              `${(_row.addressName || '').replaceAll(',', '')}${
+                _row.address || ''
+              }`
+          },
+          {
+            prop: 'linkName',
+            label: '联系人',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'linkPhone',
+            label: '联系人电话',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 120
+          },
+          {
+            prop: 'remark',
+            label: '备注',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 180
+          }
+        ],
+        /**
+         * 订单选择弹框的搜索项,与客户弹框同一套:
+         * value 就是接口参数名(contactCode/contactName/orderNo)。
+         * 默认销售订单那套(销售类型/库存状态/合同编号/订单类型/审核状态…)对账池没有对应参数。
+         */
+        poolOrderSearch: [
+          {
+            label: '订单编码:',
+            value: 'orderNo',
+            type: 'input',
+            placeholder: '请输入'
+          },
+          {
+            label: '客户编码:',
+            value: 'contactCode',
+            type: 'input',
+            placeholder: '请输入'
+          },
+          {
+            label: '客户名称:',
+            value: 'contactName',
+            type: 'input',
+            placeholder: '请输入'
+          }
+        ],
+        // 订单选择弹框的列:沿用销售订单的列。
+        // prop 用归一化后的字段名(orderNo / partaCode / partaName),
+        // orderListDialog 的 normalizeRow 会把 contactCode/contactName 补成它们。
+        poolOrderColumns: [
+          {
+            columnKey: 'index',
+            label: '序号',
+            type: 'index',
+            width: 55,
+            align: 'center',
+            showOverflowTooltip: true,
+            fixed: 'left'
+          },
+          {
+            action: 'action',
+            slot: 'action',
+            align: 'center',
+            label: '选择'
+          },
+          {
+            prop: 'orderNo',
+            label: '订单编码',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 200
+          },
+          {
+            prop: 'partaCode',
+            label: '客户编码',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 140
+          },
+          {
+            prop: 'partaName',
+            label: '客户名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 180
+          },
+          {
+            prop: 'contractName',
+            label: '合同名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 180
+          },
+          {
+            prop: 'needProduce',
+            label: '订单类型',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 150,
+            formatter: (_row, _column, cellValue) => {
+              // 无值时留空,别让缺数据的一行显示成「库存式订单」
+              if (cellValue == null) return '';
+              if (cellValue == 1) return '有客户生产性订单';
+              if (cellValue == 2) return '无客户生产性订单';
+              if (cellValue == 4) return '不定向订单';
+              if (cellValue == 5) return '委外订单';
+              return '库存式订单';
+            }
+          },
+          {
+            prop: 'deliveryDate',
+            label: '交货日期',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 120
+          },
+          {
+            prop: 'saleTypeName',
+            label: '销售类型',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 130
+          },
+          {
+            prop: 'partaLinkName',
+            label: '客户联系人',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 120
+          },
+          {
+            prop: 'partaTel',
+            label: '客户联系电话',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 130
+          },
+          {
+            prop: 'partbName',
+            label: '售出方名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 130
+          },
+          {
+            prop: 'partbLinkName',
+            label: '售出方联系人',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 120
+          },
+          {
+            prop: 'partbTel',
+            label: '售出方联系电话',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 130
+          },
+          {
+            // 原「应付金额」列。对账池口径下 normalizeRow 会用
+            // accountableAmount / availableAmount 兜底;都没有就留空。
+            prop: 'payAmount',
+            label: '应付金额(元)',
+            align: 'right',
+            showOverflowTooltip: true,
+            minWidth: 150,
+            formatter: (_row) => {
+              const raw = _row.payAmount;
+              if (raw == null || raw === '') return '';
+              const num = Number(raw);
+              return Number.isFinite(num)
+                ? num.toLocaleString('zh-CN', {
+                    minimumFractionDigits: 2,
+                    maximumFractionDigits: 2
+                  })
+                : '';
+            }
+          },
+          {
+            prop: 'createTime',
+            label: '单据/创建时间',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 160
+          }
+        ],
         rules: {
           sourceType: { required: true, message: '请选择', trigger: 'change' },
           contactId: { required: true, message: '请选择', trigger: 'change' },
@@ -351,6 +645,23 @@
       };
     },
     computed: {
+      // 对账池客户列表的必填参数:1 = 销售订单(销售侧对账只能选客户)
+      poolCustomerQuery() {
+        return { type: 1 };
+      },
+      /**
+       * 对账池订单列表参数:1 = 销售订单。
+       * contactId 是当前已选的客户 —— 接口文档没定义这个字段,
+       * 后端支持就按客户过滤,不支持会忽略掉;不加的话选完客户还能选到别家的订单。
+       */
+      poolOrderQuery() {
+        const params = { type: 1 };
+        const contactId = this.dataForm && this.dataForm.contactId;
+        if (contactId) {
+          params.contactId = contactId;
+        }
+        return params;
+      },
       // dateTypeOps() {
       //   return this.dataForm.queryDimension != 2
       //     ? this.dateTypeOp.filter((item) => item.value != 5)
@@ -369,6 +680,14 @@
       }
     },
     methods: {
+      /**
+       * 对账池客户列表 / 订单列表接口,作为 prop 传给选择弹框当数据源。
+       * Vue 2 的模板表达式只能访问实例上的属性,import 进来的模块函数必须挂到 methods,
+       * 否则渲染时会报 "not defined on the instance"。Vue 2 的 methods 已自动 bind 到实例,
+       * 直接用方法引用传给 prop 即可,不用再包一层箭头函数。
+       */
+      getAccountPoolCustomerList,
+      getAccountPoolOrderList,
       //查询
       handleSearch() {
         //

+ 13 - 1
src/views/saleManage/saleOrder/components/searchTable.vue

@@ -9,14 +9,26 @@
       needProduces: {
         type: String,
         default: ''
+      },
+      /**
+       * 自定义搜索项;不传用下面的默认销售订单搜索项。
+       * 列表数据源换接口时(可筛维度不一样)由调用方传,
+       * item.value 直接就是提交给接口的参数名。
+       */
+      fields: {
+        type: Array,
+        default: null
       }
     },
     data() {
       return {};
     },
     computed: {
-      // 表格列配置
       seekList() {
+        return this.fields || this.defaultSeekList;
+      },
+      // 默认搜索项:对接销售订单列表接口
+      defaultSeekList() {
         return [
           {
             label: '关键字:',