瀏覽代碼

售后服务;

yijing 1 年之前
父節點
當前提交
66a0e95ad0
共有 23 個文件被更改,包括 4203 次插入9 次删除
  1. 1 0
      package.json
  2. 1 1
      src/BIZComponents/outdetails.vue
  3. 10 0
      src/api/classifyManage/index.js
  4. 215 0
      src/api/salesServiceManagement/index.js
  5. 19 7
      src/api/wms/index.js
  6. 428 0
      src/views/salesServiceManagement/components/AssetsDialog.vue
  7. 315 0
      src/views/salesServiceManagement/components/applyForSpare.vue
  8. 124 0
      src/views/salesServiceManagement/components/assetTree.vue
  9. 86 0
      src/views/salesServiceManagement/components/sparePartsList.vue
  10. 508 0
      src/views/salesServiceManagement/demandList/components/addDialog.vue
  11. 114 0
      src/views/salesServiceManagement/demandList/components/search.vue
  12. 293 0
      src/views/salesServiceManagement/demandList/index.vue
  13. 75 0
      src/views/salesServiceManagement/index.vue
  14. 453 0
      src/views/salesServiceManagement/toDoList/components/addOrUpdateDialog.vue
  15. 258 0
      src/views/salesServiceManagement/toDoList/components/generateForm.vue
  16. 71 0
      src/views/salesServiceManagement/toDoList/components/plan-search.vue
  17. 256 0
      src/views/salesServiceManagement/toDoList/index.vue
  18. 120 0
      src/views/salesServiceManagement/workOrder/components/declarationDialog.vue
  19. 250 0
      src/views/salesServiceManagement/workOrder/components/detailDialog.vue
  20. 257 0
      src/views/salesServiceManagement/workOrder/components/redeployOther2.vue
  21. 71 0
      src/views/salesServiceManagement/workOrder/components/work-search.vue
  22. 277 0
      src/views/salesServiceManagement/workOrder/index.vue
  23. 1 1
      vue.config.js

+ 1 - 0
package.json

@@ -39,6 +39,7 @@
     "json-bigint": "^1.0.0",
     "lodash": "^4.17.21",
     "nprogress": "^0.2.0",
+    "print-js": "^1.6.0",
     "tinymce": "^5.10.5",
     "vue": "^2.7.10",
     "vue-clipboard2": "^0.3.3",

+ 1 - 1
src/BIZComponents/outdetails.vue

@@ -662,7 +662,7 @@
           })
           res['bizNo'] = dataArray.map((item) => item.bizNo);
         }
-        console.log(res,'res')
+        console.log(res,'ressssssssssssssssss')
         // res
         this.init(res, type);
       },

+ 10 - 0
src/api/classifyManage/index.js

@@ -66,3 +66,13 @@ export async function getPcTreeByPids (id) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+// 根据ids查询物品分类
+export async function getTreeByIds(data) {
+  const res = await request.get(`/pda/main/categoryLevel/pdaTreeByPid`, {
+    params: data
+  });
+  if (res.data.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 215 - 0
src/api/salesServiceManagement/index.js

@@ -0,0 +1,215 @@
+import request from '@/utils/request';
+import {
+  download
+} from '@/utils/file';
+
+//需求---------------------------------------------------------------
+export async function getPageSalesDemand(data) {
+  const res = await request.get('/eom/afterSalesDemand/page', {
+    params: data
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//新增
+export async function saveSalesDemand(data) {
+  const res = await request.post('/eom/afterSalesDemand/save', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//根据id查询详情
+export async function getSalesDemandById(id) {
+  const res = await request.get(`/eom/afterSalesDemand/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//修改
+export async function updateSalesDemand(data) {
+  const res = await request.put('/eom/afterSalesDemand/update', data);
+  if (res.data.code === '0') {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//删除
+export async function deleteSalesDemand(data) {
+  const res = await request.delete('/eom/afterSalesDemand/delete', {
+    data
+  });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//撤回
+export async function revokeSalesDemand(data) {
+  const res = await request.post(`/eom/afterSalesDemand/retract`, data);
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//审核
+export async function auditSalesDemand(data) {
+  const res = await request.post('/eom/afterSalesDemand/audit', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+
+// 计划-------------------------------------------------------------------
+export async function getSalesPlan(data) {
+  const res = await request.get('/eom/afterSalesPlan/page', {
+    params: data
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//删除
+export async function deleteSalesPlan(data) {
+  const res = await request.delete('/eom/afterSalesPlan/delete', {
+    data
+  });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//根据id查询详情
+export async function getSalesPlanById(id) {
+  const res = await request.get(`/eom/afterSalesPlan/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//修改
+export async function updateSalesPlan(data) {
+  const res = await request.put('/eom/afterSalesPlan/update', data);
+  if (res.data.code === '0') {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//派单
+export async function dispatchOrders(data) {
+  const res = await request.put('/eom/afterSalesPlan/sendOrder', data);
+  if (res.data.code === '0') {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//撤回
+export async function planRevocation(id) {
+  const res = await request.get(`/eom/afterSalesPlan/revocation/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//申请备品备件
+export async function saveOrUpdateSalesPlan(data) {
+  const res = await request.post('/eom/afterSalesPlan/saveOrUpdate', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//生成报价单
+export async function generateQuotationSheet(id) {
+  const res = await request.get(`/eom/afterSalesPlan/generateQuotationSheet/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//下载报价单
+export async function SalesPlanDownload(id) {
+  const res = await request.get(
+    `/eom/afterSalesPlan/download/${id}`, {
+      responseType: 'blob'
+    },
+
+  );
+
+  download(res.data, '报价单.xlsx');
+}
+
+//工单------------------------------------------------------------------------
+export async function getSalesWorkOrder(data) {
+  const res = await request.get('/eom/afterSalesWorkOrder/page', {
+    params: data
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//删除
+export async function deleteSalesWorkOrder(data) {
+  const res = await request.delete('/eom/afterSalesWorkOrder/delete', {
+    data
+  });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//获取信息
+export async function getSalesWorkOrderById(id) {
+  const res = await request.get(`/eom/afterSalesWorkOrder/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//申请备品备件
+export async function saveOrUpdateSalesWorkOrder(data) {
+  const res = await request.post('/eom/afterSalesWorkOrder/saveOrUpdate', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//接收
+export async function receiveSalesWorkOrder(data) {
+  const res = await request.put('/eom/afterSalesWorkOrder/receive', data);
+  if (res.data.code === '0') {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//转派
+export async function reassignmentSalesWorkOrder(data) {
+  const res = await request.put('/eom/afterSalesWorkOrder/reassignment', data);
+  if (res.data.code === '0') {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//报工
+export async function reportWorkingSalesWorkOrder(data) {
+  const res = await request.put('/eom/afterSalesWorkOrder/reportWorking', data);
+  if (res.data.code === '0') {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 19 - 7
src/api/wms/index.js

@@ -5,7 +5,7 @@ import request from '@/utils/request';
  */
 export async function getInventoryTotalAPI(data) {
   if (data.length == 0) {
-    return[]
+    return []
   }
   const res = await request.post(`wms/stocktwo/getInventoryTotal`, data);
   if (res.data.code == 0) {
@@ -91,7 +91,9 @@ export async function getInfoById(id) {
 // 批次明细
 
 export async function getBatchDetails(data) {
-  const res = await request.get(`/wms/outin/getBatchDetails`, { params: data });
+  const res = await request.get(`/wms/outin/getBatchDetails`, {
+    params: data
+  });
   if (res.data.code == 0) {
     return res.data.data;
   }
@@ -110,7 +112,9 @@ export async function getDetailById(data) {
  * 产品列表
  */
 export async function getProductList(params) {
-  const res = await request.get(`/main/category/getList`, { params });
+  const res = await request.get(`/main/category/getList`, {
+    params
+  });
   if (res.data.code == 0) {
     return res.data.data;
   }
@@ -154,8 +158,7 @@ export async function getWarehouseList() {
 //sourceBizNo
 export async function getInfoBySourceBizNoAPI(sourceBizNo) {
   const res = await request.get(
-    `/wms/outintwo/getInfoBySourceBizNo/${sourceBizNo}`,
-    {}
+    `/wms/outintwo/getInfoBySourceBizNo/${sourceBizNo}`, {}
   );
   if (res.data.code == 0) {
     return res.data.data;
@@ -166,8 +169,7 @@ export async function getInfoBySourceBizNoAPI(sourceBizNo) {
 //sourceBizNo
 export async function getInfoBySourceBizNoAll(sourceBizNo) {
   const res = await request.get(
-    `/wms/outintwo/getInfoBySourceBizNoAll/${sourceBizNo}`,
-    {}
+    `/wms/outintwo/getInfoBySourceBizNoAll/${sourceBizNo}`, {}
   );
   if (res.data.code == 0) {
     return res.data.data;
@@ -214,3 +216,13 @@ export async function getMaterialList(params) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+// 包装维度台账列表
+export async function getPackingList(params) {
+  const res = await request.get(`/wms/outInDetailRecordTwo/page`, {
+    params
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 428 - 0
src/views/salesServiceManagement/components/AssetsDialog.vue

@@ -0,0 +1,428 @@
+<template>
+  <ele-modal :visible.sync="visible1" :close-on-click-modal="false" width="80%" append-to-body @close="visible1 = false"
+    :maxable="true">
+    <el-form :model="searchForm" label-width="100px">
+      <el-row>
+        <el-col :span="6">
+          <el-form-item label="仓库名称:">
+            <el-select v-model="searchForm.warehouseId" style="width: 100%">
+              <el-option v-for="item in warehouseList" :key="item.id" :value="item.id" :label="item.name"></el-option>
+            </el-select></el-form-item>
+        </el-col>
+
+        <el-col :span="6" style="height: 43px">
+          <el-form-item label="列表维度:" prop="dimension">
+            <template>
+              <el-select style="width: 100%" @change="changeDimensionHandler" v-model="dimension" placeholder="请选择">
+                <el-option label="物料维度" value="4"> </el-option>
+                <el-option label="包装维度" value="3"> </el-option>
+                <el-option label="批次维度" value="2"> </el-option>
+                <el-option label="物品维度" value="1"> </el-option>
+              </el-select>
+            </template>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="物品编码:">
+            <el-input style="width: 100%" type="text" placeholder="搜索物品编码" v-model="searchForm.categoryCode"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="物品名称:">
+            <el-input style="width: 100%" type="text" placeholder="搜索物品名称" v-model="searchForm.categoryName"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="批次号:">
+            <el-input type="text" placeholder="搜索批次号" v-model="searchForm.batchNo"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="刻码">
+            <el-input type="text" placeholder="搜索物品刻码" v-model="searchForm.engrave"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="发货码">
+            <el-input type="text" placeholder="搜索发货码" v-model="searchForm.barcodes"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="6">
+          <el-form-item label="牌号">
+            <el-input type="text" placeholder="搜索牌号" v-model="searchForm.brandNum"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col style="text-align: right">
+          <el-button type="primary" @click="doSearch">搜索</el-button>
+          <el-button icon="el-icon-refresh-left" @click="reset">重置</el-button>
+        </el-col>
+      </el-row>
+    </el-form>
+    <el-container class="assets-dialog">
+      <el-aside width="200px" class="wrapper-assets">
+        <AssetTree ref="treeList" :treeIds="[6]" @handleNodeClick="handleNodeClick" />
+      </el-aside>
+      <el-main>
+        <el-table ref="multipleTable" :data="tableData" tooltip-effect="dark" height="25vh" border row-key="id"
+          style="width: 100%" :header-cell-style="{ background: '#F0F3F3', border: 'none' }"
+          @selection-change="handleSelectionChange">
+          <el-table-column type="selection" :reserve-selection="true" width="55" align="center">
+          </el-table-column>
+          <el-table-column label="序号" type="index" width="50">
+          </el-table-column>
+          <el-table-column prop="categoryCode" label="物品编码" min-width="120"
+            :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column prop="categoryName" width="200" label="物品名称" :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column prop="brandNum" label="牌号" :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column prop="categoryModel" label="型号" :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column prop="specification" label="规格" :show-overflow-tooltip="true"></el-table-column>
+          <!-- <el-table-column
+       
+            label="出库数量"
+            type="index"
+            width="100"
+          >
+            <template slot-scope="{ row }">
+              <el-input
+                type="text"
+                placeholder="请输入"
+                v-model="row.outboundNum"
+                @input="handleInput(row, $event)"
+              ></el-input>
+            </template>
+          </el-table-column> -->
+          <el-table-column v-if="dimension == 3" prop="batchNo" label="批次号" key="batchNo"
+            min-width="80"></el-table-column>
+          <el-table-column prop="level" label="级别"></el-table-column>
+          <el-table-column prop="measureQuantity" label="计量数量" width="120"></el-table-column>
+          <el-table-column prop="measureUnit" label="计量单位" width="90"></el-table-column>
+          <el-table-column prop="weight" label="重量" min-width="120"></el-table-column>
+          <el-table-column prop="weightUnit" label="重量单位" min-width="120"></el-table-column>
+          <el-table-column v-if="dimension == 3" prop="packageNo" label="包装编码" min-width="120"
+            :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column v-if="dimension == 3" prop="packingQuantity" label="包装数量" min-width="120"
+            :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column v-if="dimension == 3" prop="packingUnit" label="包装单位" min-width="120"
+            :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column v-if="dimension == 3 || dimension == 4" label="发货条码" prop="barcodes" width="80"
+            :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column v-if="dimension == 4" prop="no" label="物料编码" min-width="120"
+            :show-overflow-tooltip="true"></el-table-column>
+          <el-table-column v-if="dimension == 3 || dimension == 4" prop="materielDesignation" label="物料代号"
+            min-width="100"></el-table-column>
+          <el-table-column v-if="dimension == 3 || dimension == 4" prop="clientCode" label="客户代号"
+            min-width="100"></el-table-column>
+          <el-table-column v-if="dimension == 3 || dimension == 4" prop="engrave" label="刻码"
+            min-width="120"></el-table-column>
+          <el-table-column v-if="dimension == 3 || dimension == 4" prop="warehouseName" label="仓库" min-width="200"
+            :show-overflow-tooltip="true"></el-table-column>
+        </el-table>
+        <ele-pro-table ref="table" :columns="columns" :datasource="tableList" :selection.sync="selection" row-key="id"
+          height="20vh" :needPage="false">
+          <template v-slot:toolbar>
+            <el-button type="primary" size="small" @click="add">添加</el-button>
+            <el-button size="small" @click="del">移除</el-button></template>
+        </ele-pro-table>
+        <div style="text-align: right; padding: 10px">
+          <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
+            :page-sizes="[5, 10, 20, 50]" :page-size.sync="searchForm.size" :current-page.sync="searchForm.pageNum"
+            @current-change="handleCurrentChange" @size-change="handleSizeChange">
+          </el-pagination>
+        </div>
+      </el-main>
+    </el-container>
+
+    <div slot="footer">
+      <el-button type="primary" @click="confirm">确定</el-button>
+      <el-button @click="cancel">关闭</el-button>
+    </div>
+  </ele-modal>
+</template>
+
+<script>
+import {
+  getOutindetailtwoList,
+  getBatchList,
+  getMaterialList,
+  getPackingList
+} from '@/api/wms';
+import AssetTree from './assetTree.vue';
+
+import { getWarehouseList } from '@/api/saleManage/saleorder';
+// import warehouseDefinition from '@/api/warehouseManagement/warehouseDefinition';
+
+export default {
+  components: { AssetTree },
+  props: {
+    treeIds: { type: Array, default: () => [] }
+  },
+  data() {
+    return {
+      fullscreen: false,
+      isShowTable: true,
+      qualityResultOption: [
+        {
+          value: 0,
+          label: '合格'
+        },
+        {
+          value: 1,
+          label: '不合格'
+        }
+      ],
+      qualityStatusOption: [
+        {
+          value: 1,
+          label: '已质检'
+        },
+        {
+          value: 0,
+          label: '未质检'
+        }
+      ],
+      visible1: false,
+      tableData: [],
+      total: 0,
+      categoryLevelId: '',
+      warehouseList: [],
+      searchForm: {
+        categoryCode: '',
+        categoryName: '',
+        batchNo: '',
+        brandNum: '',
+        engrave: '',
+        barcodes: '',
+        categoryLevelId: '',
+        warehouseId: '',
+        pageNum: 1,
+        size: 20
+      },
+      selectionList: [],
+      materialType: '',
+      warehouseList: [],
+      dimension: '1',
+      selection: [],
+      tableList: [],
+      columns: [
+        {
+          columnKey: 'selection',
+          type: 'selection',
+          width: 45,
+          align: 'center',
+          fixed: 'left'
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true,
+          fixed: 'left'
+        },
+
+        {
+          prop: 'categoryCode',
+          label: '物品编码',
+          align: 'center'
+        },
+        {
+          prop: 'categoryName',
+          label: '物品名称',
+          align: 'center'
+        },
+
+        {
+          prop: 'categoryModel',
+          label: '型号',
+          align: 'center'
+        },
+        {
+          prop: 'specification',
+          label: '规格',
+          align: 'center'
+        },
+        {
+          prop: 'level',
+          label: '级别',
+          align: 'center'
+        },
+        {
+          prop: 'measureQuantity',
+          label: '库存',
+          align: 'center'
+        },
+        {
+          prop: 'measureUnit',
+          label: '计量单位',
+          align: 'center'
+        },
+
+        {
+          prop: 'warehouseName',
+          label: '仓库',
+          align: 'center'
+        }
+      ]
+    };
+  },
+  created() {
+    getWarehouseList({}).then((res) => {
+      this.warehouseList = res;
+    });
+  },
+  methods: {
+    getRowKeys(row) {
+      return row.id;
+    },
+
+    // 切换维度
+    changeDimensionHandler(e) {
+      this.searchForm.pageNum = 1;
+      this.selectionList = [];
+      this.$refs.multipleTable.clearSelection();
+      this.changeDimension(e);
+    },
+    async changeDimension(e) {
+      this.dimension = e;
+      if (this.dimension == 1) {
+        // 物品维度
+        const data = await getOutindetailtwoList(this.searchForm);
+        this.tableData = data.list;
+        this.total = data.count;
+      } else if (this.dimension == 2) {
+        // 批次维度
+        const data = await getBatchList(this.searchForm);
+        this.tableData = data.list;
+        this.total = data.count;
+      } else if (this.dimension == 4) {
+        // 物料维度
+        const data = await getMaterialList(this.searchForm);
+        this.tableData = data.list;
+        this.total = data.count;
+      } else {
+        // 包装维度
+        const data = await getPackingList(this.searchForm);
+        this.tableData = data.list;
+        this.total = data.count;
+      }
+      console.log(this.tableData);
+    },
+    open(tableList) {
+      this.visible1 = true;
+      this.tableList = JSON.parse(JSON.stringify(tableList));
+      this.$nextTick(() => {
+        console.log(this.$refs, 'this.$refs.treeList');
+        this.$refs.treeList.getTreeData().then((data) => {
+          this.handleNodeClick(data);
+        });
+      });
+    },
+    async confirm() {
+      this.$emit('choose', this.tableList, this.dimension);
+      this.cancel();
+    },
+    async add() {
+      if (!this.selectionList.length) {
+        this.$message.error('请至少选择一条数据!');
+        return;
+      }
+      // if (this.dimension == 1) {
+      //   let boolen = this.selectionList.every((item) => item.outboundNum > 0);
+      //   if (!boolen) {
+      //     this.$message.error('请输入出库数量!');
+      //     return;
+      //   }
+      // }
+      // let data = null;
+      // if (this.dimension != 1) {
+      //   data = await storageApi.getHierarchyList({
+      //     ids: this.selectionList.map((item) => item.id).join(','),
+      //     type: this.dimension
+      //   });
+      // } else {
+      //   data = await storageApi.getHierarchyFifo({
+      //     type: this.dimension,
+      //     builders: this.selectionList.map((item) => {
+      //       return {
+      //         categoryId: item.categoryId,
+      //         num: item.outboundNum || item.measureQuantity
+      //       };
+      //     })
+      //   });
+      // }
+
+      this.selectionList = this.selectionList.filter((item) => {
+        if (item.warehouseList?.length > 0) {
+          item['warehouseId'] = item.warehouseList[0].warehouse_id;
+          item['warehouseName'] = item.warehouseList[0].warehouse_name;
+        }
+        return !this.tableList
+          .map((item) => item.categoryCode)
+          .includes(item.categoryCode);
+      });
+      this.tableList.push(...this.selectionList);
+    },
+    del() {
+      let ids = this.selection.map((item) => item.id);
+      this.tableList = this.tableList.filter(
+        (item) => !ids.includes(item.id)
+      );
+    },
+    cancel() {
+      this.selectionList = [];
+      this.$refs.multipleTable.clearSelection();
+      this.visible1 = false;
+    },
+    handleSelectionChange(val) {
+      this.selectionList = val;
+    },
+    handleNodeClick(data) {
+      console.log(data);
+      this.categoryLevelId = data.id;
+      this.searchForm.categoryLevelId = data.id;
+      this.doSearch();
+    },
+    handleCurrentChange() {
+      this.changeDimension(this.dimension);
+    },
+    reset() {
+      this.searchForm = {
+        categoryCode: '',
+        categoryName: '',
+        batchNo: '',
+        brandNum: '',
+        engrave: '',
+        barcodes: '',
+        categoryLevelId: this.categoryLevelId,
+        pageNum: 1,
+        warehouseId: '',
+        size: 20
+      };
+      this.doSearch();
+    },
+    doSearch() {
+      this.searchForm.pageNum = 1;
+      this.changeDimension(this.dimension);
+    },
+    handleSizeChange() {
+      this.searchForm.pageNum = 1;
+      this.changeDimension(this.dimension, 'page');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.el-dialog__wrapper {
+  ::v-deep .el-aside {
+    background-color: #fff !important;
+    border: 1px solid #ccc;
+  }
+}
+
+.wrapper-assets {
+  max-height: 53vh;
+  overflow: auto;
+}
+</style>

+ 315 - 0
src/views/salesServiceManagement/components/applyForSpare.vue

@@ -0,0 +1,315 @@
+<template>
+    <ele-modal :visible.sync="visible" :title="title" width="75%" append-to-body @close="handleCancel" :maxable="true">
+        <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="create-form">
+            <headerTitle title="基本信息" style="margin-top: 15px"></headerTitle>
+            <el-row :gutter="24">
+                <el-col v-bind="styleResponsive ? { lg: 8, md: 12 } : { span: 8 }">
+                    <el-form-item label="编码:" prop="code">
+                        <el-input v-model="form.code" disabled />
+                    </el-form-item>
+                </el-col>
+                <el-col v-bind="styleResponsive ? { lg: 8, md: 12 } : { span: 8 }">
+                    <el-form-item label="工单名称:" prop="name">
+                        <el-input v-model="form.name" disabled />
+                    </el-form-item>
+                </el-col>
+                <el-col v-bind="styleResponsive ? { lg: 8, md: 12 } : { span: 8 }">
+                    <el-form-item label="领用部门:" prop="receivingDeptName">
+                        <el-input v-model="form.receivingDeptName" disabled />
+                    </el-form-item>
+                </el-col>
+                <el-col v-bind="styleResponsive ? { lg: 8, md: 12 } : { span: 8 }">
+                    <el-form-item label="领用人:" prop="recipientName">
+                        <el-input v-model="form.recipientName" disabled />
+                    </el-form-item>
+                </el-col>
+                <el-col v-bind="styleResponsive ? { lg: 8, md: 12 } : { span: 8 }">
+                    <el-form-item label="使用时间:" prop="usageTime">
+                        <el-date-picker style="width: 100%" v-model="form.usageTime" type="date" placeholder="选择"
+                            value-format="yyyy-MM-dd">
+                        </el-date-picker>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="8">
+                    <el-form-item label="使用部门" prop="useDeptName">
+                        <el-input v-model="form.useDeptName" />
+                    </el-form-item>
+                </el-col>
+                <el-col :span="8">
+                    <el-form-item label="使用人" prop="userName">
+                        <el-input v-model="form.userName" />
+                    </el-form-item>
+                </el-col>
+
+                <el-col v-bind="styleResponsive ? { lg: 24, md: 24 } : { span: 16 }">
+                    <el-form-item label="用途:" prop="purpose">
+                        <el-input v-model="form.purpose" type="textarea" :rows="2" />
+                    </el-form-item>
+                </el-col>
+            </el-row>
+
+            <headerTitle title="备品备件信息" style="margin-top: 15px"></headerTitle>
+
+            <ele-pro-table ref="table" :columns="columns" :datasource="tableList" row-key="id" height="30vh"
+                :needPage="false">
+                <template v-slot:toolbar>
+                    <div class="add-product" @click="addEquipment" v-if="inlet != 1">
+                        <i class="el-icon-circle-plus-outline"></i>
+                    </div>
+                </template>
+
+                <template v-slot:totalCount="{ row, $index }">
+                    <el-input type="number" placeholder="请输入" v-model="row.totalCount" @input="handleInput(row, $index)"
+                        min="0" :disabled="inlet == 1" />
+                </template>
+                <template v-slot:singlePrice="{ row, $index }">
+                    <el-input type="number" placeholder="请输入" v-model="row.singlePrice"
+                        @input="handleInput(row, $index)" min="0" :disabled="inlet == 1" />
+                </template>
+                <template v-slot:action="{ row }">
+                    <el-popconfirm class="ele-action" title="确定要删除吗?" @confirm="del(row.id)">
+                        <template v-slot:reference>
+                            <el-link type="danger" :underline="false" icon="el-icon-delete">
+                                删除
+                            </el-link>
+                        </template>
+                    </el-popconfirm>
+                </template>
+            </ele-pro-table>
+        </el-form>
+
+        <template v-slot:footer v-if="this.inlet != 1">
+            <el-button @click="handleCancel">取消</el-button>
+            <el-button type="primary" @click="save" :loading="loading">
+                提交
+            </el-button>
+        </template>
+        <AssetsDialog ref="selectStockLedgerDialogRef" :assetType="6" @choose="confirmChoose">
+        </AssetsDialog>
+    </ele-modal>
+</template>
+
+<script>
+import { saveOrUpdateSalesWorkOrder, saveOrUpdateSalesPlan, getSalesWorkOrderById } from '@/api/salesServiceManagement/index'
+import AssetsDialog from './AssetsDialog.vue'
+
+export default {
+    components: {
+        AssetsDialog
+    },
+    props: {
+        inlet: {
+            type: Number,
+            default: null
+        }
+    },
+    data() {
+        return {
+            visible: false,
+            loading: false,
+            title: '备品备件申请单',
+            type: '',
+            executorList: [],
+            form: {
+                code: '',
+                name: '',
+                receivingDeptName: '',
+                recipientName: '',
+                usageTime: '',
+                useDeptName: '',
+                userName: '',
+                purpose: ''
+            },
+            rules: {
+            },
+            tableList: [],
+            columns: [{
+                columnKey: 'index',
+                label: '序号',
+                type: 'index',
+                width: 55,
+                align: 'center',
+                showOverflowTooltip: true,
+                fixed: 'left'
+            },
+
+            {
+                prop: 'categoryCode',
+                label: '物品编码',
+                align: 'center'
+            },
+            {
+                prop: 'categoryName',
+                label: '物品名称',
+                align: 'center'
+            },
+
+            {
+                prop: 'categoryModel',
+                label: '型号',
+                align: 'center'
+            },
+            {
+                prop: 'specification',
+                label: '规格',
+                align: 'center'
+            },
+            {
+                prop: 'level',
+                label: '级别',
+                align: 'center'
+            },
+            {
+                prop: 'totalCount',
+                label: '数量',
+                align: 'center',
+                width: 100,
+                slot: 'totalCount'
+            },
+            {
+                prop: 'singlePrice',
+                label: '单价',
+                align: 'center',
+                width: 100,
+                slot: 'singlePrice'
+            },
+            {
+                prop: 'totalPrice',
+                label: '总价',
+                align: 'center',
+                width: 100,
+            },
+            {
+                prop: 'measureQuantity',
+                label: '库存',
+                align: 'center',
+            },
+            {
+                prop: 'measureUnit',
+                label: '计量单位',
+                align: 'center'
+            },
+            {
+                prop: 'warehouseName',
+                label: '仓库',
+                align: 'center'
+            },
+            {
+                columnKey: 'action',
+                label: '操作',
+                width: 140,
+                align: 'center',
+                resizable: false,
+                fixed: 'right',
+                slot: 'action',
+                showOverflowTooltip: true,
+                show: this.inlet != 1
+            }],
+        };
+    },
+
+    computed: {
+        // 是否开启响应式布局
+        styleResponsive() {
+            return this.$store.state.theme.styleResponsive;
+        }
+    },
+    created() { },
+    methods: {
+        // 出库数量限制
+        handleInput(row, index) {
+            if (row.totalCount > row.measureQuantity) {
+                this.$set(this.tableList[index], 'totalCount', row.measureQuantity);
+            }
+            if (row.singlePrice == 0) {
+                this.$set(this.tableList[index], 'totalPrice', 0);
+            } else {
+                this.$set(this.tableList[index], 'totalPrice', Number(row.singlePrice) * Number(row.totalCount));
+            }
+            console.log(row, 'row,', index)
+        },
+
+        async open(row, type) {
+            console.log(type);
+            this.visible = true;
+            this.type = type;
+
+            this.getSalesWorkOrderDetail(row)
+        },
+        async getSalesWorkOrderDetail(row) {
+            console.log(row, '申请备品备件列表详情')
+            this.form = row || {};
+
+            // const detailsArray = row?.sparePartsApplyVO.flatMap(item =>
+            //     item.details ? item.details.map(({ id, ...rest }) => rest) : []
+            // );
+            this.tableList = row.details || [];
+
+        },
+        del(id) {
+            this.tableList = this.tableList.filter((item) => item.id != id);
+        },
+        handleCancel() {
+            this.tableList = [];
+            this.form = {};
+            this.visible = false;
+        },
+        addEquipment() {
+            console.log('152454564')
+            this.$refs.selectStockLedgerDialogRef.open(this.tableList);
+        },
+        /* 保存编辑 */
+        async save() {
+            this.loading = true;
+
+            let requestName = this.type == 'work' ? saveOrUpdateSalesWorkOrder : saveOrUpdateSalesPlan
+            const res = await requestName({
+                id: this.form.id,
+                details: this.tableList
+            })
+            console.log(res, 'res')
+            this.loading = false;
+            if (!res) return
+            this.$message({
+                type: 'success',
+                message: '提交成功'
+            });
+            this.$emit('reload');
+            this.handleCancel();
+            this.loading = false;
+        },
+        confirmChoose(data) {
+            console.log(data, '选中')
+            data.map((item) => {
+                item.singlePrice = item.unitPrice
+                delete item.id
+                return {
+                    ...item
+                }
+            })
+            this.tableList = JSON.parse(JSON.stringify(data));
+        },
+    }
+};
+</script>
+<style lang="scss" scoped>
+.basic-details-title {
+    margin: 10px 0;
+}
+
+.title {
+    font-size: 16px;
+    line-height: 45px;
+}
+
+.add-product {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    // justify-content: flex-end;
+    font-size: 30px;
+    color: #1890ff;
+    margin: 10px 0;
+    cursor: pointer;
+}
+</style>

+ 124 - 0
src/views/salesServiceManagement/components/assetTree.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="tree-wrapper">
+    <el-tree
+      :data="treeList"
+      :props="defaultProps"
+      v-loading="treeLoading"
+      :node-key="nodeKey"
+      ref="tree"
+      :highlight-current="true"
+      :expand-on-click-node="false"
+      @node-click="handleNodeClick"
+      :default-expanded-keys="current && current.id ? [current.id] : []"
+    >
+    </el-tree>
+  </div>
+</template>
+
+<script>
+  import { getTreeByIds } from '@/api/classifyManage';
+
+  export default {
+    props: {
+      defaultProps: {
+        type: Object,
+        default: function () {
+          return {
+            children: 'children',
+            value: 'id',
+            label: 'name'
+          };
+        }
+      },
+
+      // 初始请求treeList
+      init: {
+        type: Boolean,
+        default: true
+      },
+
+      treeIds: {
+        type: Array,
+        default: []
+      },
+      nodeKey: {
+        type: String,
+        default: 'id'
+      }
+    },
+    data() {
+      return {
+        treeList: [],
+        treeLoading: false,
+        current: {},
+        currentKey: ''
+      };
+    },
+    mounted() {
+      if (this.init) {
+        this.getTreeData();
+      }
+    },
+    methods: {
+      // 获取树结构数据
+      getTreeData() {
+        return new Promise(async (resolve) => {
+          try {
+            this.treeLoading = true;
+
+            const res = await getTreeByIds({ ids: this.treeIds.join(',') });
+            this.treeLoading = false;
+            if (res?.code === '0') {
+              this.treeList = res.data;
+              this.$emit('setRootId', res.data[0]);
+
+              this.$nextTick(() => {
+                // 默认高亮第一级树节点
+                if (this.treeList[0]) {
+                  this.setCurrentKey(this.treeList[0]);
+                  this.current = this.treeList[0];
+                  // 默认点击加载数据
+                  // const firstNode = document.querySelector('.el-tree-node');
+                  // firstNode.click();
+                  resolve(this.current);
+                }
+              });
+              return this.treeList;
+            }
+          } catch (error) {
+            console.log(error);
+          }
+          this.treeLoading = false;
+        });
+      },
+
+      handleNodeClick(data, node) {
+        this.$emit('handleNodeClick', data, node);
+      },
+      // 设置默认高亮行
+      setCurrentKey(id) {
+        this.currentKey = id;
+        this.$refs.tree.setCurrentKey(this.currentKey);
+      },
+
+      // 获取树的选中状态
+      getSelectList() {
+        const selectList = this.$refs.tree.getCurrentNode();
+        return selectList;
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .tree-wrapper {
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+
+    :deep(.el-tree) {
+      display: inline-block;
+      min-width: 100%;
+    }
+  }
+</style>

+ 86 - 0
src/views/salesServiceManagement/components/sparePartsList.vue

@@ -0,0 +1,86 @@
+<template>
+    <div class="ele-body">
+        <!-- <el-card shadow="never" v-loading="loading"> -->
+            <!-- 数据表格 -->
+            <el-table ref="tableRef" :data="detailList" tooltip-effect="dark" border :max-height="500">
+                <el-table-column type="index" label="序号" width="55" align="center"></el-table-column>
+                <el-table-column prop="code" label="编码" align="center"></el-table-column>
+                <el-table-column prop="receivingDeptName" label="领用部门" align="center"></el-table-column>
+                <el-table-column prop="recipientName" label="领用人" align="center"></el-table-column>
+                <el-table-column prop="usageTime" label="领用时间" align="center"></el-table-column>
+                <el-table-column prop="purpose" label="用途" align="center"></el-table-column>
+                <el-table-column prop="type" label="出库场景" align="center">
+                    <template slot-scope="scope">
+                        <span v-for="(item, index) in outputSceneState" :key="index">{{ item.code === scope.row.type
+                            ? item.label : '' }}</span>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="approvalStatus" label="状态" align="center">
+                    <template slot-scope="scope">
+                        <span v-for="(item, index) in approvalStatus" :key="index">
+                            {{ item.value === scope.row.approvalStatus ? item.label : '' }}</span>
+                    </template>
+                </el-table-column>
+                <el-table-column fixed="right" label="操作" width="100">
+                    <template slot-scope="scope">
+                        <el-button @click="handleDetail(scope.row)" type="text" size="small">查看</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+
+            <applyForSpare ref="edit" :inlet="1" />
+        <!-- </el-card> -->
+    </div>
+</template>
+
+<script>
+
+import applyForSpare from './applyForSpare.vue';
+export default {
+    props: {
+        detailList: {
+            type: Array,
+            default: () => []
+        },
+    },
+    components: {
+        applyForSpare
+    },
+    data() {
+        return {
+            loading: false,
+            tableList: [],
+            outputSceneState: [
+                { code: 1, label: '退供出库' },
+                { code: 2, label: '调拨出库' },
+                { code: 3, label: '销售出库' },
+                { code: 4, label: '领用出库' },
+                { code: 5, label: '报废出库' },
+                { code: 6, label: '外协出库' },
+                { code: 7, label: '委外出库' },
+                { code: 8, label: '受托退货出库' },
+                { code: 9, label: '仓库委外出库' },
+                { code: 10, label: '采购退货出库' },
+                { code: 11, label: '自选领用出库' },
+                { code: 12, label: '配料出库' }
+            ],
+            approvalStatus: [
+                { label: '审核中', value: 0 },
+                { label: '审核通过', value: 1 },
+                { label: '审核不通过', value: 2 }
+            ]
+        };
+    },
+    computed: {
+    },
+    watch: {
+    },
+    created() { },
+    methods: {
+        handleDetail(row) {
+            console.log(row, 'rrrrrrrrrrrrrrrrrrrrrrrrrrrr')
+            this.$refs.edit.open(row, 'work');
+        },
+    }
+};
+</script>

+ 508 - 0
src/views/salesServiceManagement/demandList/components/addDialog.vue

@@ -0,0 +1,508 @@
+<template>
+  <ele-modal custom-class="ele-dialog-form long-dialog-form" :centered="true" :visible.sync="addRepairNotesDialog"
+    :title="dialogTitle" :close-on-click-modal="false" width="85%" append-to-body :fullscreen="fullscreen"
+    @close="handleClose">
+    <template slot="title">
+      <modalTitle :title="title" @setFullscreen="fullscreen = !fullscreen"></modalTitle>
+    </template>
+    <div class="dialog_top">
+      <div>
+        <el-button v-if="title === '新增'" type="primary" @click="selectCustomer">选择客户</el-button>
+      </div>
+      <div>
+        <span style="margin-left: 50px;" class="name">客户名称:{{ this.contractInfo?.name }}</span>
+        <span>客户编码:{{ this.contractInfo?.code }}</span>
+        <span>客户代号:{{ this.contractInfo?.serialNo }}</span>
+        <span>业务员:{{ this.contractInfo?.salesmanName }}</span>
+        <span>客户级别:{{ getDictValue('供应商级别', this.contractInfo.level + '') }}</span>
+      </div>
+    </div>
+    <div class="main_container">
+      <div style="width: 24%;">
+        <ele-pro-table ref="tableRef" :columns="columns" :datasource="datasource" height="calc(100vh - 520px)"
+          full-height="calc(100vh - 116px)" tool-class="ele-toolbar-form" cache-key="eomContactPageTable"
+          :initLoad="false" @row-click="rowClick" :pageSize="20" :pageSizes="[20, 50, 100, 200]"></ele-pro-table>
+      </div>
+      <div style="width: 25%;">
+        <ele-pro-table ref="tableRef2" :columns="columnsPro" :datasource="productList" height="calc(100vh - 520px)"
+          full-height="calc(100vh - 116px)" tool-class="ele-toolbar-form" cache-key="eomContactPageTable"
+          :initLoad="false" @selection-change="handleSelectionChange"></ele-pro-table>
+      </div>
+      <div style="width: 49%;">
+        <el-form ref="productDetailListRef" :model="{ productDetailList }" :rules="rules" :show-message="false">
+          <ele-pro-table ref="tableRef3" :columns="columnsDetail" :datasource="productDetailList"
+            height="calc(100vh - 520px)" full-height="calc(100vh - 116px)" tool-class="ele-toolbar-form"
+            cache-key="eomContactPageTable" :initLoad="false">
+            <template v-slot:type="{ row, $index }">
+              <el-form-item :prop="`productDetailList.${$index}.type`" required>
+                <el-select v-model="row.type" placeholder="请选择" :disabled="dialogTitle == '需求详情'">
+                  <el-option v-for="item in typeOptions" :key="item.value" :label="item.label"
+                    :value="item.value"></el-option>
+                </el-select>
+              </el-form-item>
+            </template>
+            <template v-slot:typeDescribe="{ row, $index }">
+              <el-form-item :prop="`productDetailList.${$index}.typeDescribe`" required>
+                <el-input v-model="row.typeDescribe" type="textarea" :disabled="dialogTitle == '需求详情'"></el-input>
+              </el-form-item>
+            </template>
+            <template v-slot:attachmentsArray="{ row }">
+              <fileMain v-model="row.attachmentsArray" :type="dialogTitle == '需求详情' ? 'view' : ''"></fileMain>
+            </template>
+          </ele-pro-table>
+        </el-form>
+
+      </div>
+    </div>
+    <div slot="footer" class="footer" v-if="dialogTitle != '需求详情'">
+      <el-button type="primary" @click="submitAdd">提交</el-button>
+      <el-button @click="handleClose">取消</el-button>
+    </div>
+    <customerDialog ref="customerListDialog" v-if="customerListDialogFlag"
+      :customerListDialogFlag.sync="customerListDialogFlag" @changeParent="getCustomerData">
+    </customerDialog>
+  </ele-modal>
+</template>
+
+<script>
+
+import { mapGetters, mapActions } from 'vuex';
+
+import modalTitle from '@/BIZComponents/modalTitle.vue';
+import customerDialog from '@/views/financialManage/components/customerListDialog.vue';
+
+import { getSendTableList, getSendSaleOrderrecordDetailSplit } from "@/api/saleManage/saleordersendrecord";
+import { getInfoBySourceBizNoAll } from '@/api/wms/index'
+import { saveSalesDemand, updateSalesDemand, getSalesDemandById } from "@/api/salesServiceManagement/index";
+import { contactDetail } from "@/api/saleManage/contact";
+
+import { reviewStatusEnum } from "@/enum/dict";
+
+import fileMain from '@/components/addDoc/index.vue';
+import { getSendSaleOrderConfirmDetail } from '@/api/saleManage/invoiceConfirm';
+export default {
+  props: {
+    dialogTitle: {
+      type: String,
+      default: '新建售后需求'
+    },
+  },
+  components: {
+    modalTitle,
+    customerDialog,
+    fileMain
+  },
+  watch: {},
+  computed: {
+    ...mapGetters(['getDictValue'])
+  },
+  data() {
+    return {
+      fullscreen: false,
+      title: '新增',
+      addRepairNotesDialog: false,
+      addForm: {
+      },
+      initData: [],
+      addFormRules: {
+        remark: {
+          required: true,
+          message: '请输入故障描述',
+          trigger: 'blur'
+        }
+      },
+      loading: false,
+      row: null,
+
+      customerListDialogFlag: false,
+
+
+      contractInfo: {},
+
+      columns: [
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'docNo',
+          label: '发货单编码',
+          sortable: true,
+          align: 'center',
+          slot: 'docNo',
+          showOverflowTooltip: true,
+          minWidth: 120
+        },
+
+        {
+          prop: 'orderNo',
+          label: '销售订单编码',
+          sortable: true,
+          align: 'center',
+          slot: 'orderNo',
+          showOverflowTooltip: true,
+          minWidth: 120
+        },
+        {
+          prop: 'contactName',
+          label: '客户名称',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 120
+        },
+        {
+          prop: 'replied',
+          label: '是否回执',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 120,
+          formatter: (_row, _column, cellValue) => {
+            return _row.replied == 1 ? '是' : '否';
+          }
+        },
+        {
+          prop: 'reviewStatus',
+          label: '状态',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 120,
+          formatter: (_row, _column, cellValue) => {
+            return reviewStatusEnum[_row.reviewStatus].label;
+          }
+        },
+        {
+          prop: 'createTime',
+          label: '创建时间',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 120
+        }
+      ],
+      productList: [],
+      productDetailList: [],
+      columnsPro: [
+        {
+          width: 45,
+          type: 'selection',
+          columnKey: 'selection',
+          align: 'center'
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'categoryCode',
+          label: '编码',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'categoryName',
+          label: '名称',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'batchNo',
+          label: '批次号',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'barcodes',
+          label: '发货条码',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'measureQuantity',
+          label: '计量数量',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'materielDesignation',
+          label: '物料代号',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'engrave',
+          label: '刻码',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+      ],
+      columnsDetail: [
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'categoryCode',
+          label: '编码',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'categoryName',
+          label: '名称',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'batchNo',
+          label: '批次号',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'barcodes',
+          label: '发货条码',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'measureQuantity',
+          label: '计量数量',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'materielDesignation',
+          label: '物料代号',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'engrave',
+          label: '刻码',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'type',
+          slot: 'type',
+          label: '问题类型',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 120
+        },
+        {
+          prop: 'typeDescribe',
+          slot: 'typeDescribe',
+          label: '问题描述',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 300
+        },
+        {
+          prop: 'attachmentsArray',
+          slot: 'attachmentsArray',
+          label: '附件',
+          align: 'center',
+          width: 120
+        }
+      ],
+      typeOptions: [
+        { label: '维修', value: '1' },
+        { label: '保养', value: '2' },
+        { label: '安装', value: '3' },
+      ],
+      rules: {
+        'productDetailList.*.type': [{ required: true, message: '请选择问题类型', trigger: 'change' }],
+        'productDetailList.*.typeDescribe': [{ required: true, message: '请输入问题描述', trigger: 'blur' }]
+      },
+      rowData: {},
+      rowClickData: {}
+    };
+  },
+  created() {
+  },
+  methods: {
+    // 初始化数据(父组件调用)
+    init(row) {
+      this.addRepairNotesDialog = true;
+      this.row = row;
+      if (row) {
+        this.title = '修改';
+        this.getDetail(row.id);
+      } else {
+        this.contractInfo = {}
+        this.$nextTick(() => {
+          this.$refs.tableRef.setData([]);
+        })
+        this.productList = []
+        this.productDetailList = []
+
+        this.title = '新增';
+      }
+    },
+    async getDetail(id) {
+      const res = await getSalesDemandById(id)
+      if (!res) return;
+      this.rowData = res;
+      //根据客户id获取客户信息
+      await this.getContactDetail(res.contactId)
+
+      await this.reload({
+        page: 1,
+        where: {
+          contractId: res.contactId
+        }
+      })
+
+      if (res.productDetail) {
+        this.productDetailList = res?.productDetail;
+
+        if (res.orderCode) {
+          await this.getSaleOrderrecordDetail(res.orderCode);
+        }
+      }
+    },
+    async getContactDetail(id) {
+      const res = await contactDetail(id)
+      this.contractInfo = res?.base;
+    },
+    handleClose() {
+      this.initData = [];
+      this.addRepairNotesDialog = false;
+      this.addForm = {
+        leaderUserId: ''
+      };
+    },
+    async submitAdd() {
+      this.$refs.productDetailListRef.validate(async (valid) => {
+        if (valid) {
+          let pData = {
+            id: this.title === '新增' ? '' : this.rowData.id,
+            contactId: this.contractInfo.id,
+            contactCode: this.contractInfo.code,
+            contactName: this.contractInfo.name,
+            orderCode: this.rowClickData?.docNo,
+            orderId: this.rowClickData?.id,
+
+            productDetail: this.productDetailList
+          }
+          let requestname = this.title === '新增' ? saveSalesDemand : updateSalesDemand;
+          const res = await requestname(pData)
+          if (res) {
+            this.$message.success('操作成功!')
+            this.$emit('refresh')
+            this.handleClose()
+          }
+        } else {
+          this.$message.error('请填写所有必填项!');
+        }
+      });
+    },
+    // 打开选择客户
+    selectCustomer() {
+      this.customerListDialogFlag = true;
+      let params = {
+        id: '',
+        assetTreeId: '17',
+        classType: 2,
+        titleName: '选择客户'
+      };
+      this.$nextTick(() => {
+        this.$refs.customerListDialog.init(params);
+      })
+    },
+    getCustomerData(val) {
+      this.contractInfo = val;
+      this.reload();
+    },
+    datasource({ page, limit, where, order }) {
+      return getSendTableList({
+        pageNum: page,
+        size: limit,
+        ...where
+      });
+    },
+    reload(where) {
+      where = {
+        ...where,
+        contractId: this.contractInfo.id
+      }
+      this.$refs.tableRef.reload({ page: 1, where });
+    },
+    rowClick(row) {
+      this.rowClickData = row;
+      this.getSaleOrderrecordDetail(row.docNo)
+    },
+    async getSaleOrderrecordDetail(val) {
+      const dataArray = await getInfoBySourceBizNoAll(val);
+      let res = {};
+      if (dataArray && dataArray.length > 0) {
+        res = JSON.parse(JSON.stringify(dataArray[0]));
+        res['outInDetailList'] = []
+        dataArray.forEach(item => {
+          item.outInDetailList.forEach(val => {
+            val['bizNo'] = item.bizNo
+            res['outInDetailList'].push(val)
+          })
+        })
+        res['bizNo'] = dataArray.map((item) => item.bizNo);
+      }
+      this.productList = res.outInDetailList || [];
+    },
+    handleSelectionChange(val) {
+      this.productDetailList = val?.map(({ id, ...item }) => ({
+
+        ...item,
+        type: '',
+        typeDescribe: '',
+        attachmentsArray: [],
+        orderProductId: id,
+      }));
+    },
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.dialog_top {
+  margin-bottom: 10px;
+  display: flex;
+  align-items: center;
+
+  span {
+    margin-left: 50px;
+  }
+
+  .name {
+    font-weight: 800;
+    color: #40a9ff;
+  }
+}
+
+::v-deep .el-row {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.btns {
+  text-align: right;
+  // margin: 10px 0;
+}
+
+.main_container {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+}
+</style>

+ 114 - 0
src/views/salesServiceManagement/demandList/components/search.vue

@@ -0,0 +1,114 @@
+<!-- 搜索表单 -->
+<template>
+    <el-form
+      label-width="80px"
+      class="ele-form-search"
+      @keyup.enter.native="search"
+      @submit.native.prevent
+    >
+      <el-row :gutter="24">
+        <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 5 }">
+          <el-form-item label="报修编码:">
+            <el-input clearable v-model="where.code" placeholder="请输入" />
+          </el-form-item>
+        </el-col>
+        <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 5 }">
+          <el-form-item label="报修时间:">
+            <el-date-picker
+              style="width: 100%"
+              v-model="where.time"
+              type="daterange"
+              @change="timeChange"
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              value-format="yyyy-MM-dd HH:mm:ss"
+              :default-time="['00:00:00', '23:59:59']"
+            >
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col v-bind="styleResponsive ? { span: 12 } : { span: 4 }">
+          <div>
+            <el-button
+              type="primary"
+              icon="el-icon-search"
+              class="ele-btn-icon"
+              @click="search"
+              size="small"
+            >
+              查询
+            </el-button>
+            <el-button
+              @click="reset"
+              icon="el-icon-refresh-left"
+              size="small"
+              type="primary"
+              >重置</el-button
+            >
+          </div>
+        </el-col>
+      </el-row>
+    </el-form>
+  </template>
+  
+  <script>
+    export default {
+      components: {  },
+      data() {
+        // 默认表单数据
+        const defaultWhere = {
+          sourceCode: '',
+          sourceType: '',
+          code: '',
+          requestUserId: '',
+          status: '',
+          time: []
+        };
+        return {
+          // 表单数据
+          where: { ...defaultWhere }
+        };
+      },
+      computed: {
+        // 是否开启响应式布局
+        styleResponsive() {
+          return this.$store.state.theme.styleResponsive;
+        }
+      },
+      created() {},
+      methods: {
+        timeChange(value) {
+          if (!value) {
+            this.where.startTime = '';
+            this.where.endTime = '';
+          }
+        },
+        /* 搜索 */
+        search() {
+          const parmas = this.where;
+          if (parmas.time?.length) {
+            parmas.startTime = parmas.time[0];
+            parmas.endTime = parmas.time[1];
+            delete parmas.time;
+          }
+          if (parmas.status) {
+            parmas.statusList = [parmas.status].join(',');
+          } else {
+            parmas.statusList = '';
+          }
+          delete parmas.status;
+          this.$emit('search', parmas);
+        },
+        /*  重置 */
+        reset() {
+          this.where = { ...this.defaultWhere };
+          this.search();
+        }
+      }
+    };
+  </script>
+  <style lang="scss" scoped>
+    
+  </style>
+  

+ 293 - 0
src/views/salesServiceManagement/demandList/index.vue

@@ -0,0 +1,293 @@
+<template>
+  <div class="ele-body">
+    <el-card shadow="never" v-loading="loading">
+      <search @search="reload"> </search>
+      <!-- 数据表格 -->
+      <ele-pro-table ref="table" :columns="columns" :datasource="datasource" cache-key="systemRoleTable" :pageSize="20">
+        <!-- 表头工具栏 -->
+        <template v-slot:toolbar>
+          <el-button size="small" type="primary" icon="el-icon-plus" class="ele-btn-icon" @click="openEdit()">
+            新建
+          </el-button>
+        </template>
+        <template v-slot:code="{ row }"><el-link type="primary" :underline="false" @click="goDetail(row)">
+            {{ row.code }}
+          </el-link>
+        </template>
+        <!-- 操作列 -->
+        <template v-if="" v-slot:action="{ row }">
+          <el-link type="primary" :underline="false" @click="openEdit(row)">
+            修改
+          </el-link>
+          <el-popconfirm class="ele-action" title="确定要删除此售后需求吗?" @confirm="handleRemove(row)">
+            <template v-slot:reference>
+              <el-link type="danger" :underline="false" icon="el-icon-delete">
+                删除
+              </el-link>
+            </template>
+          </el-popconfirm>
+
+          <el-dropdown @command="(command) => handleCommand(command, row)" v-if="row.demandStatus == 0">
+            <span class="el-dropdown-link">
+              操作菜单<i class="el-icon-arrow-down el-icon--right"></i>
+            </span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="handleRevoke">撤回
+              </el-dropdown-item>
+              <el-dropdown-item command="handleAudit">提交
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </ele-pro-table>
+    </el-card>
+
+    <!-- 新建或编辑弹窗 -->
+    <AddDialog ref="addDialogRef" :dialogTitle="addDialogTitle" @refresh="reload" />
+    <!-- 审核弹窗 -->
+    <ele-modal :centered="true" :visible.sync="auditDialog" title="审核" :close-on-click-modal="false" width="25%"
+      append-to-body @close="handleClose">
+      <el-form ref="form" :model="formData">
+        <el-form-item prop="approvalResult" label="审核结果" :rules="{
+          required: true,
+          message: '请选择',
+          trigger: 'change'
+        }">
+          <el-select v-model="formData.approvalResult" placeholder="请选择" style="width: 100%" clearable>
+            <el-option v-for="item in approvalResultOptions" :key="item.value" :label="item.label"
+              :value="item.value"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item prop="rejectCause" label="审核意见" :rules="{
+          required: formData.approvalResult == 0,
+          message: '请输入',
+          trigger: 'change'
+        }">
+          <el-input v-model="formData.rejectCause" placeholder="请输入" type="textarea"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="footer">
+        <el-button type="primary" @click="handleSubmit">确认</el-button>
+        <el-button @click="handleClose">取消</el-button>
+      </div>
+    </ele-modal>
+  </div>
+</template>
+
+<script>
+import { getPageSalesDemand, deleteSalesDemand, auditSalesDemand, revokeSalesDemand } from '@/api/salesServiceManagement/index.js';
+import search from './components/search.vue';
+import AddDialog from './components/addDialog.vue';
+
+import dictMixins from '@/mixins/dictMixins';
+export default {
+  mixins: [dictMixins],
+  components: {
+    search,
+    AddDialog,
+  },
+  data() {
+    return {
+      // 表格列配置
+      columns: [
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true,
+          fixed: 'left'
+        },
+        {
+          slot: 'code',
+          prop: 'code',
+          label: '编码',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 110
+        },
+        {
+          prop: 'contactCode',
+          label: '客户编码',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'contactName',
+          label: '客户名称',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'createUserName',
+          label: '创建人',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'createTime',
+          label: '创建时间',
+          align: 'center',
+          showOverflowTooltip: true,
+          formatter: (_row, _column, cellValue) => {
+            return this.$util.toDateString(cellValue);
+          }
+        },
+        {
+          prop: 'approvalUserName',
+          label: '审核人',
+          align: 'center',
+          showOverflowTooltip: true,
+        },
+        {
+          prop: 'approvalTime',
+          label: '审核时间',
+          align: 'center',
+          showOverflowTooltip: true,
+          formatter: (_row, _column, cellValue) => {
+            return this.$util.toDateString(cellValue);
+          }
+        },
+        {
+          prop: 'demandStatus',
+          label: '状态',
+          align: 'center',
+          showOverflowTooltip: true,
+          formatter: (_row, _column, cellValue) => {
+            return cellValue == 0 ? '待审核' : cellValue == 2 ? '已拒绝' : cellValue == 1 ? '已通过' : cellValue == 3 ? '已撤回' : '';
+          }
+        },
+        {
+          columnKey: 'action',
+          slot: 'action',
+          label: '操作',
+          align: 'center',
+          width: 230,
+          resizable: false,
+          showOverflowTooltip: true
+        }
+      ],
+      // 加载状态
+      loading: false,
+      infoData: {},
+      repairInfoLogs: [],
+      addDialogTitle: '新建售后需求',
+      auditDialog: false,
+
+      rowData: {},
+      formData: {
+        approvalResult: null,
+        rejectCause: '',
+        approvalUserId: '',
+        approvalUserName: '',
+      },
+      approvalResultOptions: [
+        { label: '驳回', value: 0 },
+        { label: '同意', value: 1 },
+      ],
+
+    };
+  },
+  computed: {},
+  created() {
+    this.requestDict('报修来源');
+    this.requestDict('报修状态');
+  },
+  methods: {
+    /* 表格数据源 */
+    datasource({ page, limit, where, order }) {
+      return getPageSalesDemand({ pageNum: page, size: limit, ...where });
+    },
+    async changeEnable(row) {
+      const res = await putRoles(row);
+      if (res.code == 0) {
+        this.$message({
+          type: 'success',
+          message: '修改成功',
+          customClass: 'ele-message-border'
+        });
+        this.reload();
+      }
+    },
+    /* 刷新表格 */
+    reload(where) {
+      this.$refs.table.reload({ page: 1, where });
+    },
+
+    openEdit(row) {
+      if (row) {
+        this.addDialogTitle = '编辑售后需求';
+      } else {
+        this.addDialogTitle = '新建售后需求';
+      }
+      this.$refs.addDialogRef.init(row,);
+    },
+
+    goDetail(row) {
+      this.addDialogTitle = '需求详情';
+      this.$refs.addDialogRef.init(row);
+    },
+    handleRemove(row) {
+      deleteSalesDemand([row.id]).then((res) => {
+        this.$message.success('删除成功!');
+        this.reload();
+      });
+    },
+    async handleAudit(row) {
+      this.auditDialog = true;
+      this.rowData = row;
+    },
+    handleClose() {
+      this.auditDialog = false;
+    },
+    async handleSubmit() {
+      this.$refs.form.validate(async (valid) => {
+
+        if (valid) {
+          const data = this.$store.state.user.info;
+
+          let pdata = {
+            id: this.rowData.id,
+            approvalResult: this.formData.approvalResult,
+            rejectCause: this.formData.rejectCause,
+            approvalUserId: data.userId,
+            approvalUserName: data.name,
+          }
+          const res = await auditSalesDemand(pdata);
+          if (res) {
+            this.$message.success('审核成功!');
+            this.auditDialog = false;
+            this.reload();
+          }
+        }
+      });
+    },
+    async handleRevoke(row) {
+      const res = await revokeSalesDemand({ id: row.id });
+      if (!res) return
+      this.$message.success('撤回成功!');
+      this.reload();
+    },
+    handleCommand(command, row) {
+      if (command === 'handleRevoke') {
+        this.handleRevoke(row);
+      }
+      if (command === 'handleAudit') {
+        this.handleAudit(row);
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.el-dropdown-link {
+  cursor: pointer;
+  color: var(--color-primary-5);
+}
+
+.el-icon-arrow-down {
+  font-size: 12px;
+}
+</style>

+ 75 - 0
src/views/salesServiceManagement/index.vue

@@ -0,0 +1,75 @@
+<template>
+  <div class="repair">
+    <div class="ele-body">
+      <el-card shadow="never">
+        <div class="switch">
+          <div class="switch_left">
+            <ul>
+              <li v-for="item in tabOptions" :key="item.key" :class="{ active: activeComp == item.key }"
+                @click="activeComp = item.key">
+                {{ item.name }}
+              </li>
+            </ul>
+          </div>
+          <!-- <div class="right" style="padding: 10px">
+            <el-button @click="$router.go(-1)">返回</el-button>
+          </div> -->
+        </div>
+        <div class="main">
+          <div v-if="activeComp == 'demandList'">
+            <demandList ref="demandListRef" />
+          </div>
+          <div v-if="activeComp == 'toDoList'">
+            <toDoList ref="toDoListRef" />
+          </div>
+          <div v-if="activeComp == 'workOrder'">
+            <workOrder ref="workOrderRef" />
+          </div>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+<script>
+import demandList from './demandList';
+import toDoList from './toDoList';
+import workOrder from './workOrder';
+export default {
+  components: { demandList, toDoList, workOrder },
+  data() {
+    return {
+      activeComp: 'demandList',
+      tabOptions: [
+        { key: 'demandList', name: '售后需求' },
+        { key: 'toDoList', name: '售后计划' },
+        { key: 'workOrder', name: '售后工单' },
+        { key: '1', name: '客户评价' },
+        { key: '2', name: '统计报表' }
+      ]
+    };
+  },
+  mounted() {
+    // switch (this.$route.query.title) {
+    //   case '工单':
+    //     this.activeComp = 'workOrder';
+    //     break;
+    //   default:
+    //     break;
+    // }
+  }
+};
+</script>
+<style lang="scss" scoped>
+::v-deep .el-card__body {
+  padding-top: 0;
+  padding-left: 0;
+}
+
+.main {
+  padding-left: 17px;
+
+  .plan {
+    padding-top: 15px;
+  }
+}
+</style>

+ 453 - 0
src/views/salesServiceManagement/toDoList/components/addOrUpdateDialog.vue

@@ -0,0 +1,453 @@
+<template>
+    <ele-modal width="75%" :append-to-body="true" custom-class="ele-dialog-form" :title="btnName"
+        :visible.sync="visible" :maxable="true" @close="handleClose">
+        <header-title title="需求信息"></header-title>
+
+        <div class="marginBto">
+            <div>
+                <span class="name">发货单编码:{{ orderCode }}</span>
+                <span>客户名称:{{ this.contractInfo?.name }}</span>
+                <span>客户编码:{{ this.contractInfo?.code }}</span>
+                <span>客户代号:{{ this.contractInfo?.serialNo }}</span>
+                <span>业务员:{{ this.contractInfo?.salesmanName }}</span>
+                <span>客户级别:{{ getDictValue('供应商级别', this.contractInfo.level + '') }}</span>
+            </div>
+        </div>
+        <ele-pro-table ref="tableRef" :columns="columnsDetail" :datasource="productDetailList" height="200"
+            :needPage="false" class="marginBto">
+            <template v-slot:attachmentsArray="{ row }">
+                <fileMain v-model="row.attachmentsArray" :type="btnName == '售后计划详情' ? 'view' : ''"></fileMain>
+            </template>
+        </ele-pro-table>
+        <header-title title="基本信息"></header-title>
+        <el-form ref="addFormRef" :model="addForm" :rules="addFormRules" label-width="120px">
+            <el-row>
+                <el-col :span="8">
+                    <el-form-item label="编码" prop="code">
+                        <el-input v-model="addForm.code" size="small" placeholder="请输入" :disabled="btnName == '售后计划详情'"
+                            disabled></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="8">
+                    <el-form-item label="名称" prop="name">
+                        <el-input v-model="addForm.name" size="small" placeholder="请输入"
+                            :disabled="btnName == '售后计划详情'"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="8">
+                    <el-form-item label="自动派单" prop="isSyncBill">
+                        <el-select v-model="addForm.isSyncBill" size="small" style="width: 100%"
+                            :disabled="btnName == '售后计划详情'">
+                            <el-option :value="1" label="是"></el-option>
+                            <el-option :value="0" label="否"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row>
+
+                <el-col :span="8">
+                    <el-form-item label="预计售后时长" prop="duration">
+                        <div style="display: flex">
+                            <el-input type="number" v-model="addForm.duration" size="small" placeholder="请输入"
+                                @input="formDataDurationTime" :disabled="btnName == '售后计划详情'">
+                                <template #suffix>分钟</template>
+                            </el-input>
+                        </div>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="8">
+                    <el-form-item label="部门" prop="executeGroupId">
+                        <deptSelect v-model="addForm.executeGroupId" @changeGroup="searchDeptNodeClick" />
+                    </el-form-item>
+                </el-col>
+                <el-col :span="8">
+                    <el-form-item label="执行人" prop="executeUserId">
+                        <el-select v-model="addForm.executeUserId" size="small" style="width: 100%" filterable
+                            @change="changeUser" :disabled="btnName == '售后计划详情'">
+                            <el-option v-for="item in executorList" :key="item.id" :value="item.id"
+                                :label="item.name"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row>
+                <el-col :span="8">
+                    <el-form-item label="紧急程度" prop="urgent">
+                        <DictSelection dictName="紧急程度" clearable v-model="addForm.urgent"
+                            :disabled="btnName == '售后计划详情'">
+                        </DictSelection>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="16">
+                    <el-form-item label="备注" prop="remark">
+                        <el-input type="textarea" resize="none" v-model="addForm.remark" :rows="2" placeholder="请详细说明"
+                            size="small" :disabled="btnName == '售后计划详情'"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+        <header-title title="备品备件清单" v-if="this.btnName != '派单'"></header-title>
+        <spareParts ref="refs" :detailList="detailList" v-if="this.btnName != '派单'"></spareParts>
+
+        <template v-slot:footer v-if="btnName != '售后计划详情'">
+            <el-button @click="handleClose">取消</el-button>
+            <el-button type="primary" @click="save"> 保存</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script>
+import { getSalesPlanById, updateSalesPlan, dispatchOrders } from '@/api/salesServiceManagement/index';
+import { contactDetail } from "@/api/saleManage/contact";
+import { getUserPage } from '@/api/system/organization';
+
+import deptSelect from '@/components/CommomSelect/dept-select.vue';
+import fileMain from '@/components/addDoc/index.vue';
+import spareParts from '../../components/sparePartsList.vue';
+
+import { mapGetters } from 'vuex';
+export default {
+    components: {
+        deptSelect,
+        fileMain,
+        spareParts
+    },
+    props: {
+    },
+    data() {
+        return {
+            ruleIndex: 0, // 规则index
+
+            addForm: {
+                name: '',
+                isSyncBill: null,
+                duration: null,
+                executeGroupId: '',
+                executeGroupName: '',
+                executeUserId: '',
+                executeUserName: '',
+                urgent: null,
+                remark: '',
+            },
+            uerList: [], // 审核人列表
+            executorList: [], // 业务人员列表
+
+            columnsDetail: [
+                {
+                    columnKey: 'index',
+                    label: '序号',
+                    type: 'index',
+                    width: 55,
+                    align: 'center',
+                    showOverflowTooltip: true
+                },
+                {
+                    prop: 'categoryCode',
+                    label: '编码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'categoryName',
+                    label: '名称',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'batchNo',
+                    label: '批次号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'barcodes',
+                    label: '发货条码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'measureQuantity',
+                    label: '计量数量',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'materielDesignation',
+                    label: '物料代号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'engrave',
+                    label: '刻码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'type',
+                    label: '问题类型',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    width: 120,
+                    formatter: (_row, _column, cellValue) => {
+                        return cellValue == 1 ? '维修' : cellValue == 2 ? '保养' : cellValue == 3 ? '安装' : '';
+                    }
+                },
+                {
+                    prop: 'typeDescribe',
+                    slot: 'typeDescribe',
+                    label: '问题描述',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    width: 300
+                },
+                {
+                    prop: 'attachmentsArray',
+                    slot: 'attachmentsArray',
+                    label: '附件',
+                    align: 'center',
+                    width: 120
+                }
+            ],
+            // 表单验证规则
+            addFormRules: {
+
+            },
+            visible: false,
+            columns: [
+                {
+                    type: 'index',
+                    width: 55,
+                    align: 'center'
+                },
+                {
+                    label: '子项编号',
+                    prop: 'subCode',
+                    action: 'subCode'
+                },
+                {
+                    label: '物料名称',
+                    prop: 'categoryName',
+                    action: 'categoryName'
+                },
+
+                {
+                    label: '是否回收料',
+                    prop: 'isReworkBom',
+                    action: 'isReworkBom',
+                    slot: 'isReworkBom',
+                    width: 95
+                },
+
+                {
+                    label: '物料编码',
+                    prop: 'categoryCode'
+                },
+                {
+                    label: '牌号',
+                    prop: 'brandNum'
+                },
+                {
+                    label: '型号',
+                    prop: 'modelType'
+                },
+                {
+                    label: '数量',
+                    prop: 'count'
+                },
+                {
+                    label: '计量单位',
+                    prop: 'unit'
+                },
+
+                {
+                    label: '附件',
+                    slot: 'bomArtFiles',
+                    action: 'bomArtFiles',
+                    minWidth: 100
+                },
+
+                {
+                    label: '单位',
+                    prop: 'weightUnit'
+                },
+
+                {
+                    label: '备注',
+                    prop: 'remark'
+                }
+            ],
+
+            statusList: [
+                { label: '草稿', value: -1 },
+                { label: '失效', value: 0 },
+                { label: '生效', value: 1 }
+            ],
+
+            // 提交状态
+            loading: false,
+
+            categoryId: null,
+
+            current: null,
+
+            materialShow: false,
+
+            tabsList: [],
+            tableData: [],
+
+            taskId: null,
+
+            addDialog: false,
+            tabsValue: null,
+            btnName: '',
+
+            contractInfo: {},
+            productDetailList: [],
+            orderCode: '',
+
+            detailList: [],
+            outputSceneState: [
+                { code: 1, label: '退供出库' },
+                { code: 2, label: '调拨出库' },
+                { code: 3, label: '销售出库' },
+                { code: 4, label: '领用出库' },
+                { code: 5, label: '报废出库' },
+                { code: 6, label: '外协出库' },
+                { code: 7, label: '委外出库' },
+                { code: 8, label: '受托退货出库' },
+                { code: 9, label: '仓库委外出库' },
+                { code: 10, label: '采购退货出库' },
+                { code: 11, label: '自选领用出库' },
+                { code: 12, label: '配料出库' }
+            ],
+            approvalStatus: [
+                { label: '审核中', value: 0 },
+                { label: '审核通过', value: 1 },
+                { label: '审核不通过', value: 2 }
+            ]
+        };
+    },
+    computed: {
+        ...mapGetters(['getDictValue']),
+        // 是否开启响应式布局
+        styleResponsive() {
+            return this.$store.state.theme.styleResponsive;
+        }
+    },
+    watch: {
+
+    },
+    methods: {
+        handleClose() {
+            this.addForm = {}
+            this.visible = false;
+        },
+        // 初始化
+        async init(row, tips) {
+            console.log(tips, 'tips详情');
+            this.btnName = tips;
+            if (row) {
+                this.visible = true;
+                this.getInfo(row.id);
+                if (row.executeGroupId) {
+                    this.getUserList({ groupId: row.executeGroupId })
+                }
+            } else {
+                this.addForm = {};
+            }
+        },
+        autoOrderChange(val) {
+            if (val == 0) {
+                this.addForm.executeId = '';
+                this.addForm.groupId = '';
+            }
+        },
+        formDataDurationTime(value) {
+            if (value > 0) {
+                this.addForm.duration = value.replace(/^[0]+/, '');
+            } else {
+                this.addForm.duration = 0;
+            }
+        },
+        //选择部门(搜索)
+        searchDeptNodeClick(info, data) {
+            if (info) {
+                // 根据部门获取人员
+                this.addForm.executeGroupName = data.name;
+                const params = { groupId: info };
+                this.getUserList(params);
+            } else {
+                this.addForm.executeGroupId = null;
+            }
+        },
+        // 获取审核人列表、巡点检人员
+        async getUserList(params) {
+            try {
+                let data = { pageNum: 1, size: -1 };
+                // 如果传了参数就是获取巡点检人员数据
+                if (params) {
+                    data = Object.assign(data, params);
+                }
+                const res = await getUserPage(data);
+                if (params) {
+                    this.executorList = res.list;
+                } else {
+                    this.uerList = res.list;
+                }
+            } catch (error) { }
+        },
+        changeUser(val) {
+            this.executorList.map(item => {
+                if (item.id == val) {
+                    this.addForm.executeUserName = item.name;
+                }
+            })
+        },
+        save() {
+            console.log(this.addForm);
+            this.$refs.addFormRef.validate(async (valid) => {
+                if (valid) {
+                    let requestName = this.btnName == '派单' ? dispatchOrders : updateSalesPlan;
+                    const res = await requestName(this.addForm);
+                    if (!res) return;
+                    this.$message.success('保存成功')
+                    this.visible = false;
+                    this.$emit('reload');
+                }
+            });
+        },
+        async getInfo(id) {
+            const res = await getSalesPlanById(id)
+            if (!res) return;
+            this.addForm = res;
+            console.log(this.addForm, 'addForm')
+            await this.getContactDetail(res?.afterSalesDemandVO.contactId);
+            this.productDetailList = res?.afterSalesDemandVO.productDetail || [];
+            this.orderCode = res?.afterSalesDemandVO.orderCode;
+            this.detailList = res?.sparePartsApplyVO || [];
+        },
+        async getContactDetail(id) {
+            const res = await contactDetail(id)
+            this.contractInfo = res?.base;
+        },
+
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.marginBto {
+    margin-bottom: 10px;
+
+    span {
+        margin-left: 50px;
+    }
+
+    .name {
+        font-weight: 800;
+        color: #40a9ff;
+    }
+}
+</style>

+ 258 - 0
src/views/salesServiceManagement/toDoList/components/generateForm.vue

@@ -0,0 +1,258 @@
+<template>
+    <ele-modal custom-class="ele-dialog-form long-dialog-form" :visible.sync="visibleDialog" title="报价单"
+        :close-on-click-modal="false" width="55%" append-to-body @close="handleClose" :maxable="true">
+        <div id="printBill">
+            <div class="titleStyle">售后信息</div>
+            <div>
+                <el-descriptions title="" :column="2" border>
+                    <el-descriptions-item>
+                        <template slot="label"> 客户编码 </template>
+                        {{ form ? form.contactCode : '' }}
+                    </el-descriptions-item>
+                    <el-descriptions-item>
+                        <template slot="label"> 客户名称 </template>
+                        {{ form ? form.contactName : '' }}
+                    </el-descriptions-item>
+                    <el-descriptions-item>
+                        <template slot="label"> 发货单编号 </template>
+                        {{ form ? form.afterSalesDemandVO?.orderCode : '' }}
+                    </el-descriptions-item>
+                    <el-descriptions-item>
+                        <template slot="label"> 售后时间 </template>
+                        {{ form ? form.createTime : '' }}
+                    </el-descriptions-item>
+
+                </el-descriptions>
+            </div>
+            <ele-pro-table ref="tableRef" :columns="columnsDetail" :datasource="productDetailList" height="200"
+                class="marginBto" :toolbar="false" :needPage="false">
+
+            </ele-pro-table>
+            <div>
+                <el-descriptions title="人工费用清单" :column="2" border>
+                    <el-descriptions-item label="耗时">{{ form?.laborCosts?.timeConsuming || 0 }}分钟</el-descriptions-item>
+                    <el-descriptions-item label="单价">{{ form?.laborCosts?.unitPrice || 0 }}</el-descriptions-item>
+                    <el-descriptions-item label="总价">{{ form?.laborCosts?.totalPrice || 0 }}</el-descriptions-item>
+                </el-descriptions>
+            </div>
+            <div class="titleStyle">费用清单</div>
+            <div style="margin-top: 12px;">
+                <ele-pro-table ref="tableRef" :columns="columns" :datasource="tableList" cache-key="systemRoleTable"
+                    :pageSize="20" :initLoad="false" :toolbar="false" :needPage="false">
+                </ele-pro-table>
+            </div>
+        </div>
+        <template v-slot:footer>
+            <el-button type="primary" @click="downForm">下载报价单</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script>
+
+import { generateQuotationSheet, SalesPlanDownload } from '@/api/salesServiceManagement/index'
+
+import print from 'print-js';
+
+export default {
+    props: {
+    },
+    components: {
+    },
+    watch: {},
+    computed: {
+    },
+    data() {
+        return {
+            title: '',
+            visibleDialog: false,
+            form: {},
+            tableList: [],
+            productDetailList: [],
+            columnsDetail: [
+                {
+                    columnKey: 'index',
+                    label: '序号',
+                    type: 'index',
+                    width: 55,
+                    align: 'center',
+                    showOverflowTooltip: true
+                },
+                {
+                    prop: 'categoryCode',
+                    label: '编码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'categoryName',
+                    label: '名称',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'batchNo',
+                    label: '批次号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'barcodes',
+                    label: '发货条码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'measureQuantity',
+                    label: '计量数量',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'materielDesignation',
+                    label: '物料代号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'engrave',
+                    label: '刻码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'type',
+                    label: '问题类型',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    width: 120,
+                    formatter: (_row, _column, cellValue) => {
+                        return cellValue == 1 ? '维修' : cellValue == 2 ? '保养' : cellValue == 3 ? '安装' : '';
+                    }
+                },
+                {
+                    prop: 'typeDescribe',
+                    slot: 'typeDescribe',
+                    label: '问题描述',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    width: 300
+                },
+            ],
+            columns: [
+                {
+                    columnKey: 'index',
+                    label: '序号',
+                    type: 'index',
+                    width: 55,
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    fixed: 'left'
+                },
+                {
+                    prop: 'categoryCode',
+                    label: '物品编码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'categoryName',
+                    label: '物品名称',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'specification',
+                    label: '规格',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'categoryModel',
+                    label: '型号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'singlePrice',
+                    label: '单价',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'totalPrice',
+                    label: '总价',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'warehouseName',
+                    label: '仓库名称',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+            ],
+            rowData: {}
+        }
+    },
+    created() {
+    },
+    methods: {
+        open(row) {
+            this.addForm = row;
+            this.visibleDialog = true;
+            this.getDetail(row);
+        },
+
+        handleClose() {
+            this.visibleDialog = false;
+        },
+
+        async getDetail(row) {
+            const res = await generateQuotationSheet(row.id);
+            this.rowData = row;
+            console.log(res, 'fffff');
+            this.form = res;
+            this.tableList = res.details;
+            this.productDetailList = res?.afterSalesDemandVO.productDetail || [];
+        },
+        async downForm() {
+            printJS({
+                printable: 'printBill',
+                type: 'html',
+                targetStyles: ['*'], // 保留所有样式
+                style: '@page { size: auto; margin: 0mm; }' // 调整页面边距
+            });
+        }
+
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.titleStyle {
+    font-size: 16px;
+    font-weight: bold;
+    margin: 12px 0;
+}
+
+.repair_row {
+    margin-bottom: 15px;
+
+    .repair_column {
+        display: flex;
+
+        >span {
+            &:first-child {
+                font-weight: 700;
+                text-align: right;
+                width: 120px;
+            }
+        }
+    }
+}
+
+.el-descriptions--medium.is-bordered .el-descriptions-item__cell {
+    min-width: 120px !important;
+}
+</style>

+ 71 - 0
src/views/salesServiceManagement/toDoList/components/plan-search.vue

@@ -0,0 +1,71 @@
+<!-- 搜索表单 -->
+<template>
+  <el-form label-width="100px" class="ele-form-search" @keyup.enter.native="search" @submit.native.prevent>
+    <el-row :gutter="24">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="计划单号:">
+          <el-input clearable v-model="where.planCode" placeholder="请输入" />
+        </el-form-item>
+      </el-col>
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="计划名称:">
+          <el-input clearable v-model="where.planName" placeholder="请输入" />
+        </el-form-item>
+      </el-col>
+      <el-col class="ele-form-actions" v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-button type="primary" icon="el-icon-search" class="ele-btn-icon" @click="search" size="small">
+          查询
+        </el-button>
+        <el-button @click="reset" icon="el-icon-refresh-left" size="small" type="primary">重置</el-button>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script>
+export default {
+  components: {},
+  data() {
+    // 默认表单数据
+    const defaultWhere = {
+      planName: '',
+      planCode: '',
+    };
+    return {
+      // 表单数据
+      where: { ...defaultWhere },
+    };
+  },
+  computed: {
+    // 是否开启响应式布局
+    styleResponsive() {
+      return this.$store.state.theme.styleResponsive;
+    }
+  },
+  created() { },
+  methods: {
+    /* 搜索 */
+    search() {
+      this.$emit('search');
+    },
+    /*  重置 */
+    reset() {
+      this.where = { ...this.defaultWhere };
+      this.search();
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.ele-form-actions {
+  // display: flex;
+  // align-items: center;
+  // justify-content: flex-end;
+}
+
+::v-deep {
+  .el-range-editor--medium.el-input__inner {
+    width: 100%;
+  }
+}
+</style>

+ 256 - 0
src/views/salesServiceManagement/toDoList/index.vue

@@ -0,0 +1,256 @@
+<template>
+    <div class="ele-body">
+        <el-card shadow="never" v-loading="loading">
+            <plan-search @search="reload"> </plan-search>
+            <!-- 数据表格 -->
+            <ele-pro-table ref="table" :columns="columns" :datasource="datasource" cache-key="systemRoleTable"
+                :pageSize="20">
+
+                <!-- 表头工具栏 -->
+                <template v-slot:toolbar>
+                </template>
+                <template v-slot:code="{ row }"><el-link type="primary" :underline="false" @click="goDetail(row)">
+                        {{ row.code }}
+                    </el-link>
+                </template>
+                <!-- 操作列 -->
+                <template v-slot:action="{ row }">
+                    <el-link type="primary" :underline="false" @click="handleUpdate(row)">
+                        修改
+                    </el-link>
+                    <el-popconfirm class="ele-action" title="确认删除这条记录吗?" @confirm="cancel(row)">
+                        <template v-slot:reference>
+                            <el-link type="danger" :underline="false" icon="el-icon-delete">
+                                删除
+                            </el-link>
+                        </template>
+                    </el-popconfirm>
+                    <el-dropdown @command="(command) => handleCommand(command, row)">
+                        <span class="el-dropdown-link">
+                            操作菜单<i class="el-icon-arrow-down el-icon--right"></i>
+                        </span>
+                        <el-dropdown-menu slot="dropdown">
+                            <el-dropdown-item command="handleRevocation" v-if="row.planStatus == 1">撤回
+                            </el-dropdown-item>
+                            <el-dropdown-item command="handleDispatchOrders"
+                                v-if="row.planStatus == 0 || row.planStatus == 3">派单
+                            </el-dropdown-item>
+                            <el-dropdown-item command="handleGenerate">生成报价单
+                            </el-dropdown-item>
+                            <el-dropdown-item command="addSpareItems">申请备品备件
+                            </el-dropdown-item>
+                        </el-dropdown-menu>
+                    </el-dropdown>
+                </template>
+            </ele-pro-table>
+        </el-card>
+        <addOrUpdateDialog ref="addOrUpdateDialogRef" @reload="reload"></addOrUpdateDialog>
+        <applyForSpare ref="edit" @reload="reload" />
+        <generateForm ref="generateFormRef"></generateForm>
+    </div>
+</template>
+
+<script>
+
+import planSearch from './components/plan-search.vue';
+import addOrUpdateDialog from './components/addOrUpdateDialog.vue';
+import generateForm from './components/generateForm.vue';
+
+import applyForSpare from '../components/applyForSpare.vue';
+
+import { getSalesPlan, deleteSalesPlan, planRevocation } from '@/api/salesServiceManagement/index';
+
+import dictMixins from '@/mixins/dictMixins';
+
+export default {
+    mixins: [dictMixins],
+    components: {
+        planSearch,
+        addOrUpdateDialog,
+        generateForm,
+        applyForSpare
+    },
+    data() {
+        return {
+            // 表格列配置
+            columns: [
+                {
+                    columnKey: 'index',
+                    label: '序号',
+                    type: 'index',
+                    width: 55,
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    fixed: 'left'
+                },
+                {
+                    slot: 'code',
+                    prop: 'code',
+                    label: '计划单号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'name',
+                    label: '计划名称',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+
+                    prop: 'isSyncBill',
+                    label: '是否自动派单',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    formatter: (item) => {
+                        return {
+                            0: '否',
+                            1: '是'
+                        }[item.isSyncBill];
+                    }
+                },
+                {
+                    prop: 'duration',
+                    label: '预计售后时长(分钟)',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'planStatus',
+                    label: '状态',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    formatter: (item) => {
+                        return {
+                            0: '待派单',
+                            1: '已派单',
+                            2: '已完成',
+                            3: '已撤回',
+                            4: '执行中',
+                        }[item.planStatus];
+                    }
+                },
+                {
+                    prop: 'urgent',
+                    label: '紧急程度',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    formatter: (item) => {
+                        return {
+                            1: '普通',
+                            2: '紧急',
+                            3: '重要',
+                        }[item.urgent];
+                    }
+
+                },
+                {
+                    prop: 'createUserName',
+                    label: '创建人',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'createTime',
+                    label: '创建时间',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    formatter: (_row, _column, cellValue) => {
+                        return this.$util.toDateString(cellValue);
+                    }
+                },
+                {
+                    columnKey: 'action',
+                    label: '操作',
+                    width: 230,
+                    align: 'center',
+                    resizable: false,
+                    slot: 'action',
+                    showOverflowTooltip: true,
+                    fixed: 'right'
+                }
+            ],
+            // 加载状态
+            loading: false,
+        };
+    },
+    computed: {},
+    created() {
+        this.requestDict('维修计划状态');
+    },
+    methods: {
+        /* 表格数据源 */
+        datasource({ page, limit, where, order }) {
+            return getSalesPlan({ pageNum: page, size: limit, ...where });
+        },
+        /* 刷新表格 */
+        reload(where) {
+            this.$refs.table.reload({ page: 1, where });
+        },
+        async cancel(row) {
+            const res = await deleteSalesPlan([row.id]);
+            if (res) {
+                this.$message.success('删除成功');
+                this.reload();
+            }
+
+        },
+        handleUpdate(row) {
+            this.$nextTick(() => {
+                this.$refs.addOrUpdateDialogRef.init(row, '修改售后计划');
+            })
+        },
+        goDetail(row) {
+            this.$nextTick(() => {
+                this.$refs.addOrUpdateDialogRef.init(row, '售后计划详情');
+            })
+        },
+        handleDispatchOrders(row) {
+            this.$nextTick(() => {
+                this.$refs.addOrUpdateDialogRef.init(row, '派单');
+            })
+        },
+        async handleRevocation(row) {
+            try {
+                await planRevocation([row.id]);
+                this.$message.success('撤回成功');
+                this.reload();
+            } catch (err) {
+                // this.$message.error(err)
+            }
+        },
+        addSpareItems(row) {
+            let data = JSON.parse(JSON.stringify(row));
+            this.$refs.edit.open(data, 'plan');
+        },
+        async handleGenerate(row) {
+            this.$refs.generateFormRef.open(row)
+        },
+        handleCommand(command, row) {
+            if (command === 'handleRevocation') {
+                this.handleRevocation(row);
+            }
+            if (command === 'handleDispatchOrders') {
+                this.handleDispatchOrders(row);
+            }
+            if (command === 'handleGenerate') {
+                this.handleGenerate(row);
+            }
+            if (command === 'addSpareItems') {
+                this.addSpareItems(row);
+            }
+        }
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.el-dropdown-link {
+    cursor: pointer;
+    color: var(--color-primary-5);
+}
+
+.el-icon-arrow-down {
+    font-size: 12px;
+}
+</style>

+ 120 - 0
src/views/salesServiceManagement/workOrder/components/declarationDialog.vue

@@ -0,0 +1,120 @@
+<template>
+    <ele-modal custom-class="ele-dialog-form long-dialog-form" :visible.sync="visibleDialog" title="报工"
+        :close-on-click-modal="false" width="55%" append-to-body @close="handleClose" :maxable="true">
+        <el-form ref="form" :model="addForm" label-width="100px">
+            <el-form-item label="实际起始时间" prop="time">
+                <el-date-picker v-model="addForm.time" type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss"
+                    range-separator="至" start-placeholder="实际开始日期" end-placeholder="实际结束日期">
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item label="附件:" prop="attachments">
+                <fileMain v-model="addForm.attachments"></fileMain>
+            </el-form-item>
+            <el-form-item label="备注:" prop="reason">
+                <el-input type="textarea" placeholder="请输入内容" v-model="addForm.reason">
+                </el-input>
+            </el-form-item>
+        </el-form>
+        <div slot="footer" class="footer">
+            <el-button type="primary" @click="submitAdd">提交</el-button>
+            <el-button @click="handleClose">取消</el-button>
+        </div>
+    </ele-modal>
+</template>
+
+<script>
+
+import fileMain from '@/components/addDoc/index.vue';
+
+import { reportWorkingSalesWorkOrder } from '@/api/salesServiceManagement/index'
+
+export default {
+    props: {
+    },
+    components: {
+        fileMain
+    },
+    watch: {},
+    computed: {
+    },
+    data() {
+        return {
+            title: '',
+            visibleDialog: false,
+            addForm: {
+                id: '',
+                time: [],
+                attachments: [],
+                reason: '',
+            },
+            row: {}
+        }
+    },
+    created() {
+    },
+    methods: {
+        open(row) {
+            this.row = row;
+            this.visibleDialog = true;
+
+        },
+        async submitAdd() {
+            this.$refs.form.validate(async (valid) => {
+                if (valid) {
+                    await reportWorkingSalesWorkOrder({
+                        acceptTime: this.addForm.time[0],
+                        finishTime: this.addForm.time[1],
+                        attachments: this.addForm.attachments,
+                        reason: this.addForm.reason,
+
+                    }).then((res) => {
+                        if (!res) return
+                        this.$message.success('操作成功')
+                        this.visibleDialog = false;
+                        this.$emit('reload');
+                    });
+                } else {
+                    return false;
+                }
+            });
+        },
+        handleClose() {
+            this.visibleDialog = false;
+        },
+
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.dialog_top {
+    margin-bottom: 10px;
+    display: flex;
+    align-items: center;
+
+    span {
+        margin-left: 50px;
+    }
+
+    .name {
+        font-weight: 800;
+        color: #40a9ff;
+    }
+}
+
+::v-deep .el-row {
+    display: flex;
+    flex-wrap: wrap;
+}
+
+.btns {
+    text-align: right;
+    // margin: 10px 0;
+}
+
+.main_container {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+}
+</style>

+ 250 - 0
src/views/salesServiceManagement/workOrder/components/detailDialog.vue

@@ -0,0 +1,250 @@
+<template>
+    <ele-modal custom-class="ele-dialog-form long-dialog-form" :visible.sync="visibleDialog" title="报工"
+        :close-on-click-modal="false" width="55%" append-to-body @close="handleClose" :maxable="true">
+        <el-tabs type="border-card" v-model="tabsActiveName">
+            <el-tab-pane label="工单信息" name="workInfo">
+                <el-row class="repair_row">
+                    <el-col :span="8" class="repair_column">
+                        <span>工单编号:</span>
+                        <span>{{ addForm.code || '' }}</span>
+                    </el-col>
+                    <el-col :span="8" class="repair_column">
+                        <span>执行部门:</span>
+                        <span>{{ addForm.executeGroupName || '' }}</span>
+                    </el-col>
+                    <el-col :span="8" class="repair_column">
+                        <span>执行人:</span>
+                        <span>{{ addForm.executeUserName || '' }}</span>
+                    </el-col>
+                </el-row>
+                <el-row class="repair_row">
+                    <el-col :span="8" class="repair_column">
+                        <span>计划单号:</span>
+                        <span>{{ addForm.planCode || '' }}</span>
+                    </el-col>
+                    <el-col :span="8" class="repair_column">
+                        <span>计划名称:</span>
+                        <span>{{ addForm.planName || '' }}</span>
+                    </el-col>
+                    <el-col :span="8" class="repair_column">
+                        <span>计划完成时间:</span>
+                        <span>{{ addForm.planFinishTime || '' }}</span>
+                    </el-col>
+                </el-row>
+            </el-tab-pane>
+            <el-tab-pane label="需求单信息" name="demandInfo">
+                <div class="marginBto">
+                    <div>
+                        <span class="name">发货单编码:{{ contractInfo.orderCode }}</span>
+                        <span>客户名称:{{ contractInfo?.name }}</span>
+                        <span>客户编码:{{ contractInfo?.code }}</span>
+                        <span>客户代号:{{ contractInfo?.serialNo }}</span>
+                        <span>业务员:{{ contractInfo?.salesmanName }}</span>
+                        <span>客户级别:{{ getDictValue('供应商级别', contractInfo.level + '') }}</span>
+                    </div>
+                </div>
+                <ele-pro-table ref="tableRef" :columns="columnsDetail" :datasource="productDetailList" height="200"
+                    :needPage="false" class="marginBto">
+                    <template v-slot:attachmentsArray="{ row }">
+                        <fileMain v-model="row.attachmentsArray" type='view'></fileMain>
+                    </template>
+                </ele-pro-table>
+            </el-tab-pane>
+            <el-tab-pane label="备品备件清单" name="sparePartsApply">
+                <spareParts ref="refs" :detailList="detailList"></spareParts>
+            </el-tab-pane>
+        </el-tabs>
+    </ele-modal>
+</template>
+
+<script>
+
+import fileMain from '@/components/addDoc/index.vue';
+
+import spareParts from '../../components/sparePartsList.vue';
+
+import { getSalesWorkOrderById } from '@/api/salesServiceManagement/index'
+
+import { contactDetail } from "@/api/saleManage/contact";
+
+import { mapGetters } from 'vuex';
+export default {
+    props: {
+    },
+    components: {
+        fileMain,
+        spareParts
+    },
+    watch: {},
+    computed: {
+        ...mapGetters(['getDictValue']),
+    },
+    data() {
+        return {
+            title: '',
+            visibleDialog: false,
+            tabsActiveName: 'workInfo',
+            addForm: {
+                time: [],
+                attachments: '',
+                reason: '',
+            },
+            detailList: [],
+            contractInfo: {},
+            productDetailList: [],
+            columnsDetail: [
+                {
+                    columnKey: 'index',
+                    label: '序号',
+                    type: 'index',
+                    width: 55,
+                    align: 'center',
+                    showOverflowTooltip: true
+                },
+                {
+                    prop: 'categoryCode',
+                    label: '编码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'categoryName',
+                    label: '名称',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'batchNo',
+                    label: '批次号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'barcodes',
+                    label: '发货条码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'measureQuantity',
+                    label: '计量数量',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'materielDesignation',
+                    label: '物料代号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'engrave',
+                    label: '刻码',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                },
+                {
+                    prop: 'type',
+                    label: '问题类型',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    width: 120,
+                    formatter: (_row, _column, cellValue) => {
+                        return cellValue == 1 ? '维修' : cellValue == 2 ? '保养' : cellValue == 3 ? '安装' : '';
+                    }
+                },
+                {
+                    prop: 'typeDescribe',
+                    slot: 'typeDescribe',
+                    label: '问题描述',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    width: 300
+                },
+                {
+                    prop: 'attachmentsArray',
+                    slot: 'attachmentsArray',
+                    label: '附件',
+                    align: 'center',
+                    width: 120
+                }
+            ],
+        }
+    },
+    created() {
+    },
+    methods: {
+
+        handleDetail(row) {
+            this.$refs.edit.open(this.addForm, 'work');
+        },
+        async getDetail(row) {
+            const res = await getSalesWorkOrderById(row.id)
+            console.log(res, '工单的申请备品备件清单')
+
+            res?.sparePartsApplyVO.map((item) => {
+                item.repairCode = res.code;
+                item.repairName = res.name;
+
+                return {
+                    ...item
+                }
+            })
+            console.log(res?.sparePartsApplyVO, '备品备件清单')
+            this.detailList = res.sparePartsApplyVO || [];
+            //需求单信息
+            await this.getContactDetail(res?.afterSalesDemandVO.contactId);
+            this.productDetailList = res?.afterSalesDemandVO.productDetail || [];
+        },
+        async getContactDetail(id) {
+            const res = await contactDetail(id)
+            this.contractInfo = res?.base;
+        },
+        open(row) {
+            this.addForm = row;
+            this.tabsActiveName = 'workInfo';
+            this.visibleDialog = true;
+            this.getDetail(row);
+        },
+
+        handleClose() {
+            this.tabsActiveName = '';
+            this.visibleDialog = false;
+        },
+
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.repair_row {
+    margin-bottom: 15px;
+
+    .repair_column {
+        display: flex;
+
+        >span {
+            &:first-child {
+                font-weight: 700;
+                text-align: right;
+                width: 120px;
+            }
+        }
+    }
+}
+</style>
+
+<style lang="scss" scoped>
+.marginBto {
+    margin-bottom: 10px;
+
+    span {
+        margin-left: 50px;
+    }
+
+    .name {
+        font-weight: 800;
+        color: #40a9ff;
+    }
+}
+</style>

+ 257 - 0
src/views/salesServiceManagement/workOrder/components/redeployOther2.vue

@@ -0,0 +1,257 @@
+<template>
+  <ele-modal custom-class="ele-dialog-form long-dialog-form" :visible.sync="visible" title="转派工单"
+    :close-on-click-modal="false" width="55%" append-to-body @close="cancel" :maxable="true">
+    <div class="sparepart-apply">
+      <div class="select-box">
+        <el-form label-width="80px">
+          <el-row :gutter="24">
+            <el-col :span="12">
+              <el-form-item label="工单单号">
+                <el-input v-model="row.code" disabled></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="部门" pro="groupId">
+                <deptSelect v-model="groupId" @changeGroup="deptClick" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="" label-width="10px">
+                <el-input v-model="name" placeholder="搜索姓名"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12" style="text-align: right">
+              <el-form-item label="" label-width="10px">
+                <el-button @click="search" type="primary" icon="el-icon-search">搜索</el-button>
+                <el-button @click="reset" icon="el-icon-refresh-left">重置</el-button>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </div>
+
+      <div class="data-tab">
+        <ele-pro-table ref="tableRef2" row-key="id" :columns="columns" :datasource="datasource"
+          @select-all="handleSelectAll" @selection-change="handleSelectionChange" cache-key="redeployOther"
+          :header-cell-class-name="headerCellClassName" height="300">
+        </ele-pro-table>
+      </div>
+      <div slot="footer" class="footer">
+        <el-button @click="cancel">返回</el-button>
+        <el-button :loading="btnLoading" type="primary" @click="confirm">确认</el-button>
+      </div>
+    </div>
+  </ele-modal>
+</template>
+
+<script>
+import deptSelect from '@/components/CommomSelect/dept-select.vue';
+
+import { reassignmentSalesWorkOrder } from '@/api/salesServiceManagement/index';
+
+import { getUserPage } from '@/api/system/organization';
+export default {
+  components: { deptSelect },
+  props: {
+
+  },
+  data() {
+    return {
+      fullscreen: false,
+      btnLoading: false,
+      visible: false,
+      treeList: [],
+      pages: {
+        pageNum: 1,
+        size: 15
+      },
+      total: 0,
+      selectedRow: {},
+      selectedRowData: [],
+      groupId: '',
+      executorDeptName: '',
+      name: '',
+      row: {},
+      columns: [
+        {
+          width: 45,
+          type: 'selection',
+          columnKey: 'selection',
+          align: 'center'
+          // selectable: (row, index) => {
+          //  return row.id !== this.row.executeUserId;
+          // }
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true,
+          fixed: 'left'
+        },
+        {
+          prop: 'jobNumber',
+          label: '工号',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'name',
+          label: '姓名',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'phone',
+          label: '电话',
+          align: 'center',
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'groupName',
+          label: '部门',
+          align: 'center',
+          showOverflowTooltip: true
+        }
+      ],
+      tableData: [],
+    };
+  },
+  created() {
+    this.getList();
+  },
+
+  methods: {
+    async getList() {
+      const data = await getUserPage({
+        pageNum: 1,
+        size: -1,
+        groupId: '',
+        name: '',
+        groupId: this.groupId
+      });
+      this.tableData = data.list;
+    },
+    open(row, val) {
+
+      this.row = row;
+      this.name = '';
+      this.groupId = '';
+      this.executorDeptName = '';
+      this.visible = true;
+      this.$nextTick(() => {
+        this.$refs.tableRef2.clearSelection();
+      })
+    },
+    //重置
+    reset() {
+      this.name = '';
+      this.search();
+    },
+    // 搜索
+    search() {
+      this.$refs.tableRef2.reload();
+    },
+
+    deptClick(data) {
+      if (data) {
+        this.$refs.tableRef2.reload();
+      }
+    },
+
+    // 获取审核人列表、巡点检人员
+    async datasource({ page, limit, where, order }) {
+      this.selectedRow = {};
+      // 如果传了参数就是获取巡点检人员数据
+      return getUserPage({
+        pageNum: page,
+        size: limit,
+        ...where,
+        groupId: this.groupId,
+        name: this.name
+      });
+    },
+    handleSelectAll(selection) {
+      this.$refs.tableRef2.clearSelection();
+    },
+    handleSelectionChange(row) {
+      if (row.length > 1) {
+        this.$refs.tableRef2.clearSelection();
+        this.$refs.tableRef2.toggleRowSelection(row.pop());
+      }
+      this.selectedRow = row[0];
+      console.log(this.selectedRow, 'selectedRowselectedRowselectedRow')
+    },
+    headerCellClassName({ columnIndex }) {
+      // 如果是第一列(通常是全选框),则返回自定义的class名
+      if (columnIndex === 0) {
+        return 'hide-selection-checkbox';
+      }
+      return '';
+    },
+    async confirm() {
+      try {
+        let pData = {
+          id: this.row.id,
+          executeGroupId: this.selectedRow.groupId,
+          executeGroupName: this.selectedRow.groupName,
+          executeUserId: this.selectedRow.id,
+          executeUserName: this.selectedRow.name,
+        }
+        const res = await reassignmentSalesWorkOrder(pData)
+        if (!res) return
+        this.$message.success('操作成功')
+        this.visible = false;
+        this.$emit('reload');
+      } catch (err) {
+        // this.$message.error(err)
+      }
+    },
+    cancel() {
+      this.selectedRow = {};
+      this.visible = false;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.sparepart-apply {
+  min-height: 40vh;
+
+  .select-box {
+    padding: 10px 0;
+  }
+
+  .material-item+.material-item {
+    margin-top: 10px;
+  }
+}
+
+.flex-between {
+  display: flex;
+  justify-content: space-between;
+  padding-right: 20%;
+}
+
+.list-box {
+  max-height: 60vh;
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.footer {
+  padding: 10px;
+  text-align: right;
+  background: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+::v-deep .hide-selection-checkbox .cell .el-checkbox {
+  visibility: hidden !important;
+}
+</style>

+ 71 - 0
src/views/salesServiceManagement/workOrder/components/work-search.vue

@@ -0,0 +1,71 @@
+<!-- 搜索表单 -->
+<template>
+  <el-form label-width="80px" class="ele-form-search" @keyup.enter.native="search" @submit.native.prevent>
+    <el-row :gutter="24">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="单号:">
+          <el-input clearable v-model="where.planCode" placeholder="请输入" />
+        </el-form-item>
+      </el-col>
+      <!-- <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="">
+
+        </el-form-item>
+      </el-col> -->
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-button type="primary" icon="el-icon-search" class="ele-btn-icon" @click="search" size="small">
+          查询
+        </el-button>
+        <el-button @click="reset" icon="el-icon-refresh-left" size="small" type="primary">重置</el-button>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script>
+export default {
+  components: {},
+  data() {
+    // 默认表单数据
+    const defaultWhere = {
+      planName: '',
+      planCode: '',
+    };
+    return {
+      // 表单数据
+      where: { ...defaultWhere },
+    };
+  },
+  computed: {
+    // 是否开启响应式布局
+    styleResponsive() {
+      return this.$store.state.theme.styleResponsive;
+    }
+  },
+  created() { },
+  methods: {
+    /* 搜索 */
+    search() {
+      this.$emit('search');
+    },
+    /*  重置 */
+    reset() {
+      this.where = { ...this.defaultWhere };
+      this.search();
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.ele-form-actions {
+  // display: flex;
+  // align-items: center;
+  // justify-content: flex-end;
+}
+
+::v-deep {
+  .el-range-editor--medium.el-input__inner {
+    width: 100%;
+  }
+}
+</style>

+ 277 - 0
src/views/salesServiceManagement/workOrder/index.vue

@@ -0,0 +1,277 @@
+<template>
+    <div class="ele-body">
+        <el-card shadow="never" v-loading="loading">
+            <work-search @search="reload"> </work-search>
+            <!-- 数据表格 -->
+            <ele-pro-table ref="table" :columns="columns" :datasource="datasource" cache-key="systemRoleTable"
+                :pageSize="20">
+                <!-- 表头工具栏 -->
+                <template v-slot:toolbar>
+                </template>
+                <template v-slot:code="{ row }">
+                    <el-link type="primary" :underline="false" @click="goDetail(row)">
+                        {{ row.code }}
+                    </el-link>
+                </template>
+                <!-- 操作列 -->
+                <template v-slot:action="{ row }">
+                    <el-link type="primary" :underline="false" @click="declarationForm(row)">
+                        报工
+                    </el-link>
+                    <el-popconfirm class="ele-action" title="确认删除这条记录吗?" @confirm="cancel(row)">
+                        <template v-slot:reference>
+                            <el-link type="danger" :underline="false" icon="el-icon-delete">
+                                删除
+                            </el-link>
+                        </template>
+                    </el-popconfirm>
+                    <el-dropdown @command="(command) => handleCommand(command, row)">
+                        <span class="el-dropdown-link">
+                            操作菜单<i class="el-icon-arrow-down el-icon--right"></i>
+                        </span>
+                        <el-dropdown-menu slot="dropdown">
+                            <el-dropdown-item command="handleReceive">接收
+                            </el-dropdown-item>
+                            <el-dropdown-item command="toRedeploy">转派
+                            </el-dropdown-item>
+                            <el-dropdown-item command="addSpareItems">申请备品备件
+                            </el-dropdown-item>
+                        </el-dropdown-menu>
+                    </el-dropdown>
+                </template>
+            </ele-pro-table>
+        </el-card>
+        <!-- 详情 -->
+        <detailDialog ref="detailDialogRef"></detailDialog>
+        <!-- 备品备件 -->
+        <applyForSpare @reload="reload" ref="edit" />
+        <!-- 报工 -->
+        <declarationDialog ref="declarationDialogRef" @reload="reload"></declarationDialog>
+        <!-- 转派 -->
+        <redeployOther ref="redeployOtherRef" @reload="reload" />
+    </div>
+</template>
+
+<script>
+import workSearch from './components/work-search.vue';
+
+import detailDialog from './components/detailDialog.vue'
+import applyForSpare from '../components/applyForSpare.vue';
+import declarationDialog from './components/declarationDialog.vue';
+import redeployOther from './components/redeployOther2.vue';
+
+
+import { getSalesWorkOrder, deleteSalesWorkOrder, receiveSalesWorkOrder } from '@/api/salesServiceManagement/index';
+
+import dictMixins from '@/mixins/dictMixins';
+export default {
+    mixins: [dictMixins],
+    components: {
+        workSearch,
+        detailDialog,
+        applyForSpare,
+        declarationDialog,
+        redeployOther
+    },
+    data() {
+        return {
+            workOrderStatus: [
+                { code: 0, label: '待接收' },
+                { code: 1, label: '执行中' },
+                { code: 2, label: '已完成' },
+                { code: 3, label: '已验收' },
+            ],
+            resultStatus: [
+                { code: 5, label: '未修复' },
+                { code: 4, label: '已修复' }
+            ],
+            // 表格列配置
+            columns: [
+                {
+                    columnKey: 'index',
+                    label: '序号',
+                    type: 'index',
+                    width: 55,
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    fixed: 'left'
+                },
+                {
+                    prop: 'code',
+                    slot: 'code',
+                    label: '工单编号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    minWidth: 150
+                },
+                {
+                    prop: 'planCode',
+                    label: '计划单号',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    minWidth: 110
+                },
+                {
+                    prop: 'planName',
+                    label: '计划名称',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    minWidth: 110
+                },
+                {
+                    prop: 'executeUserName',
+                    label: '执行人',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    minWidth: 110
+                },
+                // {
+                //     prop: 'assistsName',
+                //     label: '辅助人',
+                //     align: 'center',
+                //     showOverflowTooltip: true,
+                //     minWidth: 110
+                // },
+
+                {
+                    prop: 'acceptTime',
+                    label: '开始时间',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    minWidth: 110
+                },
+                {
+                    prop: 'finishTime',
+                    label: '结束时间',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    minWidth: 110
+                },
+                {
+                    prop: 'planFinishTime',
+                    label: '计划完成时间',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    minWidth: 110
+                },
+                {
+                    columnKey: 'inFactDuration',
+                    label: '实际售后时长(分钟)',
+                    align: 'center',
+                    resizable: false,
+                    showOverflowTooltip: true,
+                    minWidth: 120,
+                    formatter: (row) => {
+                        if (row.finishTime && row.acceptTime) {
+                            return parseInt(
+                                (new Date(row.finishTime).getTime() -
+                                    new Date(row.acceptTime).getTime()) /
+                                60000
+                            );
+                        }
+                    }
+                },
+                {
+                    prop: 'orderStatus',
+                    label: '状态',
+                    align: 'center',
+                    showOverflowTooltip: true,
+                    formatter: (row) => {
+                        return this.workOrderStatus.filter(
+                            (item) => item.code == row.orderStatus
+                        )[0].label;
+                    }
+                },
+                // {
+                //     prop: 'acceptanceStatus',
+                //     label: '执行结果',
+                //     align: 'center',
+                //     showOverflowTooltip: true,
+                //     formatter: (row) => {
+                //         return row.acceptanceStatus
+                //             ? resultStatus.filter(
+                //                 (item) => item.code == row.acceptanceStatus
+                //             )[0].label
+                //             : '';
+                //     }
+                // },
+                {
+                    columnKey: 'action',
+                    label: '操作',
+                    width: 240,
+                    align: 'center',
+                    resizable: false,
+                    slot: 'action',
+                    showOverflowTooltip: true
+                }
+            ],
+            // 加载状态
+            loading: false,
+        };
+    },
+    computed: {},
+    created() {
+    },
+    methods: {
+        /* 表格数据源 */
+        datasource({ page, limit, where, order }) {
+            return getSalesWorkOrder({ pageNum: page, size: limit, ...where });
+        },
+        /* 刷新表格 */
+        reload(where) {
+            this.$refs.table.reload({ page: 1, where });
+        },
+        async cancel(row) {
+            const res = await deleteSalesWorkOrder([row.id]);
+            if (res) {
+                this.$message.success('删除成功');
+                this.reload();
+            }
+        },
+        addSpareItems(row) {
+            let data = JSON.parse(JSON.stringify(row));
+            this.$refs.edit.open(data, 'work');
+        },
+        //接收
+        async handleReceive(row) {
+            const res = await receiveSalesWorkOrder(row)
+            if (!res) return
+            this.$message.success('操作成功')
+            this.reload()
+        },
+        // 转派
+        toRedeploy(row) {
+            this.$refs.redeployOtherRef.open(row, 'transfer');
+        },
+        //报工
+        declarationForm(row) {
+            this.$refs.declarationDialogRef.open(row)
+        },
+        goDetail(row) {
+            this.$refs.detailDialogRef.open(row);
+        },
+        handleCommand(command, row) {
+            if (command === 'addSpareItems') {
+                this.addSpareItems(row);
+            }
+            if (command === 'handleReceive') {
+                this.handleReceive(row);
+            }
+            if (command === 'toRedeploy') {
+                this.toRedeploy(row);
+            }
+        }
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.el-dropdown-link {
+    cursor: pointer;
+    color: var(--color-primary-5);
+}
+
+.el-icon-arrow-down {
+    font-size: 12px;
+}
+</style>

+ 1 - 1
vue.config.js

@@ -37,7 +37,7 @@ module.exports = {
         // target: 'http://192.168.1.139:18086', // 粟
         // target: 'http://192.168.1.132:18086', // 徐1
         // target: 'http://192.168.1.134:18086', //徐2
-        target: 'http://192.168.1.105:18086',
+        target: 'http://192.168.1.144:18086',
         // target: 'http://192.168.1.103:18086',
         // target: 'http://192.168.1.251:18186',
         // target: 'http://192.168.1.105:18086',