<template>
|
<div>
|
<el-dialog v-model="dialogFormVisible"
|
title="新增售后单"
|
width="90%"
|
@close="closeDia">
|
<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="proDesc">
|
<el-input v-model="form.proDesc"
|
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 #approveStatus="{ row }">
|
<el-tag :type="getApproveStatusType(row)"
|
size="small">
|
{{ getApproveStatusText(row) }}
|
</el-tag>
|
</template>
|
<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>
|
<el-button @click="closeDia">取消</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
<!-- 选择产品弹窗 -->
|
<ProductSelectDialog v-model="isShowProductSelectDialog"
|
:products="currentSalesOrderProducts"
|
:selected-ids="currentSelectedProductIds"
|
@confirm="handleSelectProducts" />
|
</div>
|
</template>
|
|
<script setup>
|
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,
|
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: {
|
topic: "",
|
serviceType: "",
|
urgency: "",
|
salesLedgerId: null,
|
productModelIds: "",
|
customerId: null,
|
salesContractNo: "",
|
proDesc: "",
|
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" }],
|
},
|
});
|
|
// 自定义校验函数:判断是否需要校验售后编号
|
|
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 getProductRowId = row => {
|
return (
|
row?.id ??
|
row?.productModelId ??
|
row?.modelId ??
|
`${row?.productCategory || row?.productName || ""}-${
|
row?.specificationModel || row?.model || ""
|
}-${row?.unit || ""}`
|
);
|
};
|
|
const normalizeProductRow = row => {
|
return {
|
...row,
|
id: getProductRowId(row),
|
productCategory: row?.productCategory ?? row?.productName ?? "",
|
specificationModel: row?.specificationModel ?? row?.model ?? "",
|
unit: row?.unit ?? "",
|
approveStatus: row?.approveStatus ?? null,
|
shippingStatus: row?.shippingStatus ?? "",
|
expressCompany: row?.expressCompany ?? "",
|
expressNumber: row?.expressNumber ?? "",
|
shippingCarNumber: row?.shippingCarNumber ?? "",
|
shippingDate: row?.shippingDate ?? "",
|
quantity: row?.quantity ?? 0,
|
taxRate: row?.taxRate ?? 0,
|
taxInclusiveUnitPrice: row?.taxInclusiveUnitPrice ?? 0,
|
taxInclusiveTotalPrice: row?.taxInclusiveTotalPrice ?? 0,
|
taxExclusiveTotalPrice: row?.taxExclusiveTotalPrice ?? 0,
|
noQuantity: row?.noQuantity ?? 0,
|
};
|
};
|
|
const tableColumn = ref([
|
{ label: "产品大类", prop: "productCategory" },
|
{ label: "规格型号", prop: "specificationModel" },
|
{ label: "单位", prop: "unit" },
|
{
|
label: "产品状态",
|
prop: "approveStatus",
|
width: 100,
|
align: "center",
|
dataType: "slot",
|
slot: "approveStatus",
|
},
|
{
|
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 => getProductRowId(i) !== getProductRowId(row)
|
);
|
},
|
},
|
],
|
},
|
]);
|
const tableData = ref([]);
|
// 选择产品弹窗
|
const isShowProductSelectDialog = ref(false);
|
const handleSelectProducts = rows => {
|
if (!Array.isArray(rows)) return;
|
const existingIds = new Set(
|
tableData.value.map(i => String(getProductRowId(i)))
|
);
|
const mapped = rows
|
.map(normalizeProductRow)
|
.filter(r => !existingIds.has(String(getProductRowId(r))));
|
tableData.value = tableData.value.concat(mapped);
|
};
|
const currentSelectedProductIds = computed(() => {
|
return tableData.value
|
.map(item => getProductRowId(item))
|
.filter(item => item !== undefined && item !== null && item !== "");
|
});
|
|
const associatedSalesOrderNumberChange = () => {
|
const opt = associatedSalesOrderNumberOptions.value.find(
|
item => item.value === form.value.salesContractNo
|
);
|
tableData.value = (opt?.productData || []).map(normalizeProductRow);
|
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 || []).map(normalizeProductRow);
|
});
|
|
const customerNameChange = val => {
|
form.value.salesContractNo = "";
|
form.value.salesLedgerId = null;
|
tableData.value = [];
|
associatedSalesOrderNumberOptions.value = [];
|
const opt = customerNameOptions.value.find(item => item.value === val);
|
if (opt) {
|
form.value.customerId = opt.id;
|
} else {
|
form.value.customerId = null;
|
}
|
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 getApproveStatusText = row => {
|
if (!row) return "不足";
|
if (
|
row.approveStatus === 1 &&
|
(!row.shippingDate || !row.shippingCarNumber)
|
) {
|
return "充足";
|
}
|
if (row.approveStatus === 0 && (row.shippingDate || row.shippingCarNumber)) {
|
return "已出库";
|
}
|
return "不足";
|
};
|
|
const getApproveStatusType = row => {
|
const statusText = getApproveStatusText(row);
|
return statusText === "不足" ? "danger" : "success";
|
};
|
|
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 = async (type, row) => {
|
// 请求多个接口,获取数据
|
let res = await getAllCustomerList({
|
current: 1,
|
size: 1000,
|
total: 0,
|
});
|
console.log(res, "res");
|
|
if (res.data.records) {
|
customerNameOptions.value = res.data.records.map(item => ({
|
label: item.customerName,
|
value: item.customerName,
|
id: item.id,
|
}));
|
} else {
|
}
|
|
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 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("新增成功");
|
closeDia();
|
});
|
} else {
|
afterSalesServiceUpdate(form.value).then(response => {
|
proxy.$modal.msgSuccess("修改成功");
|
closeDia();
|
});
|
}
|
}
|
});
|
};
|
// 关闭弹框
|
const closeDia = () => {
|
proxy.resetForm("formRef");
|
dialogFormVisible.value = false;
|
emit("close");
|
};
|
defineExpose({
|
openDialog,
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.descriptions {
|
margin-bottom: 20px;
|
display: inline-block;
|
font-size: 1rem;
|
font-weight: 600;
|
padding-left: 12px;
|
position: relative;
|
}
|
|
.descriptions::before {
|
content: "";
|
position: absolute;
|
left: 0;
|
top: 50%;
|
transform: translateY(-50%);
|
width: 4px;
|
height: 1rem;
|
background-color: #002fa7; /* Element 默认红色 */
|
border-radius: 2px;
|
}
|
</style>
|