diff --git a/web-admin/src/api/crm/product.ts b/web-admin/src/api/crm/product.ts
index 4f27fa77e..ae1bf6e12 100644
--- a/web-admin/src/api/crm/product.ts
+++ b/web-admin/src/api/crm/product.ts
@@ -1,5 +1,48 @@
import request from '@/utils/http'
+export interface ProductBrandItem {
+ id?: string
+ brandName: string
+ status?: string
+ orderNum?: number
+}
+
+export interface ProductAliasItem {
+ id?: string
+ aliasName: string
+ orderNum?: number
+}
+
+export interface ProductFormData {
+ id?: string
+ productName: string
+ productNo?: string
+ categoryId?: string
+ model?: string
+ barcode?: string
+ stock?: number
+ lowStock?: number
+ taxRate?: number
+ status?: string
+ isToxic?: number
+ isExplosive?: number
+ description?: string
+ images?: string
+ remark?: string
+ brands?: ProductBrandItem[]
+ aliases?: ProductAliasItem[]
+}
+
+export interface ProductDetail extends ProductFormData {
+ id: string
+ categoryName?: string
+ price?: number
+ costPrice?: number
+ cost?: number
+ saleCount?: number
+ createTime?: string
+}
+
// ── 产品分类 ──
/** 产品分类树(不分页) */
diff --git a/web-admin/src/views/crm/product/index.vue b/web-admin/src/views/crm/product/index.vue
index 93cbbe433..a697e7733 100644
--- a/web-admin/src/views/crm/product/index.vue
+++ b/web-admin/src/views/crm/product/index.vue
@@ -192,6 +192,18 @@
header-align="center"
show-overflow-tooltip
/>
+
+
+ {{ formatProductBrands(row.brands) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -523,6 +606,8 @@ import {
fetchImportProducts,
fetchExportProducts,
fetchImportTemplate,
+ type ProductBrandItem,
+ type ProductAliasItem,
} from '@/api/crm/product'
const dictStore = useDictStore()
@@ -543,6 +628,12 @@ function formatProductPrice(value: unknown) {
return `¥${formatMoneyDisplay(num)}`
}
+function formatProductBrands(brands?: ProductBrandItem[]) {
+ if (!brands?.length) return '-'
+ const names = brands.map((b) => b.brandName).filter(Boolean)
+ return names.length ? names.join('、') : '-'
+}
+
const productList = ref([])
const categoryOptions = ref([])
const loading = ref(false)
@@ -550,6 +641,7 @@ const loading = ref(false)
const listColumnDefs = [
{ prop: 'productNo', label: '产品编号' },
{ prop: 'categoryName', label: '产品分类' },
+ { prop: 'brands', label: '品牌' },
{ prop: 'model', label: '规格型号' },
{ prop: 'price', label: '标准价(元)' },
{ prop: 'cost', label: '成本价(元)', defaultVisible: false },
@@ -595,6 +687,8 @@ const formData = reactive({
description: '',
images: '',
remark: '',
+ brands: [] as ProductBrandItem[],
+ aliases: [] as ProductAliasItem[],
})
const formRules = {
productName: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
@@ -782,6 +876,17 @@ async function handleUpdate(row: any) {
description: data.description || '',
images: data.images || '',
remark: data.remark || '',
+ brands: (data.brands || []).map((item: ProductBrandItem) => ({
+ id: item.id,
+ brandName: item.brandName || '',
+ status: item.status || 'on',
+ orderNum: item.orderNum,
+ })),
+ aliases: (data.aliases || []).map((item: ProductAliasItem) => ({
+ id: item.id,
+ aliasName: item.aliasName || '',
+ orderNum: item.orderNum,
+ })),
})
formDialogVisible.value = true
}
@@ -864,6 +969,21 @@ async function submitForm() {
description: formData.description || undefined,
images: formData.images || undefined,
remark: formData.remark || undefined,
+ brands: formData.brands
+ .filter((item) => item.brandName?.trim())
+ .map((item, index) => ({
+ ...(item.id ? { id: item.id } : {}),
+ brandName: item.brandName.trim(),
+ status: item.status || 'on',
+ orderNum: item.orderNum ?? index + 1,
+ })),
+ aliases: formData.aliases
+ .filter((item) => item.aliasName?.trim())
+ .map((item, index) => ({
+ ...(item.id ? { id: item.id } : {}),
+ aliasName: item.aliasName.trim(),
+ orderNum: item.orderNum ?? index + 1,
+ })),
}
if (isEdit.value) {
await fetchUpdateProduct({ id: formData.id, ...data })
@@ -969,6 +1089,24 @@ function resetFormData() {
formData.description = ''
formData.images = ''
formData.remark = ''
+ formData.brands = []
+ formData.aliases = []
+}
+
+function handleAddBrand() {
+ formData.brands.push({ brandName: '', status: 'on' })
+}
+
+function handleRemoveBrand(index: number) {
+ formData.brands.splice(index, 1)
+}
+
+function handleAddAlias() {
+ formData.aliases.push({ aliasName: '' })
+}
+
+function handleRemoveAlias(index: number) {
+ formData.aliases.splice(index, 1)
}
function resetForm() {
@@ -1026,6 +1164,27 @@ onMounted(() => {
justify-content: flex-end;
gap: 8px;
}
+
+.sub-table-section {
+ margin-top: 16px;
+}
+
+.sub-table-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 8px;
+}
+
+.sub-table-title {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--text, #1e293b);
+}
+
+.sub-table {
+ margin-bottom: 8px;
+}