产品id绑定改为code绑定

This commit is contained in:
luojiaquan
2026-09-22 11:20:51 +08:00
parent e0df965d89
commit 91e9a0c9b7
8 changed files with 91 additions and 64 deletions
@@ -1,11 +1,11 @@
<template>
<div class="product-select">
<el-button type="primary" plain icon="Connection" @click="openDialog">
{{ selectedIds.length ? '重新选择产品' : '选择产品' }}
{{ selectedCodes.length ? '重新选择产品' : '选择产品' }}
</el-button>
<span class="ps-summary">
已绑定 <b>{{ selectedIds.length }}</b> 个产品
<el-button v-if="selectedIds.length" link type="danger" @click="clearSelection">清空</el-button>
已绑定 <b>{{ selectedCodes.length }}</b> 个产品
<el-button v-if="selectedCodes.length" link type="danger" @click="clearSelection">清空</el-button>
</span>
<el-dialog
@@ -46,7 +46,7 @@
ref="tableRef"
v-loading="loading"
:data="filteredList"
row-key="productId"
row-key="productCode"
height="380"
border
@select="handleSelect"
@@ -74,7 +74,7 @@
<template #footer>
<div class="ps-footer">
<span>已选 <b>{{ draftIds.size }}</b> 个产品</span>
<span>已选 <b>{{ draftCodes.size }}</b> 个产品</span>
<div>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmSelection">确定</el-button>
@@ -90,7 +90,7 @@ import { ref, reactive, computed, watch, nextTick } from 'vue'
import { useProducts } from './useProducts'
const props = defineProps({
/** v-model 绑定值:逗号分隔的产品ID串,如 "3,6" */
/** v-model 绑定值:逗号分隔的产品代码串,如 "WSP011,WSP037" */
modelValue: { type: [String, Number], default: '' }
})
const emit = defineEmits(['update:modelValue', 'change'])
@@ -100,7 +100,7 @@ const { productList, ensureProducts } = useProducts()
const dialogVisible = ref(false)
const loading = ref(false)
const tableRef = ref(null)
const draftIds = reactive(new Set())
const draftCodes = reactive(new Set())
const queryParams = reactive({
productCode: '',
productName: '',
@@ -109,7 +109,7 @@ const queryParams = reactive({
status: ''
})
const selectedIds = computed(() =>
const selectedCodes = computed(() =>
String(props.modelValue || '').split(',').map(s => s.trim()).filter(Boolean)
)
@@ -133,21 +133,21 @@ function openDialog() {
function handleOpen() {
loading.value = true
draftIds.clear()
selectedIds.value.forEach(id => draftIds.add(id))
draftCodes.clear()
selectedCodes.value.forEach(code => draftCodes.add(code))
resetQuery(false)
// 拉取最新产品后,按 draftIds 全量同步一次勾选(覆盖上次弹窗遗留状态)
// 拉取最新产品后,按 draftCodes 全量同步一次勾选(覆盖上次弹窗遗留状态)
ensureProducts(true).finally(() => {
loading.value = false
nextTick(syncSelection)
})
}
/** 按 draftIds 同步当前渲染行的勾选状态(draftIds 为唯一数据源,筛选切换不丢选择) */
/** 按 draftCodes 同步当前渲染行的勾选状态(draftCodes 为唯一数据源,筛选切换不丢选择) */
function syncSelection() {
if (!tableRef.value) return
filteredList.value.forEach(row => {
tableRef.value.toggleRowSelection(row, draftIds.has(String(row.productId)))
tableRef.value.toggleRowSelection(row, draftCodes.has(String(row.productCode)))
})
}
@@ -158,17 +158,17 @@ watch(filteredList, () => {
}, { flush: 'post' })
function handleSelect(selection, row) {
const id = String(row.productId)
if (draftIds.has(id)) draftIds.delete(id)
else draftIds.add(id)
const code = String(row.productCode)
if (draftCodes.has(code)) draftCodes.delete(code)
else draftCodes.add(code)
}
function handleSelectAll(selection) {
// selection 为当前过滤后页面被选中的行;长度>0 表示全选,否则取消全选
if (selection.length) {
filteredList.value.forEach(row => draftIds.add(String(row.productId)))
filteredList.value.forEach(row => draftCodes.add(String(row.productCode)))
} else {
filteredList.value.forEach(row => draftIds.delete(String(row.productId)))
filteredList.value.forEach(row => draftCodes.delete(String(row.productCode)))
}
}
@@ -186,10 +186,10 @@ function resetQuery(reload = true) {
}
function confirmSelection() {
const ids = Array.from(draftIds)
const value = ids.join(',')
const codes = Array.from(draftCodes)
const value = codes.join(',')
emit('update:modelValue', value)
emit('change', ids)
emit('change', codes)
dialogVisible.value = false
}
@@ -4,23 +4,26 @@ import { listProduct } from '@/api/price/product'
/**
* 产品列表模块级共享缓存:全应用只拉取一次
* 用于客户绑定产品的选择与已绑产品名称/明细回显
*
* 口径说明:客户绑定关系统一存「产品代码 productCode」逗号串,
* 不存产品ID——后续产品从旧库迁移导入时新旧库ID不一致,产品代码两库稳定。
*/
const productList = ref([])
let loadingPromise = null
/** 逗号分隔 id 串 -> 字符串 id 数组 */
export function parseProductIds(value) {
/** 逗号分隔 code 串 -> 字符串 code 数组 */
export function parseProductCodes(value) {
if (Array.isArray(value)) return value.map(v => String(v)).filter(Boolean)
if (value === null || value === undefined) return []
return String(value).split(',').map(s => s.trim()).filter(Boolean)
}
/** id 数组 -> 逗号分隔串(去重,保持原顺序) */
export function joinProductIds(ids) {
/** code 数组 -> 逗号分隔串(去重,保持原顺序) */
export function joinProductCodes(codes) {
const seen = new Set()
const out = []
;(ids || []).forEach(id => {
const key = String(id)
;(codes || []).forEach(code => {
const key = String(code)
if (key && !seen.has(key)) {
seen.add(key)
out.push(key)
@@ -45,16 +48,17 @@ export function useProducts() {
return loadingPromise
}
/** 按产品ID查产品对象 */
function findProduct(productId) {
if (productId === null || productId === undefined || productId === '') return null
return productList.value.find(p => String(p.productId) === String(productId)) || null
/** 按产品代码查产品对象 */
function findProduct(productCode) {
if (productCode === null || productCode === undefined || productCode === '') return null
const code = String(productCode).trim()
return productList.value.find(p => String(p.productCode) === code) || null
}
/** 产品展示名:代码 + 名称 */
function getProductLabel(productId) {
const p = findProduct(productId)
if (!p) return String(productId)
/** 产品展示名:代码 + 名称;查不到的产品回显原代码(旧库数据/已删除产品不致空白) */
function getProductLabel(productCode) {
const p = findProduct(productCode)
if (!p) return String(productCode)
return p.productCode + ' ' + p.productName
}