feat(web-admin): edit product brands and aliases in product form
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
bfa0d50597
commit
bcfadd23d9
2 files changed
+204
-2
No files matched your search
@@ -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
|
||||
}
|
||||
|
||||
// ── 产品分类 ──
|
||||
|
||||
/** 产品分类树(不分页) */
|
||||
|
||||
@@ -192,6 +192,18 @@
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('brands')"
|
||||
label="品牌"
|
||||
min-width="140"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatProductBrands(row.brands) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="isColumnVisible('model')"
|
||||
prop="model"
|
||||
@@ -273,8 +285,7 @@
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-position="right"
|
||||
label-width="100px"
|
||||
label-position="top"
|
||||
class="crm-dialog-form"
|
||||
scroll-to-error
|
||||
:scroll-into-view-options="{ behavior: 'smooth', block: 'center' }"
|
||||
@@ -375,6 +386,78 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="sub-table-section">
|
||||
<div class="sub-table-header">
|
||||
<span class="sub-table-title">品牌</span>
|
||||
<el-button type="primary" size="small" @click="handleAddBrand">
|
||||
<el-icon><Plus /></el-icon>新增品牌
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="formData.brands" stripe class="list-table sub-table">
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column label="品牌名称" min-width="200">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.brandName" placeholder="请输入品牌名称" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" min-width="120" align="center" header-align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-model="row.status" placeholder="请选择状态" style="width: 100%">
|
||||
<el-option
|
||||
v-for="opt in dictStore.getOptions('crm_product_status')"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="80" align="center" header-align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" @click="handleRemoveBrand($index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="sub-table-section">
|
||||
<div class="sub-table-header">
|
||||
<span class="sub-table-title">别名</span>
|
||||
<el-button type="primary" size="small" @click="handleAddAlias">
|
||||
<el-icon><Plus /></el-icon>新增别名
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="formData.aliases" stripe class="list-table sub-table">
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
header-align="center"
|
||||
/>
|
||||
<el-table-column label="别名" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<el-input v-model="row.aliasName" placeholder="请输入别名" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="80" align="center" header-align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button link type="danger" @click="handleRemoveAlias($index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button class="btn-ghost" @click="formDialogVisible = false">
|
||||
@@ -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<any[]>([])
|
||||
const categoryOptions = ref<any[]>([])
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
Reference in new issue
Block a user