客户关联产品开发

This commit is contained in:
朱昊天
2026-06-18 18:19:07 +08:00
parent 27807c14eb
commit d1506d24e5
13 changed files with 717 additions and 3 deletions

View File

@@ -42,3 +42,28 @@ export function delProduct(productId) {
method: 'delete'
})
}
// 查询产品关联客户
export function listProductCustomers(productId) {
return request({
url: '/oa/customerProduct/product/' + productId,
method: 'get'
})
}
// 新增产品关联客户
export function addProductCustomer(data) {
return request({
url: '/oa/customerProduct',
method: 'post',
data: data
})
}
// 删除产品关联客户
export function delProductCustomer(relationId) {
return request({
url: '/oa/customerProduct/' + relationId,
method: 'delete'
})
}

View File

@@ -42,3 +42,28 @@ export function delCustomer(customerId) {
method: 'delete'
})
}
// 查询客户关联产品
export function listCustomerProducts(customerId) {
return request({
url: '/oa/customerProduct/customer/' + customerId,
method: 'get'
})
}
// 新增客户关联产品
export function addCustomerProduct(data) {
return request({
url: '/oa/customerProduct',
method: 'post',
data: data
})
}
// 删除客户关联产品
export function delCustomerProduct(relationId) {
return request({
url: '/oa/customerProduct/' + relationId,
method: 'delete'
})
}

View File

@@ -94,6 +94,7 @@
<el-tab-pane :label="entityType === 'customer' ? '客户详情' : '供货商详情'" name="detail" />
<el-tab-pane label="信息编辑" name="edit" />
<el-tab-pane v-if="entityType === 'customer'" label="历史订单" name="orders" />
<el-tab-pane v-if="entityType === 'customer'" label="关联产品" name="products" />
<el-tab-pane v-if="entityType === 'customer'" label="财务状态" name="finance" />
<el-tab-pane v-if="entityType === 'customer'" label="订单异议" name="dispute" />
<el-tab-pane v-if="entityType === 'customer'" label="发货单据" name="shipping" />
@@ -214,6 +215,50 @@
</div>
</div>
<div v-show="activeTab === 'products'">
<div class="orders-section">
<div class="finance-title-row">
<div class="finance-title">关联产品</div>
<div>
<el-button size="small" plain type="primary" icon="Plus" :disabled="!selectedCustomerId" @click="openCustomerProductAdd">新增关联</el-button>
<el-button size="small" plain icon="Refresh" :loading="productLoading" @click="loadCustomerProducts" style="margin-left: 8px;">刷新</el-button>
</div>
</div>
<el-table v-loading="productLoading" :data="customerProductList" border stripe :header-cell-style="{ background: '#f5f7fa' }">
<el-table-column label="产品编号" prop="productCode" min-width="160" />
<el-table-column label="产品名称" prop="productName" min-width="220" show-overflow-tooltip />
<el-table-column label="负责人" prop="owner" width="120" />
<el-table-column label="单位" prop="unit" width="100" />
<el-table-column label="备注" prop="remark" min-width="220" show-overflow-tooltip />
<el-table-column label="关联时间" prop="createTime" width="180" />
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button link type="danger" @click="handleDeleteCustomerProduct(scope.row)">移除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="新增关联产品" v-model="customerProductAddOpen" width="520px" append-to-body>
<el-form :model="customerProductForm" label-width="80px">
<el-form-item label="客户">
<el-input :model-value="selectedCustomer && selectedCustomer.name ? selectedCustomer.name : '-'" disabled />
</el-form-item>
<el-form-item label="产品">
<ProductSelect v-model="customerProductForm.productId" placeholder="请选择产品" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="customerProductForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="customerProductAddOpen = false">取消</el-button>
<el-button type="primary" :loading="customerProductAddLoading" @click="submitCustomerProductAdd">确定</el-button>
</template>
</el-dialog>
</div>
</div>
<div v-show="activeTab === 'finance'">
<div class="orders-section">
<div class="finance-title-row">
@@ -383,7 +428,7 @@
<script>
import { getCurrentInstance } from "vue";
import { listCustomer, getCustomer, delCustomer, addCustomer, updateCustomer } from "@/api/oms/customer";
import { listCustomer, getCustomer, delCustomer, addCustomer, updateCustomer, listCustomerProducts, addCustomerProduct, delCustomerProduct } from "@/api/oms/customer";
import { listSupplier, getSupplier, delSupplier, addSupplier, updateSupplier } from "@/api/oa/supplier";
import { listSupplyType } from "@/api/oa/supplyType";
import { listOrder } from "@/api/oms/order";
@@ -392,11 +437,12 @@ import { listOrderDetail } from "@/api/oms/orderDetail";
import { listReceivable } from "@/api/finance/receivable";
import request from "@/utils/request";
import DisputeFlow from "@/views/oms/order/panels/disputeFlow.vue";
import ProductSelect from "@/components/ProductSelect/index.vue";
import * as XLSX from "xlsx";
export default {
name: "Customer",
components: { DisputeFlow },
components: { DisputeFlow, ProductSelect },
setup() {
const { proxy } = getCurrentInstance();
const { customer_from } = proxy.useDict("customer_from");
@@ -456,6 +502,15 @@ export default {
orderCode: undefined,
salesManager: undefined
},
// 关联产品
productLoading: false,
customerProductList: [],
customerProductAddOpen: false,
customerProductAddLoading: false,
customerProductForm: {
productId: undefined,
remark: ""
},
orderAmountMap: {},
orderDeliveryMap: {},
// 发货单据右侧“发货单据”Tab 使用)
@@ -564,6 +619,10 @@ export default {
this.orderAmountMap = {};
this.orderDeliveryMap = {};
this.shippingList = [];
this.customerProductList = [];
this.customerProductAddOpen = false;
this.customerProductAddLoading = false;
this.customerProductForm = { productId: undefined, remark: "" };
this.financeList = [];
this.financeSummary = { receivableAmount: 0, receivedAmount: 0, unreceivedAmount: 0 };
this.financeReceiveAddOpen = false;
@@ -581,6 +640,9 @@ export default {
if (this.activeTab === "orders") {
this.loadOrders();
}
if (this.activeTab === "products") {
this.loadCustomerProducts();
}
if (this.activeTab === "finance") {
this.loadFinance();
}
@@ -719,6 +781,9 @@ export default {
if (this.activeTab === "orders") {
this.loadOrders();
}
if (this.activeTab === "products") {
this.loadCustomerProducts();
}
if (this.activeTab === "finance") {
this.loadFinance();
}
@@ -903,6 +968,58 @@ export default {
});
},
openCustomerProductAdd() {
if (!this.selectedCustomerId) return;
this.customerProductForm = {
productId: undefined,
remark: ""
};
this.customerProductAddOpen = true;
},
submitCustomerProductAdd() {
if (!this.selectedCustomerId) return;
if (!this.customerProductForm.productId) {
this.$modal.msgError("请选择产品");
return;
}
this.customerProductAddLoading = true;
addCustomerProduct({
customerId: this.selectedCustomerId,
productId: this.customerProductForm.productId,
remark: this.customerProductForm.remark || undefined
}).then(() => {
this.$modal.msgSuccess("关联成功");
this.customerProductAddOpen = false;
this.loadCustomerProducts();
}).finally(() => {
this.customerProductAddLoading = false;
});
},
loadCustomerProducts() {
if (!this.selectedCustomerId) return;
this.productLoading = true;
listCustomerProducts(this.selectedCustomerId)
.then(res => {
this.customerProductList = (res && res.data) ? res.data : [];
})
.finally(() => {
this.productLoading = false;
});
},
handleDeleteCustomerProduct(row) {
const relationId = row && row.relationId ? row.relationId : undefined;
if (!relationId) return;
this.$modal.confirm("是否确认移除此关联产品?").then(() => {
return delCustomerProduct(relationId);
}).then(() => {
this.$modal.msgSuccess("移除成功");
this.loadCustomerProducts();
}).catch(() => {});
},
openDispute(row) {
if (!row || !row.orderId) return;
this.disputeOrderId = row.orderId;

View File

@@ -73,6 +73,7 @@
<template #default="scope">
<el-button size="small" type="text" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-button size="small" type="text" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
<el-button size="small" type="text" icon="User" @click="handleCustomerRelation(scope.row)">关联客户</el-button>
<el-button size="small" type="text" icon="Document"
@click="handleInstallManual(scope.row)">安装说明书</el-button>
<el-button size="small" type="text" icon="DataAnalysis" @click="handleBom(scope.row)">BOM</el-button>
@@ -128,13 +129,50 @@
<el-button v-loading="buttonLoading" type="primary" @click="submitInstallManual">提交</el-button>
<el-button @click="cancelInstallManual">取消</el-button>
</el-dialog>
<el-dialog :title="customerRelationTitle" v-model="customerRelationOpen" width="820px" append-to-body>
<div class="customer-relation-toolbar">
<div class="customer-relation-toolbar__title">已关联客户</div>
<el-button type="primary" plain size="small" icon="Plus" @click="openCustomerRelationAdd">新增关联</el-button>
</div>
<el-table v-loading="customerRelationLoading" :data="customerRelationList" border stripe>
<el-table-column label="客户名称" prop="customerName" min-width="220" show-overflow-tooltip />
<el-table-column label="备注" prop="remark" min-width="260" show-overflow-tooltip />
<el-table-column label="关联时间" prop="createTime" width="180" />
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button link type="danger" @click="handleDeleteProductCustomer(scope.row)">移除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="新增关联客户" v-model="customerRelationAddOpen" width="520px" append-to-body>
<el-form :model="customerRelationForm" label-width="80px">
<el-form-item label="产品">
<el-input :model-value="selectedProduct && selectedProduct.productName ? selectedProduct.productName : '-'" disabled />
</el-form-item>
<el-form-item label="客户">
<CustomerSelect v-model="customerRelationForm.customerId" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="customerRelationForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="customerRelationAddOpen = false">取消</el-button>
<el-button type="primary" :loading="customerRelationAddLoading" @click="submitProductCustomerAdd">确定</el-button>
</template>
</el-dialog>
</el-dialog>
</div>
</template>
<script>
import { listProduct, getProduct, delProduct, addProduct, updateProduct } from "@/api/oa/product";
import { listProduct, getProduct, delProduct, addProduct, updateProduct, listProductCustomers, addProductCustomer, delProductCustomer } from "@/api/oa/product";
import { listProductCategory } from "@/api/oa/productCategory";
import UserSelect from '@/components/UserSelect';
import CustomerSelect from '@/components/CustomerSelect';
import BomPanel from '../components/BomPanel.vue';
import BomInfoMini from '@/components/Renderer/BomInfoMini.vue';
@@ -142,6 +180,7 @@ export default {
name: "Product",
components: {
UserSelect,
CustomerSelect,
BomPanel,
BomInfoMini
},
@@ -205,6 +244,16 @@ export default {
itemId: undefined,
installManualDialogVisible: false,
customerRelationOpen: false,
customerRelationLoading: false,
customerRelationList: [],
customerRelationAddOpen: false,
customerRelationAddLoading: false,
customerRelationForm: {
customerId: '',
remark: ''
},
selectedProduct: {},
showDetail: false,
};
@@ -293,6 +342,62 @@ export default {
this.form = row;
this.installManualDialogVisible = true;
},
handleCustomerRelation(row) {
if (!row || !row.productId) return;
this.selectedProduct = { ...row };
this.customerRelationOpen = true;
this.customerRelationAddOpen = false;
this.customerRelationForm = {
customerId: '',
remark: ''
};
this.loadProductCustomers();
},
loadProductCustomers() {
if (!this.selectedProduct || !this.selectedProduct.productId) return;
this.customerRelationLoading = true;
listProductCustomers(this.selectedProduct.productId).then(response => {
this.customerRelationList = response && response.data ? response.data : [];
}).finally(() => {
this.customerRelationLoading = false;
});
},
openCustomerRelationAdd() {
this.customerRelationForm = {
customerId: '',
remark: ''
};
this.customerRelationAddOpen = true;
},
submitProductCustomerAdd() {
if (!this.selectedProduct || !this.selectedProduct.productId) return;
if (!this.customerRelationForm.customerId) {
this.$modal.msgError("请选择客户");
return;
}
this.customerRelationAddLoading = true;
addProductCustomer({
productId: this.selectedProduct.productId,
customerId: this.customerRelationForm.customerId,
remark: this.customerRelationForm.remark || undefined
}).then(() => {
this.$modal.msgSuccess("关联成功");
this.customerRelationAddOpen = false;
this.loadProductCustomers();
}).finally(() => {
this.customerRelationAddLoading = false;
});
},
handleDeleteProductCustomer(row) {
const relationId = row && row.relationId ? row.relationId : undefined;
if (!relationId) return;
this.$modal.confirm('是否确认移除此客户关联?').then(() => {
return delProductCustomer(relationId);
}).then(() => {
this.$modal.msgSuccess("移除成功");
this.loadProductCustomers();
}).catch(() => {});
},
handleBom(row) {
this.bomDialogVisible = true;
this.bomId = row.bomId;
@@ -371,6 +476,28 @@ export default {
...this.queryParams
}, `product_${new Date().getTime()}.xlsx`)
}
},
computed: {
customerRelationTitle() {
const productName = this.selectedProduct && this.selectedProduct.productName ? this.selectedProduct.productName : "";
return productName ? `关联客户 - ${productName}` : "关联客户";
}
}
};
</script>
<style scoped>
.customer-relation-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.customer-relation-toolbar__title {
font-size: 14px;
font-weight: 600;
color: #303133;
}
</style>