瀏覽代碼

售后质量问题反馈流程审批页面

hezhanp 9 月之前
父節點
當前提交
8c98d687e9

+ 1 - 0
src/BIZComponents/processSubmitDialog/components/SHGDListDialog.vue

@@ -232,6 +232,7 @@ export default {
       const params = {
         pageNum: page,
         size: limit,
+        isDispatch:'0',
         ...where
       };
       return getSalesWorkOrder(params).finally(() => {

+ 20 - 0
src/api/afterSales/index.js

@@ -42,4 +42,24 @@ export async function salesrealcostinfo(id) {
     return res.data.data;
   }
   return Promise.reject(new Error(res.data.message));
+}
+//售后服务 售后需求详情
+
+export async function getSalesDemandById(id) {
+  const res = await request.get(`/eom/afterSalesDemand/getById/${id}`);
+  if (res.data.code === "0") { 
+    return res.data; 
+  }
+  return Promise.reject(new Error(res.data.message || "获取售后需求详情失败"));
+}
+
+/**
+ * 获取客户信息详情
+ */
+export async function contactDetail(id) {
+  const res = await request.get(`/eom/contact/getById/${id}`, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
 }

+ 1145 - 0
src/views/bpm/handleTask/components/salesServiceManagement/demand/salesServiceDialog.vue

@@ -0,0 +1,1145 @@
+<template>
+  <el-form
+    ref="form"
+    :model="form"
+    :rules="rules"
+    label-width="130px"
+    class="create-form"
+  >
+    <!-- 标题区域 -->
+    <headerTitle
+      title="基本信息"
+      style="margin-top: 8px"
+      v-if="isPurchaseNeed"
+    ></headerTitle>
+    <headerTitle
+      title="需求信息"
+      style="margin-top: 8px"
+      v-if="!isPurchaseNeed"
+    ></headerTitle>
+
+    <!-- 基本信息表单区域 -->
+    <el-row :gutter="24" v-if="source != '报工信息'">
+      <el-col :span="6">
+        <el-form-item label="需求编码:">
+          <el-input v-model="form.code" disabled />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="关联类型:">
+          <el-select
+            style="width: 100%"
+            v-model="form.associationType"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in associationTypeList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="需求名称:">
+          <el-select
+            style="width: 100%"
+            v-model="form.name"
+            filterable
+            allow-create
+            default-first-option
+            placeholder="请选择需求名称"
+            disabled
+          >
+            <el-option
+              v-for="item in demandList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="客户名称:">
+          <el-input
+            v-model="form.contractInfo.contactName"
+            readonly
+            disabled
+            placeholder="请选择"
+          />
+        </el-form-item>
+      </el-col>
+
+      <!-- 关联类型对应的订单展示 -->
+      <el-col :span="6" v-if="form.associationType == '1'">
+        <el-form-item label="发货单:">
+          <el-input
+            v-model="form.orderCode"
+            readonly
+            disabled
+            placeholder="请选择"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6" v-if="form.associationType == '2'">
+        <el-form-item label="销售订单:">
+          <el-input
+            v-model="form.orderCode"
+            readonly
+            disabled
+            placeholder="请选择"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6" v-if="form.associationType == '3'">
+        <el-form-item label="产品:">
+          <el-input
+            v-model="form.orderCode"
+            readonly
+            disabled
+            placeholder="请选择"
+          />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="6">
+        <el-form-item label="客户编码:">
+          <el-input v-model="form.contractInfo.code" readonly disabled />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="客户代号:">
+          <el-input v-model="form.contractInfo.serialNo" readonly disabled />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="业务员:">
+          <el-select
+            style="width: 100%"
+            v-model="form.salespersonId"
+            placeholder="请选择业务员"
+            filterable
+            clearable
+            disabled
+          >
+            <el-option
+              v-for="item in userList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="报修地址:">
+          <el-input v-model="form.contactAddress" readonly disabled />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="6">
+        <el-form-item label="故障等级:">
+          <DictSelection
+            dictName="故障等级"
+            disabled
+            v-model="form.faultLevel"
+          ></DictSelection>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="期望解决时间:">
+          <el-date-picker
+            disabled
+            style="width: 100%"
+            v-model="form.expectedTime"
+            type="date"
+            :picker-options="pickerOptions"
+            placeholder="选择日期时间"
+          ></el-date-picker>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="售后类型:">
+          <el-select
+            style="width: 100%"
+            v-model="form.aftertype"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in aftertypeList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="是否收费:">
+          <el-select
+            style="width: 100%"
+            v-model="form.charge"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in chargeList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="6">
+        <el-form-item label="是否带配件:">
+          <el-select
+            style="width: 100%"
+            v-model="form.part"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in partList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="是否派车:">
+          <el-select
+            style="width: 100%"
+            v-model="form.car"
+            placeholder="请选择是否派车"
+            disabled
+          >
+            <el-option
+              v-for="item in carList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6" v-if="form.car == '1'">
+        <el-form-item label="派车类型:">
+          <el-select
+            style="width: 100%"
+            v-model="form.pietype"
+            placeholder="请选择"
+            disabled
+          >
+            <el-option
+              v-for="item in pietypeList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="是否外包:">
+          <el-select
+            style="width: 100%"
+            v-model="form.isOutsource"
+            placeholder="请选择是否外包"
+            disabled
+          >
+            <el-option
+              v-for="item in outsourceList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="6" v-if="form.isOutsource == '1'">
+        <el-form-item label="供应商名称:">
+          <el-input
+            v-model="form.supplierName"
+            placeholder="请选择供应商名称"
+            readonly
+            disabled
+          ></el-input>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="是否生成采购订单:">
+          <el-select
+            style="width: 100%"
+            v-model="form.isCreatePurchaseOrder"
+            placeholder="请选择是否生成采购订单"
+            disabled
+          >
+            <el-option
+              v-for="item in isCreatePurchaseOrderList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="涉及事业部门:">
+          <ele-tree-select
+            v-model="form.involveDeptId"
+            :data="deptTreeData"
+            label-key="name"
+            value-key="id"
+            clearable
+            default-expand-all
+            placeholder="请选择"
+            disabled
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="备注说明:" class="remark-input">
+          <el-input
+            v-model="form.remark"
+            type="textarea"
+            rows="2"
+            placeholder="请输入备注信息(选填)"
+            readonly
+            disabled
+          ></el-input>
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <!-- 售后方案区域 -->
+    <headerTitle
+      title="售后方案"
+      style="margin-top: 10px"
+      v-if="isPurchaseNeed"
+    ></headerTitle>
+    <spareParts
+      ref="sparePartsRef"
+      type="view"
+      :source="source"
+      obtain="售后"
+      :is-after-sales="true"
+      v-if="isPurchaseNeed"
+    />
+
+    <!-- 售后对象/故障信息区域 -->
+    <div class="after_sales_target" v-if="salesShow">
+      <headerTitle
+        title="售后对象"
+        style="margin-top: 10px"
+        v-if="isPurchaseNeed"
+      ></headerTitle>
+      <ele-pro-table
+        ref="tableRef3"
+        :columns="columnsDetail"
+        :datasource="form.tableList"
+        :need-page="false"
+        :toolbar="false"
+        maxHeight="300px"
+        row-key="id"
+        default-expand-all
+      >
+        <template v-slot:measureQuantity="{ row }">
+          <el-input
+            v-model="row.measureQuantity"
+            type="number"
+            :min="1"
+            placeholder="请输入"
+            disabled
+          ></el-input>
+        </template>
+        <template v-slot:batchNo="{ row }">
+          <el-input
+            v-model="row.batchNo"
+            placeholder="请输入"
+            disabled
+          ></el-input>
+        </template>
+        <template v-slot:barcodes="{ row }">
+          <el-input
+            v-model="row.barcodes"
+            placeholder="请输入"
+            disabled
+          ></el-input>
+        </template>
+        <template v-slot:shipmentDate="{ row }">
+          <el-date-picker
+            v-model="row.shipmentDate"
+            type="datetime"
+            placeholder="选择结束时间"
+            format="yyyy-MM-dd HH:mm:ss"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            disabled
+          ></el-date-picker>
+        </template>
+
+        <template v-slot:expand="{ row, $index }">
+          <div
+            style="
+              width: calc(100% - 55px);
+              min-height: 60px;
+              margin-left: 55px;
+            "
+            v-if="row.faultDetails && row.faultDetails.length > 0"
+          >
+            <ele-pro-table
+              :columns="faultList"
+              :datasource="row.faultDetails || []"
+              :need-page="false"
+              maxHeight="300px"
+              class="el-form-box"
+              :toolkit="[]"
+            >
+              <template v-slot:type="{ row }">
+                <el-select
+                  v-model="row.type"
+                  disabled
+                  :placeholder="
+                    form.aftertype === '3' ? '请选择质量类型' : '请选择故障类型'
+                  "
+                >
+                  <el-option
+                    v-for="item in form.aftertype === '3'
+                      ? qualityTypeOptions
+                      : typeOptions"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  ></el-option>
+                </el-select>
+              </template>
+              <template v-slot:faultPhenomenon="{ row }">
+                <el-input
+                  v-model="row.faultPhenomenon"
+                  placeholder="请输入"
+                  disabled
+                ></el-input>
+              </template>
+              <template v-slot:faultReason="{ row }">
+                <el-input
+                  v-model="row.faultReason"
+                  placeholder="请输入"
+                  disabled
+                ></el-input>
+              </template>
+              <template v-slot:maintenanceProcess="{ row }">
+                <el-input
+                  v-model="row.maintenanceProcess"
+                  placeholder="请输入"
+                  disabled
+                ></el-input>
+              </template>
+              <template v-slot:attachments="{ row }">
+                <fileMain v-model="row.attachments" type="view"></fileMain>
+              </template>
+
+              <template v-slot:headerType="{ column }">
+                <span>{{
+                  form.aftertype === '3' ? '质量类型' : column.label
+                }}</span>
+              </template>
+              <template v-slot:headerPhenomenon="{ column }">
+                <span>{{
+                  form.aftertype === '3' ? '质量问题反馈' : column.label
+                }}</span>
+              </template>
+              <template v-slot:headerMaintenance="{ column }">
+                <span>{{
+                  form.aftertype === '3' ? '质量处理方式' : column.label
+                }}</span>
+              </template>
+              <template v-slot:headerFaultReason="{ column }">
+                <span>{{
+                  form.aftertype === '3' ? '质量问题分析' : column.label
+                }}</span>
+              </template>
+            </ele-pro-table>
+          </div>
+        </template>
+
+        <template v-slot:useLocation="{ row }">
+          <el-input v-model="row.useLocation" disabled></el-input>
+        </template>
+      </ele-pro-table>
+    </div>
+
+    <div class="fault_infor" v-else>
+      <headerTitle
+        title="故障信息"
+        style="margin-top: 10px"
+        v-if="form.aftertype !== '3'"
+      ></headerTitle>
+      <headerTitle
+        title="质量反馈信息"
+        style="margin-top: 10px"
+        v-if="form.aftertype === '3'"
+      ></headerTitle>
+
+      <ele-pro-table
+        :columns="faultList"
+        :datasource="form.faultDetailList"
+        :need-page="false"
+        maxHeight="300px"
+        class="el-form-box"
+        :toolkit="[]"
+      >
+        <template v-slot:type="{ row }">
+          <el-select
+            v-model="row.type"
+            disabled
+            :placeholder="
+              form.aftertype === '3' ? '请选择质量类型' : '请选择故障类型'
+            "
+          >
+            <el-option
+              v-for="item in form.aftertype === '3'
+                ? qualityTypeOptions
+                : typeOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            ></el-option>
+          </el-select>
+        </template>
+        <template v-slot:faultPhenomenon="{ row }">
+          <el-input
+            v-model="row.faultPhenomenon"
+            placeholder="请输入"
+            disabled
+          ></el-input>
+        </template>
+        <template v-slot:faultReason="{ row }">
+          <el-input
+            v-model="row.faultReason"
+            placeholder="请输入"
+            disabled
+          ></el-input>
+        </template>
+        <template v-slot:maintenanceProcess="{ row }">
+          <el-input
+            v-model="row.maintenanceProcess"
+            placeholder="请输入"
+            disabled
+          ></el-input>
+        </template>
+        <template v-slot:attachments="{ row }">
+          <fileMain v-model="row.attachments" type="view"></fileMain>
+        </template>
+
+        <template v-slot:headerType="{ column }">
+          <span>{{ form.aftertype === '3' ? '质量类型' : column.label }}</span>
+        </template>
+        <template v-slot:headerPhenomenon="{ column }">
+          <span>{{
+            form.aftertype === '3' ? '质量问题反馈' : column.label
+          }}</span>
+        </template>
+        <template v-slot:headerMaintenance="{ column }">
+          <span>{{
+            form.aftertype === '3' ? '质量处理方式' : column.label
+          }}</span>
+        </template>
+        <template v-slot:headerFaultReason="{ column }">
+          <span>{{
+            form.aftertype === '3' ? '质量问题分析' : column.label
+          }}</span>
+        </template>
+      </ele-pro-table>
+    </div>
+
+    <!-- 联系人信息区域 -->
+    <headerTitle
+      title="联系人信息"
+      style="margin-top: 10px"
+      v-if="isPurchaseNeed"
+    ></headerTitle>
+    <ele-pro-table
+      :style="{ marginTop: isPurchaseNeed ? 0 : '10px' }"
+      ref="table"
+      :columns="bankColumns"
+      :datasource="form.contactInfoVOS"
+      :toolkit="[]"
+      class="el-form-box"
+      :need-page="false"
+      v-if="source != '报工信息'"
+    >
+      <template v-slot:contactName="{ row }">
+        <el-input
+          v-model="row.contactName"
+          placeholder="请输入"
+          disabled
+          maxlength="50"
+        ></el-input>
+      </template>
+      <template v-slot:contactPhone="{ row }">
+        <el-input
+          v-model="row.contactPhone"
+          placeholder="请输入"
+          disabled
+        ></el-input>
+      </template>
+      <template v-slot:post="{ row }">
+        <el-input v-model="row.post" placeholder="请输入" disabled></el-input>
+      </template>
+      <template v-slot:telephone="{ row }">
+        <el-input
+          v-model="row.telephone"
+          placeholder="请输入"
+          disabled
+        ></el-input>
+      </template>
+      <template v-slot:email="{ row }">
+        <el-input v-model="row.email" placeholder="请输入" disabled></el-input>
+      </template>
+      <template v-slot:wechat="{ row }">
+        <el-input v-model="row.wechat" placeholder="请输入" disabled></el-input>
+      </template>
+      <template v-slot:deptName="{ row }">
+        <el-input
+          v-model="row.deptName"
+          placeholder="请输入"
+          disabled
+        ></el-input>
+      </template>
+      <template v-slot:remark="{ row }">
+        <el-input v-model="row.remark" placeholder="请输入" disabled></el-input>
+      </template>
+    </ele-pro-table>
+  </el-form>
+</template>
+
+<script>
+import fileMain from '@/components/addDoc/index.vue';
+import dictMixins from '@/mixins/dictMixins';
+import { mapGetters } from 'vuex';
+import spareParts from './sparePartsList.vue';
+import { getByCode } from '@/api/system/dictionary-data';
+import { listOrganizations } from '@/api/system/organization';
+import { getUserPage } from '@/api/system/organization';
+import { getSalesDemandById, contactDetail } from '@/api/afterSales/index';
+
+export default {
+  mixins: [dictMixins],
+  components: {
+    fileMain,
+    spareParts
+  },
+  computed: {
+    ...mapGetters(['getDictValue']),
+    sxtValueLabelMap() {
+      return this.sxtList.reduce((map, item) => {
+        map[item.value] = item.label;
+        return map;
+      }, {});
+    },
+    salesShow() {
+      if (this.form.associationType === '1') return true;
+      if (this.form.tableList.length > 0) return true;
+      if (this.form.associationType === '2' && !this.isOrder) return true;
+      return false;
+    },
+    bankColumns() {
+      return [
+        {
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center'
+        },
+        {
+          label: '姓名',
+          prop: 'contactName',
+          slot: 'contactName',
+          align: 'center'
+        },
+        {
+          label: '手机',
+          prop: 'contactPhone',
+          slot: 'contactPhone',
+          align: 'center'
+        },
+        {
+          label: '电话',
+          prop: 'telephone',
+          slot: 'telephone',
+          align: 'center'
+        },
+        {
+          label: '微信号',
+          prop: 'wechat',
+          slot: 'wechat',
+          align: 'center'
+        },
+        {
+          label: '邮箱',
+          prop: 'email',
+          slot: 'email',
+          align: 'center'
+        },
+        {
+          label: '部门',
+          prop: 'deptName',
+          slot: 'deptName',
+          align: 'center'
+        },
+        {
+          label: '职务',
+          prop: 'post',
+          slot: 'post',
+          align: 'center'
+        },
+        {
+          label: '备注',
+          prop: 'remark',
+          slot: 'remark',
+          align: 'center'
+        }
+      ];
+    },
+    columnsDetail() {
+      return [
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          width: 45,
+          type: 'expand',
+          columnKey: 'expand',
+          align: 'center',
+          slot: 'expand'
+        },
+        {
+          prop: 'productCategoryName',
+          label: '类型',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 200
+        },
+        {
+          prop: 'categoryCode',
+          label: '编码',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'categoryName',
+          label: '名称',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'categoryModel',
+          label: '型号',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          slot: 'batchNo',
+          prop: 'batchNo',
+          label: '批次号',
+          align: 'center',
+          minWidth: 135
+        },
+        {
+          slot: 'barcodes',
+          prop: 'barcodes',
+          label: '发货条码',
+          align: 'center',
+          minWidth: 135
+        },
+        {
+          slot: 'shipmentDate',
+          prop: 'shipmentDate',
+          label: '发货时间',
+          align: 'center',
+          minWidth: 180
+        },
+        {
+          prop: 'guaranteePeriodDeadline',
+          label: '有效期',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'warrantyStatus',
+          label: '质保状态',
+          align: 'center',
+          showOverflowTooltip: true,
+          formatter: (_row) =>
+            _row.warrantyStatus === 1 ? '有效期外' : '有效期内'
+        },
+        {
+          slot: 'measureQuantity',
+          prop: 'measureQuantity',
+          label: '计量数量',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'singlePrice',
+          label: '单价(元)',
+          align: 'center',
+          showOverflowTooltip: true
+        }
+      ];
+    },
+    faultList() {
+      const isQualityScene = this.form.aftertype === '3';
+      return [
+        {
+          type: 'index',
+          label: '序号',
+          align: 'center',
+          width: 60
+        },
+        {
+          prop: 'type',
+          slot: 'type',
+          label: isQualityScene ? '质量类型' : '故障类型',
+          align: 'center',
+          headerSlot: 'headerType',
+          showOverflowTooltip: true,
+          width: 120
+        },
+        {
+          prop: 'faultPhenomenon',
+          slot: 'faultPhenomenon',
+          headerSlot: 'headerPhenomenon',
+          label: isQualityScene ? '质量问题反馈' : '故障现象',
+          align: 'center',
+          minWidth: 240
+        },
+        {
+          prop: 'faultReason',
+          slot: 'faultReason',
+          headerSlot: 'headerFaultReason',
+          label: isQualityScene ? '质量问题分析' : '故障原因',
+          align: 'center',
+          minWidth: 340
+        },
+        {
+          prop: 'maintenanceProcess',
+          slot: 'maintenanceProcess',
+          headerSlot: 'headerMaintenance',
+          label: isQualityScene ? '质量处理方式' : '维修过程',
+          align: 'center',
+          width: 340
+        },
+        {
+          prop: 'imageUrl',
+          slot: 'imageUrl',
+          label: '查看拍照',
+          align: 'center',
+          width: 120
+        }
+      ];
+    },
+    tableOperate() {
+      return false;
+    },
+    faultOperate() {
+      return () => false;
+    },
+    change_permission() {
+      return true;
+    },
+    fileStr() {
+      return 'view';
+    },
+    imageUrleng() {
+      return (row) => (row.imageUrl ? row.imageUrl.length : 0);
+    }
+  },
+  props: {
+    businessId: {
+      default: '',
+      required: true
+    },
+    isPurchaseNeed: {
+      default: true,
+      type: Boolean
+    },
+    source: {
+      default: '',
+      type: String
+    },
+    demandList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  watch: {
+    demandList: {
+      handler(val) {
+        this.demandList = val;
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  data() {
+    return {
+      userList: [],
+      pickerOptions: {
+        disabledDate(time) {
+          return time.getTime() < new Date().setHours(0, 0, 0, 0);
+        }
+      },
+      detailList: [],
+      chargeList: [
+        { label: '是', value: '1' },
+        { label: '否', value: '0' }
+      ],
+      partList: [
+        { label: '是', value: '1' },
+        { label: '否', value: '0' }
+      ],
+      carList: [
+        { label: '是', value: '1' },
+        { label: '否', value: '0' }
+      ],
+      typeOptions: [
+        { label: '维修', value: '1' },
+        { label: '保养', value: '2' },
+        { label: '安装', value: '3' }
+      ],
+      qualityTypeOptions: [],
+      outsourceList: [
+        { label: '是', value: '1' },
+        { label: '否', value: '0' }
+      ],
+      isCreatePurchaseOrderList: [
+        { label: '是', value: '1' },
+        { label: '否', value: '0' }
+      ],
+      sxtList: [
+        { label: '自制件', value: '1' },
+        { label: '采购件', value: '2' },
+        { label: '外协件', value: '3' },
+        { label: '受托件', value: '4' }
+      ],
+      form: {
+        contractInfo: {},
+        tableList: [],
+        contactInfoVOS: [],
+        faultDetailList: [],
+        associationType: '1',
+        aftertype: '',
+        salespersonName: '',
+        salespersonId: '',
+        charge: '',
+        part: '',
+        car: '',
+        pietype: '',
+        isOutsource: '0',
+        isCreatePurchaseOrder: '0',
+        supplierName: '',
+        supplierId: '',
+        supplierCode: '',
+        involveDeptId: '',
+        involveDeptName: '',
+        remark: ''
+      },
+      rules: {},
+      associationTypeList: [
+        { value: '1', label: '发货单' },
+        { value: '2', label: '销售订单' },
+        { value: '3', label: '客户' }
+      ],
+      isOrder: false,
+      aftertypeList: [],
+      pietypeList: [],
+      deptTreeData: []
+    };
+  },
+  async created() {
+    // 独立加载每个字典,单独捕获错误,不阻塞整体流程
+    await this.loadDict('after_sales_type', 'aftertypeList', '售后类型');
+    await this.loadDict('pie_car_type', 'pietypeList', '派车类型');
+    await this.loadDict('after_sale_quality_type', 'qualityTypeOptions', '质量类型');
+    
+    // 其他初始化操作
+    await this.getUserList().catch(err => {
+      this.$message.error(`获取用户列表失败:${err.message}`);
+    });
+    await this.getDeptTreeData().catch(err => {
+      this.$message.error(`获取部门数据失败:${err.message}`);
+    });
+  },
+  mounted() {
+    this.getDetail(this.businessId);
+  },
+  methods: {
+    async getDetail(id) {
+      try {
+        const res = await getSalesDemandById(id);
+        if (!res || res.code !== '0' || !res.data) {
+          this.$message.error('获取详情失败:接口返回异常');
+          return;
+        }
+        this.init(res.data);
+      } catch (err) {
+        this.$message.error(`获取详情失败:${err.message}`);
+      }
+    },
+    /**
+     * 通用字典加载方法(核心修改:兼容数组/对象两种数据格式)
+     * @param {String} dictCode - 字典编码(如pie_car_type)
+     * @param {String} targetKey - 存储字典的data属性名(如pietypeList)
+     * @param {String} dictName - 字典名称(用于错误提示)
+     */
+    async loadDict(dictCode, targetKey, dictName) {
+      try {
+        const res = await getByCode(dictCode);
+        // 1. 基础校验:接口是否返回成功且有数据
+        if (res.code !== '0' || !res.data) { // 注意:接口返回code是字符串"0",需匹配类型
+          throw new Error(`接口返回异常(code: ${res.code},data: ${res.data ? '存在' : '不存在'})`);
+        }
+
+        // 2. 核心修改:兼容数组/对象两种格式的data
+        let dictItems = [];
+        if (Array.isArray(res.data)) {
+          // 格式1:data是数组(如[{ "1": "工时" }, { "2": "零配件" }])
+          dictItems = res.data;
+        } else {
+          // 格式2:data是对象(如{ "1": "工时", "2": "零配件" })
+          dictItems = Object.values(res.data);
+        }
+
+        // 3. 解析字典数据:提取键值对,确保value为字符串
+        const dictData = dictItems.map(el => {
+          // 每个el是单个键值对对象(如{ "1": "工时" })
+          const key = Object.keys(el)[0];
+          const value = Object.values(el)[0];
+          return { 
+            label: value || '', // 防止label为空
+            value: String(key)  // 强制转为字符串,与表单value类型匹配
+          };
+        });
+
+        // 4. 赋值到目标变量
+        this[targetKey] = dictData;
+        console.log(`【${dictName}】字典加载成功:`, dictData); // 可选:调试用
+      } catch (err) {
+        // 单独提示某个字典失败,不影响其他功能
+        this.$message.warning(`获取${dictName}字典失败:${err.message}`);
+        // 赋值空数组,避免渲染报错
+        this[targetKey] = [];
+      }
+    },
+    async getDeptTreeData() {
+      const deptRes = await listOrganizations();
+      this.deptTreeData = this.$util.toTreeData({
+        data: deptRes || [],
+        idField: 'id',
+        parentIdField: 'parentId'
+      });
+    },
+    async getUserList() {
+      const res = await getUserPage({ pageNum: 1, pageSize: -1 });
+      this.userList = res?.list || [];
+    },
+    init(res) {
+      const productDetail = res.productDetail || [];
+      this.$set(this.form, 'tableList', productDetail);
+      this.$set(this.form, 'orderCode', res.orderCode);
+      this.$set(this.form, 'orderId', res.orderId);
+      this.$set(this.form, 'contactInfoVOS', res.contactInfoVOS || []);
+      this.$set(this.form, 'name', res.name);
+      this.$set(this.form, 'faultLevel', res.faultLevel ? String(res.faultLevel) : '');
+      this.$set(this.form, 'code', res.code);
+      this.$set(this.form, 'expectedTime', res.expectedTime);
+      this.$set(this.form, 'contactAddress', res.contactAddress || '');
+
+      this.contactDetail(res.contactId, 'init');
+      this.form.associationType = res.associationType ? String(res.associationType) : '1';
+      this.$set(this.form, 'faultDetailList', res.faultDetailList || []);
+      this.isOrder = res.faultDetailList?.length > 0;
+
+      this.$set(this.form, 'aftertype', res.afterSalesType ? String(res.afterSalesType) : '');
+      this.$set(this.form, 'charge', res.isFee !== undefined ? String(res.isFee) : '');
+      this.$set(this.form, 'part', res.isWithAccessories !== undefined ? String(res.isWithAccessories) : '');
+      this.$set(this.form, 'car', res.isPieCar !== undefined ? String(res.isPieCar) : '');
+      this.$set(this.form, 'pietype', res.pieCarType ? String(res.pieCarType) : '');
+      this.$set(this.form, 'salespersonName', res.salespersonName || '');
+      this.$set(this.form, 'salespersonId', res.salespersonId || '');
+      this.$set(this.form, 'isOutsource', res.isOutsource !== undefined ? String(res.isOutsource) : '0');
+      this.$set(this.form, 'isCreatePurchaseOrder', res.isCreatePurchaseOrder !== undefined ? String(res.isCreatePurchaseOrder) : '0');
+      this.$set(this.form, 'remark', res.remark || '');
+      this.$set(this.form, 'involveDeptId', res.involveDeptId ?? '');
+      this.$set(this.form, 'involveDeptName', res.involveDeptName ?? '');
+      this.$set(this.form, 'supplierId', res.supplierId ?? '');
+      this.$set(this.form, 'supplierName', res.supplierName ?? '');
+      this.$set(this.form, 'supplierCode', res.supplierCode ?? '');
+      this.$set(this, 'detailList', res.costListVOS || []);
+
+      this.$nextTick(() => {
+        if (this.$refs.sparePartsRef && this.detailList.length > 0) {
+          this.$refs.sparePartsRef.setTableValue(this.detailList);
+        }
+      });
+    },
+    async contactDetail(id, type) {
+      if (!id) {
+        this.contractInfo = {};
+        return;
+      }
+      try {
+        const { base, other } = await contactDetail(id);
+        base.contactName = base.name;
+        const addressName = `${other.addressName || ''}${other.address || ''}`;
+        this.$set(this.form, 'contractInfo', base);
+        if (type !== 'init') {
+          this.$set(this.form, 'contactAddress', addressName);
+        }
+      } catch (err) {
+        this.$message.error(`获取客户详情失败:${err.message}`);
+      }
+    },
+    getValue() {
+      return this.form;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-table__body .el-form-item__content) {
+  margin-left: 0px !important;
+}
+:deep(.el-table__body .el-form-item) {
+  margin-bottom: 0px !important;
+}
+.reserve {
+  margin-left: 16px;
+  color: #e6a23c;
+}
+:deep(.el-row) {
+  display: flex;
+  flex-wrap: wrap;
+}
+:deep(.remark-input .el-textarea__inner) {
+  color: #f56c6c !important;
+}
+:deep(.remark-input .el-textarea.is-disabled .el-textarea__inner) {
+  color: #f56c6c !important;
+}
+</style>

+ 474 - 0
src/views/bpm/handleTask/components/salesServiceManagement/demand/sparePartsList.vue

@@ -0,0 +1,474 @@
+<template>
+  <div class="ele-body">
+    <el-form ref="form" :model="form" label-width="100px" class="create-form">
+      <ele-pro-table
+        ref="tableRef2"
+        :columns="columnsDetail"
+        :datasource="form.tableList"
+        tool-class="ele-toolbar-form"
+        :initLoad="false"
+        :need-page="false"
+        height="300px"
+      >
+        <!-- 工具栏:仅保留合计显示,隐藏添加按钮 -->
+        <template v-slot:toolbar>
+          <div class="toobar toobar_r">
+            <span class="total_price">总计: {{ totalPrice }} (元)</span>
+          </div>
+        </template>
+
+        <!-- 项目类型:禁用下拉 -->
+        <template v-slot:typeId="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.typeId'"
+          >
+            <el-select
+              v-model="scope.row.typeId"
+              placeholder="请选择"
+              class="w100"
+              disabled
+            >
+              <el-option
+                v-for="item in typeIdList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <!-- 费用类别:禁用下拉 -->
+        <template v-slot:feeType="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.feeType'"
+          >
+            <el-select
+              v-model="scope.row.feeType"
+              placeholder="请选择"
+              class="w100"
+              disabled
+            >
+              <el-option
+                v-for="item in feeTypeList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <!-- 编码:纯文本展示 -->
+        <template v-slot:code="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.code'"
+          >
+            <span>{{ scope.row.code || '-' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 名称:纯文本展示(隐藏选择按钮) -->
+        <template v-slot:name="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.name'"
+          >
+            <span>{{ scope.row.name || '-' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 型号:纯文本展示 -->
+        <template v-slot:categoryModel="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.categoryModel'"
+          >
+            <span>{{ scope.row.categoryModel || '-' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 规格:纯文本展示 -->
+        <template v-slot:specification="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.specification'"
+          >
+            <span>{{ scope.row.specification || '-' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 详细内容:纯文本展示 -->
+        <template v-slot:content="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.content'"
+          >
+            <span>{{ scope.row.content || '-' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 数量:纯文本展示 -->
+        <template v-slot:totalCount="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.totalCount'"
+          >
+            <span>{{ scope.row.totalCount || '0' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 单位:纯文本展示 -->
+        <template v-slot:measureUnit="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.measureUnit'"
+          >
+            <span>{{ scope.row.measureUnit || '-' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 单价:纯文本展示 -->
+        <template v-slot:singlePrice="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.singlePrice'"
+          >
+            <span>{{ scope.row.singlePrice || '0' }}</span>
+          </el-form-item>
+        </template>
+
+        <!-- 仓库:纯文本展示(显示仓库名称,隐藏下拉) -->
+        <template v-slot:warehouseId="scope">
+          <el-form-item
+            style="margin-bottom: 20px"
+            :prop="'tableList.' + scope.$index + '.warehouseId'"
+          >
+            <span>{{ scope.row.warehouseName || '-' }}</span>
+          </el-form-item>
+        </template>
+
+      </ele-pro-table>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { getWarehouseOutStock, getIdWarehouseList } from '@/api/afterSales/index';
+import { getByCode } from '@/api/system/dictionary-data';
+
+export default {
+  components: {},
+  props: {
+    source: {
+      default: '',
+      type: String
+    },
+    detailList: {
+      type: Array,
+      default: () => []
+    },
+    obtain: {
+      type: String,
+      default: '主数据'
+    },
+    hideFeeType: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      loading: false,
+      form: {
+        tableList: [] // 展示用数据列表
+      },
+      totalPrice: 0, // 合计金额(保留展示)
+      typeIdList: [], // 项目类型字典(核心修复点)
+      feeTypeList: [ // 费用类别字典(硬编码,已适配字符串value)
+        { label: '收入', value: '1' },
+        { label: '支出', value: '2' }
+      ]
+    };
+  },
+  created() {
+    // 初始化项目类型字典(修复后)
+    if (this.source !== '配件回收') { // 配件回收场景固定字典,无需请求
+      this.getLevelCode('after_sales_project');
+    }
+  },
+  computed: {
+    // 表格列配置:隐藏操作列,调整展示逻辑
+    columnsDetail() {
+      return [
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'typeId',
+          label: '项目',
+          align: 'center',
+          width: 120,
+          slot: 'typeId',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'code',
+          label: '编码',
+          align: 'center',
+          slot: 'code',
+          minWidth: 150,
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'feeType',
+          label: '费用类别',
+          align: 'center',
+          slot: 'feeType',
+          minWidth: 150,
+          showOverflowTooltip: true,
+          show: !this.hideFeeType // 保留原隐藏逻辑
+        },
+        {
+          prop: 'name',
+          label: '名称',
+          align: 'center',
+          minWidth: 250,
+          slot: 'name',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'categoryModel',
+          label: '型号',
+          width: 150,
+          align: 'center',
+          slot: 'categoryModel',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'specification',
+          width: 150,
+          label: '规格',
+          slot: 'specification',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'content',
+          minWidth: 300,
+          slot: 'content',
+          label: '详细内容',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'warehouseId',
+          minWidth: 170,
+          slot: 'warehouseId',
+          label: '仓库',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'warehouseNum',
+          minWidth: 100,
+          label: '库存',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'totalCount',
+          slot: 'totalCount',
+          width: 100,
+          label: '数量',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'measureUnit',
+          width: 120,
+          slot: 'measureUnit',
+          label: '单位',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'singlePrice',
+          slot: 'singlePrice',
+          width: 120,
+          label: '单价(元)',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'settlementPrice',
+          width: 80,
+          label: '合计(元)',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        // 完全隐藏操作列
+        {
+          columnKey: 'action',
+          label: '操作',
+          width: 150,
+          align: 'center',
+          resizable: false,
+          slot: 'action',
+          fixed: 'right',
+          showOverflowTooltip: true,
+          show: false
+        }
+      ];
+    },
+    // 以下计算属性均返回固定值,确保操作元素不显示、组件禁用
+    chooseShow() {
+      return () => false; // 隐藏"选择"按钮
+    },
+    buttonJud() {
+      return false; // 隐藏"添加"按钮
+    },
+    schemeOperate() {
+      return () => false; // 隐藏"删除"按钮
+    },
+    schemeTableTool() {
+      return () => true; // 所有表格组件禁用
+    },
+    inputOperate() {
+      return () => true; // 所有输入组件禁用
+    }
+  },
+  watch: {
+    // 监听传入的详情数据,格式化后渲染(修复:类型Id转字符串)
+    detailList: {
+      handler(newVal) {
+        const formattedList = newVal.map((item) => ({
+          ...item,
+          feeType: (item.feeType || '1').toString(), // 费用类别转字符串
+          typeId: (item.typeId || '').toString(), // 项目类型转字符串(核心修复)
+          warehouseName: item.warehouseName || '', // 确保仓库名称有值
+          settlementPrice: item.settlementPrice || (Number(item.totalCount) * Number(item.singlePrice) || 0) // 补全合计金额(修复数字计算)
+        }));
+        this.form.tableList = formattedList;
+        this.calculatePrice(); // 计算合计
+      },
+      immediate: true
+    }
+  },
+  mounted() {
+    // 配件回收场景:固定项目类型为"零配件"(确保value是字符串)
+    if (this.source === '配件回收') {
+      this.typeIdList = [{ label: '零配件', value: '2' }];
+      // 触发一次数据格式化(防止watch未覆盖)
+      if (this.detailList.length > 0) {
+        this.watch.detailList.handler(this.detailList);
+      }
+    }
+  },
+  methods: {
+    // 初始化项目类型字典(核心修复:兼容接口格式+类型匹配)
+    async getLevelCode(code) {
+      try {
+        const res = await getByCode(code);
+        // 修复1:接口code是字符串"0",而非数字0
+        if (res.code !== '0' || !res.data) {
+          throw new Error(`接口返回异常(code: ${res.code})`);
+        }
+
+        // 修复2:兼容接口返回的"数组"或"对象"格式
+        let dictItems = [];
+        if (Array.isArray(res.data)) {
+          dictItems = res.data; // 格式1:[{ "1": "工时" }, { "2": "零配件" }]
+        } else {
+          dictItems = Object.values(res.data); // 格式2:{ "1": "工时", "2": "零配件" }
+        }
+
+        // 修复3:解析为label-value格式,确保value是字符串
+        this.typeIdList = dictItems.map(el => {
+          const key = Object.keys(el)[0];
+          const value = Object.values(el)[0];
+          return {
+            label: value || '', // 防止空label
+            value: String(key) // 强制转为字符串,与表单typeId匹配
+          };
+        });
+
+        console.log('项目类型字典加载成功:', this.typeIdList); // 调试用
+      } catch (err) {
+        this.$message.error(`获取项目类型字典失败:${err.message}`);
+        this.typeIdList = []; // 避免渲染报错
+      }
+    },
+    // 计算合计金额(修复:确保数字类型计算)
+    calculatePrice() {
+      this.totalPrice = this.form.tableList.reduce((total, item) => {
+        return total + (Number(item.settlementPrice) || 0); // 转为数字计算
+      }, 0);
+    },
+    // 接收外部数据并格式化(修复:项目类型转字符串)
+    async setTableValue(val) {
+      const tableList = JSON.parse(JSON.stringify(val));
+      for (const item of tableList) {
+        // 格式化基础字段(确保类型匹配)
+        item.feeType = (item.feeType || '1').toString();
+        item.typeId = (item.typeId || '').toString(); // 项目类型转字符串
+        item.categoryCode = item.categoryCode || item.code;
+        item.categoryName = item.categoryName || item.name;
+        item.settlementPrice = item.settlementPrice || (Number(item.totalCount) * Number(item.singlePrice) || 0);
+        
+        // 补充仓库数据(仅用于展示)
+        if (item.typeId === '2' && item.categoryId) {
+          try {
+            item.warehouseList = await getIdWarehouseList({ categoryId: item.categoryId });
+            if (item.warehouseId) {
+              item.warehouseNum = await getWarehouseOutStock({
+                warehouseId: item.warehouseId,
+                code: item.code
+              });
+            }
+          } catch (err) {
+            console.error('获取仓库数据失败:', err);
+            item.warehouseList = [];
+            item.warehouseNum = 0;
+          }
+        }
+      }
+      this.form.tableList = tableList;
+      this.calculatePrice(); // 重新计算合计
+    },
+    // 获取当前展示数据(保留用于外部获取数据)
+    getTableValue() {
+      return this.form.tableList;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-table__body .el-form-item__content) {
+  margin-left: 0px !important;
+}
+:deep(.el-table__body .el-form-item) {
+  margin-bottom: 0px !important;
+}
+.total_price {
+  margin-left: 24px;
+}
+.toobar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding-right: 16px;
+}
+.toobar_r {
+  justify-content: flex-end;
+}
+</style>

+ 1 - 0
src/views/bpm/todo/index.vue

@@ -291,6 +291,7 @@
       /** 处理审批按钮 */
 
       handleAudit(type, row) {
+        console.log(type,row)
         // return
 
         if (type == 'audit') {

+ 1 - 1
vue.config.js

@@ -40,7 +40,7 @@ module.exports = {
         // target: 'http://192.168.1.105:18086',
 
         // target: 'http://192.168.1.251:18186',
-        target: 'http://192.168.1.125:18086',
+        target: 'http://192.168.1.251:18086',
         // target: 'http://192.168.1.125:18086',
 
         // target: 'http://192.168.1.3:18086',