From 2307a3125c23ad79ee92306603a418085cee194d Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期四, 05 三月 2026 15:50:02 +0800
Subject: [PATCH] feat: 更新售后管理模块,新增产品选择弹窗和统计卡片
---
src/views/customerService/feedbackRegistration/components/formDia.vue | 452 ++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 371 insertions(+), 81 deletions(-)
diff --git a/src/views/customerService/feedbackRegistration/components/formDia.vue b/src/views/customerService/feedbackRegistration/components/formDia.vue
index 41c8ac6..2af71da 100644
--- a/src/views/customerService/feedbackRegistration/components/formDia.vue
+++ b/src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -2,70 +2,118 @@
<div>
<el-dialog
v-model="dialogFormVisible"
- title="鍞悗鐧昏"
- width="70%"
+ title="鏂板鍞悗鍗�"
+ width="90%"
@close="closeDia"
>
- <el-form
- :model="form"
- label-width="140px"
- label-position="top"
- :rules="rules"
- ref="formRef"
- >
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="鍙嶉鏃堕棿锛�" prop="feedbackDate">
- <el-date-picker
- style="width: 100%"
- v-model="form.feedbackDate"
- value-format="YYYY-MM-DD"
- format="YYYY-MM-DD"
- type="date"
- placeholder="璇烽�夋嫨"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鐧昏浜猴細" prop="checkUserId">
- <el-select
- v-model="form.checkUserId"
- placeholder="璇烽�夋嫨"
- clearable
- >
- <el-option
- v-for="item in userList"
- :key="item.userId"
- :label="item.nickName"
- :value="item.userId"
- ></el-option>
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerName">
- <el-input
- v-model="form.customerName"
- placeholder="璇疯緭鍏�"
- clearable
- />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="闂鎻忚堪锛�" prop="proDesc">
- <el-input
- v-model="form.proDesc"
- placeholder="璇疯緭鍏�"
- clearable
- type="textarea"
- />
- </el-form-item>
- </el-col>
- </el-row>
- </el-form>
+ <div>
+ <span class="descriptions">鍩虹璧勬枡</span>
+ <el-form
+ :model="form"
+ label-width="140px"
+ label-position="top"
+ :rules="rules"
+ ref="formRef"
+ >
+ <el-row :gutter="30">
+ <el-col :span="4">
+ <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerName">
+ <el-select
+ v-model="form.customerName"
+ filterable
+ @change="customerNameChange"
+ >
+ <el-option
+ v-for="item in customerNameOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="鍞悗绫诲瀷锛�" prop="serviceType">
+ <el-select
+ v-model="form.serviceType"
+ filterable
+ >
+ <el-option
+ v-for="dict in serviceTypeOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="鍏宠仈閿�鍞崟鍙凤細" prop="salesContractNo">
+ <el-select
+ v-model="form.salesContractNo"
+ @change="associatedSalesOrderNumberChange"
+ filterable
+ >
+ <el-option
+ v-for="item in associatedSalesOrderNumberOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="绱ф�ョ▼搴︼細" prop="urgency">
+ <el-select
+ v-model="form.urgency"
+ filterable
+ >
+ <el-option
+ v-for="dict in urgencyOptions"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="4">
+ <el-form-item label="闂鎻忚堪锛�" prop="disRes">
+ <el-input
+ v-model="form.disRes"
+ placeholder="璇疯緭鍏ラ棶棰樻弿杩�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <hr>
+ <div style="padding-top: 20px">
+ <div style="display: flex; justify-content: space-between">
+ <span class="descriptions">鍏宠仈浜у搧</span>
+ <el-button
+ type="primary"
+ style="margin-right: 12px; margin-bottom: 10px"
+ @click="isShowProductSelectDialog = true"
+ >
+ 閫夋嫨浜у搧
+ </el-button>
+ </div>
+ <PIMTable
+ :isShowPagination="false"
+ rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ >
+ <template #shippingStatus="{ row }">
+ <el-tag :type="getShippingStatusType(row)" size="small">
+ {{ getShippingStatusText(row) }}
+ </el-tag>
+ </template>
+ </PIMTable>
+ </div>
+ </div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">纭</el-button>
@@ -73,63 +121,286 @@
</div>
</template>
</el-dialog>
+ <!-- 閫夋嫨浜у搧寮圭獥 -->
+ <ProductSelectDialog
+ v-model="isShowProductSelectDialog"
+ :products="currentSalesOrderProducts"
+ :selected-ids="currentSelectedProductIds"
+ @confirm="handleSelectProducts"
+ />
</div>
</template>
<script setup>
-import {ref} from "vue";
+import { ref, reactive, toRefs, getCurrentInstance, computed } from "vue";
+import ProductSelectDialog from "./ProductSelectDialog.vue";
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
-import {afterSalesServiceAdd, afterSalesServiceUpdate} from "@/api/customerService/index.js";
+import {afterSalesServiceAdd, afterSalesServiceUpdate, getAllCustomerList, getSalesLedger } from "@/api/customerService/index.js";
import { getCurrentDate } from "@/utils/index.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
+const formRef = ref(null)
+const customerNameOptions = ref([])
const userStore = useUserStore();
const data = reactive({
form: {
- feedbackDate: "",
- checkUserId: "",
- customerName: "",
- proDesc: "",
+ topic: "",
+ serviceType: "",
+ urgency: "",
+ salesLedgerId: null,
+ productModelIds: "",
+ customerId: null,
+ salesContractNo: "",
+ disRes: "",
+ customerName: ""
},
rules: {
+ customerName: [{required: true, message: "璇烽�夋嫨瀹㈡埛鍚嶇О", trigger: "change"}],
+ serviceType: [{required: true, message: "璇烽�夋嫨鍞悗绫诲瀷", trigger: "change"}],
+ urgency: [{required: true, message: "璇烽�夋嫨绱ф�ョ▼搴�", trigger: "change"}],
feedbackDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
- checkUserId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
- customerName: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
- proDesc: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
}
})
+
+// 鑷畾涔夋牎楠屽嚱鏁帮細鍒ゆ柇鏄惁闇�瑕佹牎楠屽敭鍚庣紪鍙�
+
const { form, rules } = toRefs(data);
const userList = ref([])
+const formatCurrency = (val) => {
+ if (val === null || val === undefined || val === '') return '-'
+ const num = Number(val)
+ return Number.isFinite(num) ? num.toFixed(2) : '-'
+}
+
+const { post_sale_waiting_list, degree_of_urgency } = proxy.useDict(
+ "post_sale_waiting_list",
+ "degree_of_urgency"
+);
+
+const serviceTypeOptions = computed(() => post_sale_waiting_list?.value || []);
+const urgencyOptions = computed(() => degree_of_urgency?.value || []);
+
+const tableColumn = ref([
+ { label: "浜у搧澶х被", prop: "productCategory" },
+ { label: "瑙勬牸鍨嬪彿", prop: "specificationModel" },
+ { label: "鍗曚綅", prop: "unit" },
+ {
+ label: "浜у搧鐘舵��",
+ prop: "approveStatus",
+ width: 100,
+ align: "center",
+ dataType: "tag",
+ formatData: (v) => (v === 1 ? "鍏呰冻" : "涓嶈冻"),
+ formatType: (v) => (v === 1 ? "success" : "danger"),
+ },
+ {
+ label: "鍙戣揣鐘舵��",
+ align: "center",
+ width: 140,
+ dataType: "slot",
+ slot: "shippingStatus",
+ },
+ { label: "蹇�掑叕鍙�", prop: "expressCompany", width: 140 },
+ { label: "蹇�掑崟鍙�", prop: "expressNumber", width: 160 },
+ { label: "鍙戣揣杞︾墝", prop: "shippingCarNumber", minWidth: 100, align: "center" },
+ { label: "鍙戣揣鏃ユ湡", prop: "shippingDate", minWidth: 100, align: "center" },
+ { label: "鏁伴噺", prop: "quantity", width: 100 },
+ { label: "绋庣巼(%)", prop: "taxRate", width: 100 },
+ {
+ label: "鍚◣鍗曚环(鍏�)",
+ prop: "taxInclusiveUnitPrice",
+ width: 160,
+ formatData: formatCurrency,
+ },
+ {
+ label: "鍚◣鎬讳环(鍏�)",
+ prop: "taxInclusiveTotalPrice",
+ width: 160,
+ formatData: formatCurrency,
+ },
+ {
+ label: "涓嶅惈绋庢�讳环(鍏�)",
+ prop: "taxExclusiveTotalPrice",
+ width: 160,
+ formatData: formatCurrency,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: 'right',
+ operation: [
+ {
+ name: "鍒犻櫎",
+ type: "text",
+ clickFun: (row) => {
+ tableData.value = tableData.value.filter(i => i.id !== row.id)
+ },
+
+ },
+ ],
+ },
+])
+const tableData = ref([])
+// 閫夋嫨浜у搧寮圭獥
+const isShowProductSelectDialog = ref(false)
+const handleSelectProducts = (rows) => {
+ if (!Array.isArray(rows)) return
+ const existingIds = new Set(tableData.value.map(i => i.id))
+ const mapped = rows
+ .filter(r => !existingIds.has(r.id))
+ .map(r => ({
+ id: r.id,
+ productCategory: r.productName,
+ specificationModel: r.model,
+ unit: r.unit || '',
+ approveStatus: null,
+ shippingStatus: '',
+ expressCompany: '',
+ expressNumber: '',
+ shippingCarNumber: '',
+ shippingDate: '',
+ quantity: 0,
+ taxRate: 0,
+ taxInclusiveUnitPrice: 0,
+ taxInclusiveTotalPrice: 0,
+ taxExclusiveTotalPrice: 0,
+ }))
+ tableData.value = tableData.value.concat(mapped)
+}
+const currentSelectedProductIds = computed(() => {
+ return tableData.value.map(item => item.id)
+})
+
+const associatedSalesOrderNumberChange = () => {
+ const opt = associatedSalesOrderNumberOptions.value.find(
+ (item) => item.value === form.value.salesContractNo
+ )
+ tableData.value = opt?.productData || []
+ form.value.salesLedgerId = opt?.id || null
+}
+
+const associatedSalesOrderNumberOptions = ref([])
+
+const currentSalesOrderProducts = computed(() => {
+ const opt = associatedSalesOrderNumberOptions.value.find(
+ (item) => item.value === form.value.salesContractNo
+ )
+ return opt?.productData || []
+})
+
+const customerNameChange = (val) => {
+ const opt = customerNameOptions.value.find(item => item.value === val);
+ if (opt) {
+ form.value.customerId = opt.id;
+ }
+ getSalesLedger({
+ customerName: form.value.customerName
+ }).then(res => {
+ if(res.code === 200){
+ associatedSalesOrderNumberOptions.value = res.data.records.map(item => ({
+ label: item.salesContractNo,
+ value: item.salesContractNo,
+ productData:item.productData,
+ id: item.id
+ }))
+ }
+ })
+}
+
+const getShippingStatusText = (row) => {
+ if (!row) return '寰呭彂璐�'
+ if (row.shippingDate || row.shippingCarNumber) {
+ return '宸插彂璐�'
+ }
+ const status = row.shippingStatus
+ if (status === null || status === undefined || status === '') {
+ return '寰呭彂璐�'
+ }
+ const map = {
+ '寰呭彂璐�': '寰呭彂璐�',
+ '寰呭鏍�': '寰呭鏍�',
+ '瀹℃牳涓�': '瀹℃牳涓�',
+ '瀹℃牳鎷掔粷': '瀹℃牳鎷掔粷',
+ '瀹℃牳閫氳繃': '瀹℃牳閫氳繃',
+ '宸插彂璐�': '宸插彂璐�'
+ }
+ return map[String(status).trim()] || '寰呭彂璐�'
+}
+
+const getShippingStatusType = (row) => {
+ if (!row) return 'info'
+ if (row.shippingDate || row.shippingCarNumber) {
+ return 'success'
+ }
+ const status = row.shippingStatus
+ if (status === null || status === undefined || status === '') {
+ return 'info'
+ }
+ const map = {
+ '寰呭彂璐�': 'info',
+ '寰呭鏍�': 'warning',
+ '瀹℃牳涓�': 'warning',
+ '瀹℃牳鎷掔粷': 'danger',
+ '瀹℃牳閫氳繃': 'success',
+ '宸插彂璐�': 'success'
+ }
+ return map[String(status).trim()] || 'info'
+}
+
// 鎵撳紑寮规
-const openDialog = (type, row) => {
+const openDialog =async (type, row) => {
+ // 璇锋眰澶氫釜鎺ュ彛锛岃幏鍙栨暟鎹�
+ let res = await getAllCustomerList();
+ if(res.records){
+ customerNameOptions.value = res.records.map(item => ({
+ label: item.customerName,
+ value: item.customerName,
+ id: item.id
+ }));
+ }
+
+
operationType.value = type;
dialogFormVisible.value = true;
form.value = {}
proxy.resetForm("formRef");
form.value.checkUserId = userStore.id;
form.value.feedbackDate = getCurrentDate();
+ // 鏂板鏃舵竻绌哄凡閫夊叧鑱斾骇鍝�
+ if (type === "add") {
+ tableData.value = []
+ }
userListNoPageByTenantId().then((res) => {
userList.value = res.data;
});
if (type === "edit") {
form.value = {...row}
+ if (form.value.customerName) {
+ const res = await getSalesLedger({ customerName: form.value.customerName })
+ if (res?.code === 200) {
+ console.log(res)
+ associatedSalesOrderNumberOptions.value = (res.data?.records || []).map(item => ({
+ label: item.salesContractNo,
+ value: item.salesContractNo,
+ productData: item.productData,
+ id: item.id
+ }))
+ }
+ }
+ console.log(form.value)
}
}
-// const setName = (code) => {
-// const index = userList.value.findIndex(item => item.deviceModel === code);
-// if (index > -1) {
-// console.log(userList)
-// form.value.name = userList.value[index].deviceName;
-// }
-// }
const submitForm = () => {
proxy.$refs["formRef"].validate(valid => {
if (valid) {
+ // 鍖归厤浜у搧鍨嬪彿IDs
+ form.value.productModelIds = tableData.value.map(item => item.id).join(",")
if (operationType.value === "add") {
afterSalesServiceAdd(form.value).then(response => {
proxy.$modal.msgSuccess("鏂板鎴愬姛")
@@ -155,6 +426,25 @@
});
</script>
-<style scoped>
+<style scoped lang="scss">
+.descriptions {
+ margin-bottom: 20px;
+ display: inline-block;
+ font-size: 1rem;
+ font-weight: 600;
+ padding-left: 12px;
+ position: relative;
+}
-</style>
\ No newline at end of file
+.descriptions::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 4px;
+ height: 1rem;
+ background-color: #002FA7; /* Element 榛樿绾㈣壊 */
+ border-radius: 2px;
+}
+</style>
--
Gitblit v1.9.3