在SKU管理中新增规格单位和规格数量字段,优化时间显示格式并调整列宽布局,同时修改分类属性类型接口路径以匹配后端枚举接口规范
This commit is contained in:
@@ -154,3 +154,12 @@ export function deleteAssetSku(id: string) {
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
// 获取规格单位选项
|
||||
export function getSpecsUnitOptions(assetType: string) {
|
||||
return newService({
|
||||
url: '/assets/enum/getSpecsUnit',
|
||||
method: 'get',
|
||||
params: { assetType },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export function getCategory(id: string) {
|
||||
// 获取属性类型选项
|
||||
export function getCategoryAttrTypeOptions() {
|
||||
return newService({
|
||||
url: '/assets/category/getCategoryAttrTypeOptions',
|
||||
url: '/assets/enum/getCategoryAttrType',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" :title="`SKU管理 - ${assetName}`" width="1000px" :close-on-click-modal="false" @close="onClose">
|
||||
<el-dialog v-model="dialogVisible" :title="`SKU管理 - ${assetName}`" width="1200px" :close-on-click-modal="false" @close="onClose">
|
||||
<!-- 搜索区域 -->
|
||||
<div class="sku-search mb15">
|
||||
<el-button type="primary" @click="onOpenAddSku">
|
||||
@@ -21,7 +21,7 @@
|
||||
<!-- SKU 列表 -->
|
||||
<el-table :data="tableData" v-loading="loading" border style="width: 100%">
|
||||
<el-table-column prop="skuName" label="SKU名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="specValues" label="规格属性" min-width="150">
|
||||
<el-table-column prop="specValues" label="规格属性" min-width="140">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.specValues && Array.isArray(scope.row.specValues) && scope.row.specValues.length > 0">
|
||||
<el-tag v-for="(item, idx) in scope.row.specValues" :key="idx" size="small" style="margin-right: 4px">
|
||||
@@ -36,7 +36,17 @@
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="价格" width="100" align="center">
|
||||
<el-table-column prop="specsUnit" label="规格单位" width="80" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.specsUnit?.value || getSpecsUnitLabel(scope.row.specsUnit) || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="specsCount" label="规格数量" width="80" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.specsCount || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="价格" width="90" align="center">
|
||||
<template #default="scope">
|
||||
¥{{ (scope.row.price / 100).toFixed(2) }}
|
||||
</template>
|
||||
@@ -53,9 +63,21 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sort" label="排序" width="80" align="center" />
|
||||
<el-table-column prop="createdAt" label="创建时间" width="160" align="center" />
|
||||
<el-table-column prop="updatedAt" label="修改时间" width="160" align="center" />
|
||||
<el-table-column prop="sort" label="排序" width="60" align="center" />
|
||||
<el-table-column prop="createdAt" label="创建时间" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tooltip :content="scope.row.createdAt" placement="top">
|
||||
<span>{{ formatShortTime(scope.row.createdAt) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="updatedAt" label="修改时间" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tooltip :content="scope.row.updatedAt" placement="top">
|
||||
<span>{{ formatShortTime(scope.row.updatedAt) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onEditSku(scope.row)">编辑</el-button>
|
||||
@@ -86,7 +108,7 @@
|
||||
<el-form-item label="SKU名称" prop="skuName">
|
||||
<el-input v-model="skuForm.skuName" placeholder="请输入SKU名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格属性" v-if="assetSpecAttrs.length > 0">
|
||||
<el-form-item v-if="assetSpecAttrs.length > 0">
|
||||
<div class="spec-values-container">
|
||||
<div v-for="attr in assetSpecAttrs" :key="attr.name" class="spec-item">
|
||||
<span class="spec-label">{{ attr.name }}:</span>
|
||||
@@ -97,6 +119,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="规格单位" prop="specsUnit">
|
||||
<el-select v-model="skuForm.specsUnit" placeholder="请选择规格单位" style="width: 150px" clearable>
|
||||
<el-option v-for="opt in specsUnitOptions" :key="opt.key" :label="opt.value" :value="opt.key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="规格数量" prop="specsCount">
|
||||
<el-input-number v-model="skuForm.specsCount" :min="1" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="价格" prop="price">
|
||||
<el-input-number v-model="skuForm.price" :min="0" :precision="2" :step="0.01" controls-position="right" />
|
||||
<span style="margin-left: 8px">元</span>
|
||||
@@ -143,7 +173,7 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { listAssetSkus, createAssetSku, updateAssetSku, deleteAssetSku, getAssetSku, getAsset, uploadAssetImage } from '/@/api/assets/asset';
|
||||
import { listAssetSkus, createAssetSku, updateAssetSku, deleteAssetSku, getAssetSku, getAsset, uploadAssetImage, getSpecsUnitOptions } from '/@/api/assets/asset';
|
||||
import type { UploadRequestOptions, UploadUserFile } from 'element-plus';
|
||||
|
||||
interface SpecValueItem {
|
||||
@@ -167,9 +197,11 @@ const editSkuId = ref('');
|
||||
|
||||
const assetId = ref('');
|
||||
const assetName = ref('');
|
||||
const assetType = ref('');
|
||||
const assetSpecAttrs = ref<AssetSpecAttr[]>([]);
|
||||
const fileAddressPrefix = ref('');
|
||||
const skuImagePreview = ref('');
|
||||
const specsUnitOptions = ref<{ key: string; value: string }[]>([]);
|
||||
const tableData = ref<any[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
@@ -190,6 +222,8 @@ const skuForm = reactive({
|
||||
sort: 0,
|
||||
imageUrl: '',
|
||||
description: '',
|
||||
specsUnit: '',
|
||||
specsCount: 1,
|
||||
});
|
||||
|
||||
const specValuesList = ref<SpecValueItem[]>([{ key: '', value: '' }]);
|
||||
@@ -197,6 +231,11 @@ const specValuesMap = reactive<Record<string, string>>({});
|
||||
|
||||
const skuRules: FormRules = {
|
||||
skuName: [{ required: true, message: '请输入SKU名称', trigger: 'blur' }],
|
||||
specsUnit: [{ required: true, message: '请选择规格单位', trigger: 'change' }],
|
||||
specsCount: [
|
||||
{ required: true, message: '请输入规格数量', trigger: 'blur' },
|
||||
{ type: 'number', min: 1, message: '规格数量必须大于0', trigger: 'blur' }
|
||||
],
|
||||
price: [
|
||||
{ required: true, message: '请输入价格', trigger: 'blur' },
|
||||
{ type: 'number', min: 0.01, message: '价格必须大于0', trigger: 'blur' }
|
||||
@@ -221,13 +260,41 @@ const skuRules: FormRules = {
|
||||
};
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (row: { id: string; name: string }) => {
|
||||
const openDialog = (row: { id: string; name: string; type?: string }) => {
|
||||
assetId.value = row.id;
|
||||
assetName.value = row.name;
|
||||
assetType.value = row.type || '';
|
||||
dialogVisible.value = true;
|
||||
resetQuery();
|
||||
getSkuList();
|
||||
fetchAssetSpecAttrs();
|
||||
fetchSpecsUnitOptions();
|
||||
};
|
||||
|
||||
// 获取规格单位选项
|
||||
const fetchSpecsUnitOptions = () => {
|
||||
if (!assetType.value) return;
|
||||
getSpecsUnitOptions(assetType.value)
|
||||
.then((res: any) => {
|
||||
specsUnitOptions.value = res.data?.options || [];
|
||||
})
|
||||
.catch(() => {
|
||||
specsUnitOptions.value = [];
|
||||
});
|
||||
};
|
||||
|
||||
// 根据规格单位 key 获取对应的 label
|
||||
const getSpecsUnitLabel = (key: string) => {
|
||||
if (!key) return '';
|
||||
const option = specsUnitOptions.value.find((opt) => opt.key === key);
|
||||
return option?.value || key;
|
||||
};
|
||||
|
||||
// 格式化时间为短格式(只显示日期)
|
||||
const formatShortTime = (time: string) => {
|
||||
if (!time) return '-';
|
||||
// 只取日期部分 YYYY-MM-DD
|
||||
return time.substring(0, 10);
|
||||
};
|
||||
|
||||
// 获取资产规格属性(只获取多选类型)
|
||||
@@ -328,6 +395,13 @@ const onEditSku = async (row: any) => {
|
||||
skuForm.sort = data.sort || 0;
|
||||
skuForm.imageUrl = data.imageUrl || '';
|
||||
skuForm.description = data.description || '';
|
||||
// specsUnit 可能是对象格式 { key, value } 或字符串
|
||||
if (data.specsUnit && typeof data.specsUnit === 'object') {
|
||||
skuForm.specsUnit = data.specsUnit.key || '';
|
||||
} else {
|
||||
skuForm.specsUnit = data.specsUnit || '';
|
||||
}
|
||||
skuForm.specsCount = data.specsCount || 1;
|
||||
// 图片预览回显
|
||||
if (data.imageUrl) {
|
||||
skuImagePreview.value = formatImageUrl(data.imageUrl);
|
||||
@@ -396,6 +470,8 @@ const resetSkuForm = () => {
|
||||
skuForm.sort = 0;
|
||||
skuForm.imageUrl = '';
|
||||
skuForm.description = '';
|
||||
skuForm.specsUnit = '';
|
||||
skuForm.specsCount = 1;
|
||||
specValuesList.value = [{ key: '', value: '' }];
|
||||
// 清空 specValuesMap
|
||||
Object.keys(specValuesMap).forEach((key) => delete specValuesMap[key]);
|
||||
@@ -522,6 +598,16 @@ const onSubmitSku = async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// 构建 specsUnit 对象格式
|
||||
let specsUnitObj = undefined;
|
||||
if (skuForm.specsUnit) {
|
||||
const unitOption = specsUnitOptions.value.find((opt) => opt.key === skuForm.specsUnit);
|
||||
specsUnitObj = {
|
||||
key: skuForm.specsUnit,
|
||||
value: unitOption?.value || skuForm.specsUnit,
|
||||
};
|
||||
}
|
||||
|
||||
const data = {
|
||||
assetId: assetId.value,
|
||||
assetName: assetName.value,
|
||||
@@ -533,7 +619,9 @@ const onSubmitSku = async () => {
|
||||
stock: skuForm.stock,
|
||||
sort: skuForm.sort,
|
||||
status: skuForm.status,
|
||||
description:skuForm.description,
|
||||
description: skuForm.description,
|
||||
specsUnit: specsUnitObj,
|
||||
specsCount: skuForm.specsCount || undefined,
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user