Ver código fonte

售后管理功能开发

cz 1 ano atrás
pai
commit
e7741e515d
1 arquivos alterados com 336 adições e 170 exclusões
  1. 336 170
      src/views/salesMange/salesMange/afterSales/index.vue

+ 336 - 170
src/views/salesMange/salesMange/afterSales/index.vue

@@ -21,18 +21,33 @@
         ]"
         @get-list="getList"
       >
-        <template #code="{ item }">
+        <template #contractCode="{ item }">
           <div
             style="cursor: pointer; color: #409eff"
             @click="handleClickCode(item)"
           >
-            {{ item.code }}
+            {{ item.contractCode }}
+          </div>
+        </template>
+        <template #status="{ item }">
+          <div
+            style="cursor: pointer; color: #409eff"
+            @click="handleClickStatus(item)"
+          >
+            <span v-if="item.status == 0">进行中</span>
+            <span v-else>关闭</span>
           </div>
         </template>
       </byTable>
     </div>
 
-    <el-dialog title="添加售后" v-model="dialogVisible" width="50%">
+    <el-dialog
+      title="添加售后"
+      v-model="dialogVisible"
+      width="50%"
+      destroy-on-close
+      v-if="dialogVisible"
+    >
       <byForm
         :formConfig="formConfig"
         :formOption="formOption"
@@ -43,34 +58,40 @@
       >
         <template #details>
           <div style="width: 100%">
-            <el-button type="primary" plain @click="openProduct = true">
+            <!-- <el-button type="primary" plain @click="openProduct = true">
               添加商品
-            </el-button>
+            </el-button> -->
             <el-table
-              :data="formData.data.subscribeDetailList"
+              :data="formData.data.afterSalesDetailList"
               style="margin-top: 10px"
             >
-              <el-table-column prop="code" label="产品编码" />
-              <el-table-column prop="name" label="产品名称" min-width="150" />
-              <el-table-column prop="spec" label="规格型号" />
+              <el-table-column prop="productCode" label="产品编码" />
+              <el-table-column
+                prop="productName"
+                label="产品名称"
+                min-width="150"
+              />
+              <el-table-column prop="productSpec" label="规格型号" />
               <el-table-column
-                prop="unit"
+                prop="productUnit"
                 label="单位"
-                :formatter="(row) => dictValueLabel(row.unit, productUnit)"
+                :formatter="
+                  (row) => dictValueLabel(row.productUnit, productUnit)
+                "
               />
-              <el-table-column prop="count" label="售后数量" min-width="150">
+              <el-table-column prop="quantity" label="售后数量" min-width="150">
                 <template #default="{ row, $index }">
                   <el-form-item
-                    :prop="'subscribeDetailList.' + $index + '.count'"
-                    :rules="rules.count"
+                    :prop="'afterSalesDetailList.' + $index + '.quantity'"
+                    :rules="rules.quantity"
                     :inline-message="true"
                   >
                     <el-input-number
                       onmousewheel="return false;"
-                      v-model="row.count"
+                      v-model="row.quantity"
                       :precision="4"
                       :controls="false"
-                      :min="1"
+                      :min="0"
                     />
                   </el-form-item>
                 </template>
@@ -78,7 +99,7 @@
               <el-table-column prop="remark" label="备注" min-width="150">
                 <template #default="{ row, $index }">
                   <el-form-item
-                    :prop="'subscribeDetailList.' + $index + '.remark'"
+                    :prop="'afterSalesDetailList.' + $index + '.remark'"
                     :rules="rules.remark"
                     :inline-message="true"
                   >
@@ -115,6 +136,7 @@
       v-model="openFollow"
       width="500"
       destroy-on-close
+      v-if="openFollow"
     >
       <byForm
         :formConfig="formConfigAFollow"
@@ -127,7 +149,7 @@
         <template #fileSlot>
           <div style="width: 100%">
             <el-upload
-              v-model:fileList="fileList"
+              v-model:fileList="formData.followData.fileList"
               action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
               :data="uploadData"
               multiple
@@ -156,6 +178,7 @@
       v-model="openRecord"
       width="500"
       destroy-on-close
+      v-if="openRecord"
     >
       <div>
         <div style="padding-top: 16px">
@@ -164,51 +187,26 @@
               <el-timeline-item
                 v-for="(record, index) in recordList"
                 :key="index"
-                :timestamp="record.date"
+                :timestamp="record.followUpTime"
                 hide-timestamp
               >
-                <div>
-                  <div
-                    style="
-                      padding: 0 0 8px 0;
-                      display: flex;
-                      justify-content: space-between;
-                    "
-                  >
-                    <span>{{
-                      dictValueLabel(record.createUser, userList)
-                    }}</span>
-                    <span>{{ record.date }}</span>
-                  </div>
-                  <div v-if="record.type == '30'">
-                    <div
-                      style="word-wrap: break-word; margin: 8px 0"
-                      v-html="getStyle(record.content)"
-                      v-if="record.content"
-                    ></div>
-                    <div v-else>跟进记录:</div>
-                  </div>
-                  <div v-else>
-                    <div style="word-wrap: break-word; margin: 8px 0">
-                      {{ getContent(record) }}
-                    </div>
-                  </div>
-                  <div
-                    style="margin: 8px 0; display: flex"
-                    v-if="record.fileList && record.fileList.length > 0"
-                  >
-                    <div style="width: 36px">附件:</div>
-                    <div style="width: calc(100% - 36px)">
-                      <div
-                        v-for="(file, index) in record.fileList"
-                        :key="index"
+                <div>{{ record.followUpTime }}</div>
+                <div style="margin: 8px 0">
+                  跟进人:{{ record.followUpUserName }}
+                </div>
+                <div>跟进记录:{{ record.remark }}</div>
+                <div
+                  style="margin: 8px 0; display: flex"
+                  v-if="record.fileList && record.fileList.length > 0"
+                >
+                  <div style="width: 36px">附件:</div>
+                  <div style="width: calc(100% - 36px)">
+                    <div v-for="(file, index) in record.fileList" :key="index">
+                      <span
+                        style="color: #409eff; cursor: pointer"
+                        @click="openFile(file.fileUrl)"
+                        >{{ file.fileName }}</span
                       >
-                        <a
-                          style="color: #409eff; cursor: pointer"
-                          @click="openFile(file.fileUrl)"
-                          >{{ file.fileName }}</a
-                        >
-                      </div>
                     </div>
                   </div>
                 </div>
@@ -244,6 +242,8 @@ import byTable from "@/components/byTable/index";
 import byForm from "@/components/byForm/index";
 import SelectGoods from "@/components/product/SelectGoods";
 import { computed } from "vue";
+import useUserStore from "@/store/modules/user";
+
 const { proxy } = getCurrentInstance();
 const fileList = ref([]);
 const uploadData = ref({});
@@ -263,33 +263,45 @@ const openFollow = ref(false);
 const openRecord = ref(false);
 const modalType = ref("add");
 const rules = ref({
-  name: [{ required: true, message: "请输入供应商名称", trigger: "blur" }],
-  qualifiedCount: [
-    { required: true, message: "请输入质检合格数量", trigger: "blur" },
-  ],
-  noQualifiedCount: [
-    { required: true, message: "请输入质检不合格数量", trigger: "blur" },
+  customerId: [
+    { required: true, message: "请选择客户名称", trigger: "change" },
   ],
+  type: [{ required: true, message: "请选择售后类型", trigger: "change" }],
+  remark: [{ required: true, message: "请输入售后说明", trigger: "blur" }],
+  quantity: [{ required: true, message: "请输入售后数量", trigger: "blur" }],
+  // remark: [{ required: true, message: "请输入售后说明", trigger: "blur" }],
 });
+const userList = ref([]);
 const customerData = ref([]);
+const contractList = ref([]);
 const productUnit = ref([]);
-const productType = ref([]);
+const salesStatus = ref([]);
+const afterSalesType = ref([]);
 const recordList = ref([]);
 const selectConfig = computed(() => [
   {
     label: "客户名称",
-    prop: "status",
+    prop: "customerId",
     data: customerData.value,
   },
   {
     label: "售后类型",
-    prop: "status",
-    data: customerData.value,
+    prop: "type",
+    data: afterSalesType.value,
   },
   {
     label: "售后状态",
     prop: "status",
-    data: customerData.value,
+    data: [
+      {
+        label: "进行中",
+        value: 0,
+      },
+      {
+        label: "关闭",
+        value: 1,
+      },
+    ],
   },
 ]);
 const config = computed(() => {
@@ -297,27 +309,25 @@ const config = computed(() => {
     {
       attrs: {
         label: "客户名称",
-        prop: "code",
-        slot: "code",
+        prop: "customerName",
       },
     },
     {
       attrs: {
         label: "销售合同编号",
-        prop: "supplyName",
-        slot: "code",
 
+        slot: "contractCode",
         width: 150,
       },
     },
     {
       attrs: {
         label: "售后类型",
-        prop: "productDefinition",
+        prop: "type",
         width: 100,
       },
       render(type) {
-        return type == 1 ? "产品" : "物料";
+        return proxy.dictValueLabel(type, afterSalesType.value);
       },
     },
 
@@ -353,28 +363,27 @@ const config = computed(() => {
     {
       attrs: {
         label: "数量",
-        prop: "count",
+        prop: "quantity",
         width: 100,
       },
     },
     {
       attrs: {
         label: "售后状态",
-        prop: "qualifiedCount",
-        slot: "code",
+        slot: "status",
         width: 100,
       },
     },
     {
       attrs: {
         label: "售后说明",
-        prop: "noQualifiedCount",
+        prop: "remark",
       },
     },
     {
       attrs: {
         label: "操作",
-        width: "150",
+        width: "200",
         align: "center",
         fixed: "right",
       },
@@ -390,7 +399,10 @@ const config = computed(() => {
             el: "button",
             click() {
               modalType.value = "add";
-              formData.followData = {};
+              formData.followData = {
+                afterSalesDetailId: row.id,
+                fileList: [],
+              };
               openFollow.value = true;
             },
           },
@@ -402,7 +414,26 @@ const config = computed(() => {
               disabled: false,
             },
             el: "button",
-            click() {},
+            click() {
+              ElMessageBox.confirm("您确定执行此操作吗?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(() => {
+                proxy
+                  .post("/afterSalesDetail/edit", {
+                    id: row.id,
+                    status: 1,
+                  })
+                  .then(() => {
+                    ElMessage({
+                      message: "操作成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
+            },
           },
           {
             attrs: {
@@ -412,38 +443,74 @@ const config = computed(() => {
               disabled: false,
             },
             el: "button",
-            click() {},
-          },
-          {
-            attrs: {
-              label: "换货",
-              type: "primary",
-              text: true,
-              disabled: false,
+            click() {
+              ElMessageBox.confirm("您确定执行退货操作吗?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(() => {
+                proxy
+                  .post("/afterSalesDetail/salesReturn", {
+                    id: row.id,
+                  })
+                  .then(() => {
+                    ElMessage({
+                      message: "退货成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
             },
-            el: "button",
-            click() {},
           },
           {
             attrs: {
-              label: "退款",
+              label: "换货",
               type: "primary",
               text: true,
               disabled: false,
             },
             el: "button",
-            click() {},
-          },
-          {
-            attrs: {
-              label: "发起采购",
-              type: "primary",
-              text: true,
-              disabled: false,
+            click() {
+              ElMessageBox.confirm("您确定执行换货操作吗?", "提示", {
+                confirmButtonText: "确定",
+                cancelButtonText: "取消",
+                type: "warning",
+              }).then(() => {
+                proxy
+                  .post("/afterSalesDetail/salesReplace", {
+                    id: row.id,
+                  })
+                  .then(() => {
+                    ElMessage({
+                      message: "换货成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
             },
-            el: "button",
-            click() {},
           },
+          // {
+          //   attrs: {
+          //     label: "退款",
+          //     type: "primary",
+          //     text: true,
+          //     disabled: false,
+          //   },
+          //   el: "button",
+          //   click() {},
+          // },
+          // {
+          //   attrs: {
+          //     label: "发起采购",
+          //     type: "primary",
+          //     text: true,
+          //     disabled: false,
+          //   },
+          //   el: "button",
+          //   click() {},
+          // },
         ];
       },
     },
@@ -473,28 +540,34 @@ const formConfig = computed(() => {
         width: "50%",
       },
       disabled: false,
+      fn: (val) => {
+        changeCustomer(val);
+      },
     },
     {
       type: "select",
-      prop: "customerId",
+      prop: "contractIds",
       label: "销售合同",
       required: true,
       itemWidth: 100,
       multiple: true,
-      data: customerData.value,
+      data: contractList.value,
       style: {
         width: "50%",
       },
       disabled: false,
+      fn: (val) => {
+        changeContract(val);
+      },
     },
     {
       type: "select",
-      prop: "customerId",
+      prop: "type",
       label: "售后类型",
       required: true,
       itemWidth: 100,
-      multiple: true,
-      data: customerData.value,
+      // multiple: true,
+      data: afterSalesType.value,
       style: {
         width: "30%",
       },
@@ -503,7 +576,7 @@ const formConfig = computed(() => {
     {
       type: "input",
       itemType: "textarea",
-      prop: "customerId",
+      prop: "remark",
       label: "售后说明",
       required: true,
       itemWidth: 100,
@@ -527,22 +600,21 @@ const formConfigAFollow = computed(() => {
       type: "date",
       itemType: "datetime",
       label: "跟进时间",
-      prop: "date",
+      prop: "followUpTime",
       itemWidth: 100,
     },
     {
       type: "select",
-
       label: "跟进人",
-      prop: "aa",
+      prop: "followUpUserId",
       itemWidth: 100,
-      data: [],
+      data: userList.value,
     },
     {
       type: "input",
       itemType: "textarea",
       label: "跟进记录",
-      prop: "content",
+      prop: "remark",
       itemWidth: 100,
     },
     {
@@ -553,22 +625,26 @@ const formConfigAFollow = computed(() => {
     },
   ];
 });
+
+const followRules = ref({
+  followUpTime: [
+    { required: true, message: "请选择跟进时间", trigger: "change" },
+  ],
+  followUpUserId: [
+    { required: true, message: "请选择跟进用户", trigger: "change" },
+  ],
+  remark: [{ required: true, message: "请输入跟进记录", trigger: "blur" }],
+});
 const formDom = ref(null);
+const followDom = ref(null);
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
   proxy
-    .post("/arrivalDetail/page", sourceList.value.pagination)
-    .then((message) => {
-      message.rows.forEach((x) => {
-        if (x.status < 20) {
-          x.isCheck = true;
-        } else {
-          x.isCheck = false;
-        }
-      });
-      sourceList.value.data = message.rows;
-      sourceList.value.pagination.total = message.total;
+    .post("/afterSalesDetail/page", sourceList.value.pagination)
+    .then((res) => {
+      sourceList.value.data = res.rows;
+      sourceList.value.pagination.total = res.total;
       setTimeout(() => {
         loading.value = false;
       }, 200);
@@ -576,32 +652,13 @@ const getList = async (req) => {
 };
 
 const submitForm = () => {
-  formDom.value.validate((valid) => {
-    const list = formData.data.qualityDetailList;
-    for (let i = 0; i < list.length; i++) {
-      const e = list[i];
-      delete e.id;
-      if (!(e.qualifiedCount + e.noQualifiedCount > 0)) {
-        return ElMessage({
-          message: "质检数量不能为0!",
-          type: "info",
-        });
-      }
-      if (
-        e.qualifiedCount + e.noQualifiedCount + Number(e.sumQualityCount) >
-        Number(e.count)
-      ) {
-        return ElMessage({
-          message: "质检数量不能大于到货数量!",
-          type: "info",
-        });
-      }
-    }
+  formDom.value.handleSubmit(() => {
     submitLoading.value = true;
-    proxy.post("/quality/" + modalType.value, formData.data).then(
+    formData.data.contractIds = formData.data.contractIds.join(",");
+    proxy.post("/afterSales/" + modalType.value, formData.data).then(
       (res) => {
         ElMessage({
-          message: "质检成功",
+          message: "新增成功",
           type: "success",
         });
         dialogVisible.value = false;
@@ -614,6 +671,30 @@ const submitForm = () => {
     );
   });
 };
+const submitFollow = () => {
+  followDom.value.handleSubmit(() => {
+    submitLoading.value = true;
+    formData.followData.fileList = formData.followData.fileList.map(
+      (x) => x.raw
+    );
+    proxy
+      .post("/afterSalesRecords/" + modalType.value, formData.followData)
+      .then(
+        (res) => {
+          ElMessage({
+            message: "跟进成功",
+            type: "success",
+          });
+          openFollow.value = false;
+          submitLoading.value = false;
+          getList();
+        },
+        (err) => {
+          submitLoading.value = false;
+        }
+      );
+  });
+};
 
 const getDtl = (row) => {
   modalType.value = "edit";
@@ -631,7 +712,60 @@ const getDtl = (row) => {
   //   dialogVisible.value = true;
   // });
 };
+const changeCustomer = (val) => {
+  proxy
+    .post("/contract/page", {
+      pageNum: 1,
+      pageSize: 9999,
+      status: 30,
+      customerId: val,
+    })
+    .then((res) => {
+      contractList.value = res.rows.map((x) => ({
+        ...x,
+        label: x.code,
+        value: x.id,
+      }));
+    });
+};
+const changeContract = (val) => {
+  if (val && val.length > 0) {
+    proxy
+      .post("/contractProduct/getListDetailByContractId", val)
+      .then((res) => {
+        formData.data.afterSalesDetailList = res.map((x) => ({
+          contractId: x.contractId,
+          contractProductId: x.id,
+          productCode: x.productCode,
+          productName: x.productName,
+          productSpec: x.productSpec,
+          productUnit: x.productUnit,
+          productId: x.productId,
+          quantity: null,
+          remark: "",
+        }));
+      });
+  } else {
+    formData.data.afterSalesDetailList = [];
+  }
+};
+
 const getDict = () => {
+  proxy
+    .get("/tenantUser/list", {
+      pageNum: 1,
+      pageSize: 10000,
+      tenantId: useUserStore().user.tenantId,
+    })
+    .then((res) => {
+      userList.value = res.rows.map((item) => {
+        return {
+          label: item.nickName,
+          value: item.userId,
+        };
+      });
+    });
+
   proxy.post("/customer/page", { pageNum: 1, pageSize: 9999 }).then((res) => {
     customerData.value = res.rows.map((x) => ({
       ...x,
@@ -640,12 +774,16 @@ const getDict = () => {
     }));
   });
 
-  proxy.getDictOne(["unit", "product_type"]).then((res) => {
+  proxy.getDictOne(["unit", "sales_status", "after_sales_type"]).then((res) => {
     productUnit.value = res["unit"].map((x) => ({
       label: x.dictValue,
       value: x.dictKey,
     }));
-    productType.value = res["product_type"].map((x) => ({
+    salesStatus.value = res["sales_status"].map((x) => ({
+      label: x.dictValue,
+      value: x.dictKey,
+    }));
+    afterSalesType.value = res["after_sales_type"].map((x) => ({
       label: x.dictValue,
       value: x.dictKey,
     }));
@@ -655,19 +793,21 @@ getDict();
 getList();
 const clickAdd = (type) => {
   modalType.value = "add";
-  formData.data = {};
+  formData.data = {
+    contractIds: [],
+  };
   dialogVisible.value = true;
 };
 
 const acquireSelectList = () => {
   let data = [];
   if (
-    formData.data.subscribeDetailList &&
-    formData.data.subscribeDetailList.length > 0
+    formData.data.afterSalesDetailList &&
+    formData.data.afterSalesDetailList.length > 0
   ) {
-    data = formData.data.subscribeDetailList.map((item) => {
+    data = formData.data.afterSalesDetailList.map((item) => {
       return {
-        id: item.bussinessId,
+        id: item.productId,
         name: item.productName,
       };
     });
@@ -679,11 +819,11 @@ const pushGoods = (goods) => {
   if (goods && goods.length > 0) {
     let afterFiltering = [];
     if (
-      formData.data.subscribeDetailList &&
-      formData.data.subscribeDetailList.length > 0
+      formData.data.afterSalesDetailList &&
+      formData.data.afterSalesDetailList.length > 0
     ) {
       afterFiltering = goods.filter((item) => {
-        let data = formData.data.subscribeDetailList.filter(
+        let data = formData.data.afterSalesDetailList.filter(
           (itemProduct) => itemProduct.bussinessId === item.id
         );
         if (data && data.length > 0) {
@@ -695,17 +835,16 @@ const pushGoods = (goods) => {
       afterFiltering = goods;
     }
     const arr = afterFiltering.map((x) => ({
-      goodType: x.goodType,
-      code: x.code,
-      name: x.name,
-      spec: x.spec,
-      unit: x.unit,
-      bussinessId: x.id,
-      count: "",
+      productCode: x.code,
+      productName: x.name,
+      productSpec: x.spec,
+      productUnit: x.unit,
+      productId: x.id,
+      quantity: null,
       remark: "",
     }));
-    formData.data.subscribeDetailList =
-      formData.data.subscribeDetailList.concat(arr);
+    formData.data.afterSalesDetailList =
+      formData.data.afterSalesDetailList.concat(arr);
     openProduct.value = false;
     return ElMessage({
       message: "添加成功!",
@@ -716,7 +855,7 @@ const pushGoods = (goods) => {
   }
 };
 const handleRemove = (index) => {
-  formData.data.subscribeDetailList.splice(index, 1);
+  formData.data.afterSalesDetailList.splice(index, 1);
   return ElMessage({
     message: "删除成功!",
     type: "success",
@@ -734,6 +873,33 @@ const uploadFile = async (file) => {
 const onPreviewFile = (file) => {
   window.open(file.raw.fileUrl, "_blank");
 };
+
+const handleClickStatus = (row) => {
+  proxy
+    .post("/afterSalesRecords/page", { afterSalesDetailId: row.id })
+    .then((res) => {
+      recordList.value = res.rows;
+      openRecord.value = true;
+      let ids = recordList.value.map((x) => x.id);
+      proxy
+        .post("/fileInfo/getList", {
+          businessIdList: ids,
+        })
+        .then((fileObj) => {
+          for (let i = 0; i < recordList.value.length; i++) {
+            const e = recordList.value[i];
+            for (const key in fileObj) {
+              if (e.id === key) {
+                e.fileList = fileObj[key] || [];
+              }
+            }
+          }
+        });
+    });
+};
+const openFile = (path) => {
+  window.open(path, "_blank");
+};
 </script>
   
 <style lang="scss" scoped>