Files
klp-oa/klp-ui/src/views/crm/components/OrderObjection.vue
砂糖 7eab286e52 feat(crm/objection): 添加钢卷关联管理功能
1.  为销售异议列表、新增/编辑弹窗、详情页添加钢卷信息展示列
2.  新增钢卷选择组件,支持按订单筛选可发货钢卷
3.  实现钢卷的选择、移除、跳转详情功能
4.  改造提交接口,传递钢卷ID列表
5.  重构原有组件逻辑,优化表单处理流程
2026-05-11 15:12:20 +08:00

512 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="异议编号" prop="objectionCode">
<el-input v-model="queryParams.objectionCode" placeholder="请输入异议编号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="处理人" prop="handleUser">
<el-input v-model="queryParams.handleUser" placeholder="请输入处理人" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="salesObjectionList" @selection-change="handleSelectionChange">
<el-table-column label="编号" align="center" prop="objectionCode" />
<!-- <el-table-column label="异议类型" align="center" prop="objectionType" /> -->
<el-table-column label="产品类别" align="center" prop="productCategory" />
<el-table-column label="反馈日期" align="center" prop="returnDate" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.returnDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="投诉情况" align="center" prop="complaintContent" show-overflow-tooltip></el-table-column>
<el-table-column label="客户诉求" align="center" prop="customerDemand" show-overflow-tooltip></el-table-column>
<el-table-column label="状态" align="center" prop="objectionStatus">
<template slot-scope="scope">
<el-tag v-if="scope.row.objectionStatus === 0" type="danger">待处理</el-tag>
<el-tag v-else-if="scope.row.objectionStatus === 1" type="success">已处理</el-tag>
<el-tag v-else-if="scope.row.objectionStatus === 2" type="info">已关闭</el-tag>
</template>
</el-table-column>
<el-table-column label="钢卷信息" align="center" prop="coilList" width="200" show-overflow-tooltip>
<template slot-scope="scope">
<div v-if="scope.row.coilList && scope.row.coilList.length > 0">
<div v-for="(coil, index) in scope.row.coilList" :key="coil.coilId || index" style="margin-bottom: 4px;">
<CurrentCoilNo @click.native="handleClickCoil(coil)" :currentCoilNo="coil.currentCoilNo || coil.coilNo || ''" />
</div>
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="处理结果" align="center" prop="handleContent" show-overflow-tooltip>
<template slot-scope="scope">
<div class="cell-html" v-html="scope.row.handleContent"></div>
</template>
</el-table-column>
<el-table-column label="处理人" align="center" prop="handleUser" />
<el-table-column label="处理时间" align="center" prop="handleTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.handleTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-check" @click="handleDo(scope.row)"
v-if="scope.row.objectionStatus == 0"></el-button>
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"
v-if="scope.row.objectionStatus == 1"></el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<!-- 添加或修改销售异议管理对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="异议编号" prop="objectionCode">
<el-input v-model="form.objectionCode" placeholder="请输入异议编号" />
</el-form-item>
<el-form-item label="产品类别" prop="productCategory">
<el-input v-model="form.productCategory" placeholder="请输入产品类别" />
</el-form-item>
<el-form-item label="反馈日期" prop="returnDate">
<el-date-picker clearable
v-model="form.returnDate"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择反馈日期">
</el-date-picker>
</el-form-item>
<el-form-item label="投诉情况">
<el-input
v-model="form.complaintContent"
type="textarea"
:rows="4"
placeholder="请输入投诉情况"
></el-input>
</el-form-item>
<el-form-item label="客户诉求">
<el-input
v-model="form.customerDemand"
type="textarea"
:rows="4"
placeholder="请输入客户诉求"
></el-input>
</el-form-item>
<el-form-item label="关联钢卷">
<div style="width: 100%;">
<CoilSelector
:multiple="true"
:rangeMode="true"
:rangeData="shippedCoils"
:orderBy="true"
@confirm="(coils) => handleCoilConfirm('form', coils)"
placeholder="选择钢卷">
<el-button type="primary" size="small">
<i class="el-icon-search"></i> 选择钢卷
</el-button>
</CoilSelector>
<div v-if="formCoilList.length > 0" style="margin-top: 10px; display: flex; flex-wrap: wrap; gap: 8px;">
<div v-for="(coil, index) in formCoilList" :key="coil.coilId || index" style="display: flex; align-items: center; gap: 4px;">
<CurrentCoilNo :currentCoilNo="coil.currentCoilNo || coil.coilNo || ''" />
<el-button type="text" icon="el-icon-close" size="mini" @click="removeCoil('form', index)"></el-button>
</div>
</div>
<span v-else style="color: #909399; font-size: 12px;">暂未选择钢卷</span>
</div>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<el-dialog title="处理订单" :visible.sync="checkOpen" width="500px" append-to-body>
<el-form ref="form" :model="checkForm" :rules="rules" label-width="80px">
<el-form-item label="处理内容">
<editor v-model="checkForm.handleContent" :min-height="192" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitCheckForm"> </el-button>
<el-button @click="checkOpen = false"> </el-button>
</div>
</el-dialog>
<el-dialog title="查看处理结果" :visible.sync="viewOpen" width="700px" append-to-body>
<el-descriptions :column="2" :data="viewForm" label-width="80px" border>
<el-descriptions-item label="异议编号" :span="2">{{ viewForm.objectionCode }}</el-descriptions-item>
<el-descriptions-item label="产品类别">{{ viewForm.productCategory }}</el-descriptions-item>
<el-descriptions-item label="反馈日期">{{ parseTime(viewForm.returnDate, '{y}-{m}-{d}') }}</el-descriptions-item>
<el-descriptions-item label="投诉情况" :span="2">{{ viewForm.complaintContent }}</el-descriptions-item>
<el-descriptions-item label="客户诉求" :span="2">{{ viewForm.customerDemand }}</el-descriptions-item>
<el-descriptions-item label="处理人">{{ viewForm.handleUser }}</el-descriptions-item>
<el-descriptions-item label="处理时间">{{ parseTime(viewForm.handleTime, '{y}-{m}-{d}') }}</el-descriptions-item>
<el-descriptions-item label="处理内容" :span="2">
<div v-html="viewForm.handleContent"></div>
</el-descriptions-item>
<el-descriptions-item label="关联钢卷" :span="2">
<div v-if="viewForm.coilList && viewForm.coilList.length > 0" style="display: flex; flex-wrap: wrap; gap: 8px;">
<div v-for="(coil, index) in viewForm.coilList" :key="coil.coilId || index">
<CurrentCoilNo @click.native="handleClickCoil(coil)" :currentCoilNo="coil.currentCoilNo || coil.coilNo || ''" />
</div>
</div>
<span v-else>-</span>
</el-descriptions-item>
</el-descriptions>
<div slot="footer" class="dialog-footer">
<el-button @click="viewOpen = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listSalesObjection, getSalesObjection, delSalesObjection, addSalesObjection, updateSalesObjection } from "@/api/crm/salesObjection";
import { listOrderPackaging } from "@/api/crm/order";
import CoilSelector from "@/components/CoilSelector/index.vue";
import CurrentCoilNo from "@/components/KLPService/Renderer/CurrentCoilNo.vue";
export default {
name: "SalesObjection",
components: {
CoilSelector,
CurrentCoilNo
},
props: {
order: {
type: Object,
default: () => { }
}
},
computed: {
customerId() {
return this.order.customerId
},
orderId() {
console.log(this.order, 'order')
return this.order.orderId
},
currentUserName() {
return this.$store.getters.name;
}
},
watch: {
orderId: {
handler(newVal, oldVal) {
if (newVal !== oldVal) {
this.queryParams.orderId = newVal
// this.queryParams.customerId = this.customerId
this.getList();
}
},
immediate: true
},
},
data() {
return {
// 按钮loading
buttonLoading: false,
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 销售异议管理表格数据
salesObjectionList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
objectionCode: undefined,
orderId: this.orderId,
productCategory: undefined,
returnDate: undefined,
complaintContent: undefined,
customerDemand: undefined,
objectionType: undefined,
objectionStatus: undefined,
handleContent: undefined,
handleUser: undefined,
handleTime: undefined,
closeTime: undefined,
},
// 表单参数
form: {},
// 表单校验
rules: {
},
checkForm: {
handleContent: undefined,
},
checkOpen: false,
viewForm: {},
viewOpen: false,
// 钢卷选择相关
coilSelectorType: 'form', // form 或 check
formCoilList: [], // 表单中的钢卷列表
checkCoilList: [], // 处理中的钢卷列表
shippedCoils: [], // 订单已发货的钢卷(可选范围)
};
},
methods: {
/** 查询销售异议管理列表 */
getList() {
this.loading = true;
listSalesObjection(this.queryParams).then(response => {
this.salesObjectionList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 点击钢卷
handleClickCoil(coil) {
this.$router.push({
path: '/wms/coil/' + coil.coilId,
});
},
// 表单重置
reset() {
this.form = {
objectionId: undefined,
objectionCode: undefined,
orderId: this.orderId,
productCategory: undefined,
returnDate: undefined,
complaintContent: undefined,
customerDemand: undefined,
objectionType: 0,
objectionStatus: undefined,
handleContent: undefined,
handleUser: undefined,
handleTime: undefined,
closeTime: undefined,
remark: undefined,
createBy: undefined,
createTime: undefined,
updateBy: undefined,
updateTime: undefined,
delFlag: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.objectionId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 删除按钮操作 */
handleDelete(row) {
const objectionIds = row.objectionId || this.ids;
this.$modal.confirm('是否确认删除销售异议管理编号为"' + objectionIds + '"的数据项?').then(() => {
this.loading = true;
return delSalesObjection(objectionIds);
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
/** 导出按钮操作 */
handleExport() {
this.download('crm/salesObjection/export', {
...this.queryParams
}, `salesObjection_${new Date().getTime()}.xlsx`)
},
// ==================== 钢卷相关方法 ====================
// 获取订单已发货的钢卷
getShippedCoils() {
if (this.orderId) {
listOrderPackaging(this.orderId).then(response => {
this.shippedCoils = response.data || [];
}).catch(() => {
this.shippedCoils = [];
});
} else {
this.shippedCoils = [];
}
},
// 钢卷选择确认
handleCoilConfirm(type, selectedCoils) {
if (type === 'form') {
this.formCoilList = [...selectedCoils];
} else if (type === 'check') {
this.checkCoilList = [...selectedCoils];
}
},
// 移除选中的钢卷
removeCoil(type, index) {
if (type === 'form') {
this.formCoilList.splice(index, 1);
} else if (type === 'check') {
this.checkCoilList.splice(index, 1);
}
},
// 将钢卷数组转换为CSV字符串
coilListToCsv(coilList) {
if (!coilList || coilList.length === 0) {
return '';
}
return coilList.map(coil => coil.coilId).join(',');
},
// 从coilIds CSV和coilList中恢复钢卷数据
// 重写handleAdd方法
handleAdd() {
this.reset();
this.formCoilList = []; // 清空钢卷列表
this.getShippedCoils(); // 获取可选择的钢卷
this.open = true;
this.title = "添加销售异议管理";
},
// 重写handleUpdate方法
handleUpdate(row) {
this.loading = true;
this.reset();
this.getShippedCoils(); // 获取可选择的钢卷
const objectionId = row.objectionId || this.ids;
getSalesObjection(objectionId).then(response => {
this.loading = false;
this.form = response.data;
// 恢复钢卷列表
this.formCoilList = response.data.coilList || [];
this.open = true;
this.title = "修改销售异议管理";
});
},
// 重写handleDo方法
handleDo(row) {
console.log(row, '处理');
this.checkForm = row;
this.checkCoilList = row.coilList || []; // 恢复钢卷列表
this.getShippedCoils(); // 获取可选择的钢卷
this.checkOpen = true;
},
// 重写handleView方法
handleView(row) {
this.viewForm = row;
this.viewOpen = true;
},
// 重写submitForm方法添加coilIds
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
// 添加coilIds
const formData = {
...this.form,
coilIds: this.coilListToCsv(this.formCoilList)
};
if (this.form.objectionId != null) {
updateSalesObjection(formData).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
} else {
addSalesObjection(formData).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
// 重写submitCheckForm方法添加coilIds
submitCheckForm() {
this.buttonLoading = true;
updateSalesObjection({
...this.checkForm,
handleUser: this.currentUserName,
handleTime: this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
objectionStatus: 1,
coilIds: this.coilListToCsv(this.checkCoilList)
}).then(response => {
this.$modal.msgSuccess("处理成功");
this.checkOpen = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
}
}
};
</script>
<style scoped>
.cell-html {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200px;
}
</style>