Merge branch 'dev' of https://gitee.com/red-future---jilin-g/admin-ui into dev
This commit is contained in:
@@ -84,3 +84,73 @@ export function uploadAssetImage(file: File) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// SKU 列表查询参数
|
||||
export interface SkuQueryParams {
|
||||
assetId: string;
|
||||
status?: number;
|
||||
keyword?: string;
|
||||
minPrice?: number;
|
||||
maxPrice?: number;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
// SKU 创建参数
|
||||
export interface CreateSkuParams {
|
||||
assetId: string;
|
||||
assetName: string;
|
||||
skuName: string;
|
||||
imageUrl?: string;
|
||||
specValues?: Record<string, any>;
|
||||
price: number;
|
||||
unlimitedStock: boolean;
|
||||
stock: number;
|
||||
sort?: number;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
// 获取 SKU 列表
|
||||
export function listAssetSkus(params: SkuQueryParams) {
|
||||
return newService({
|
||||
url: '/assets/asset/sku/listAssetSkus',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 创建 SKU
|
||||
export function createAssetSku(data: CreateSkuParams) {
|
||||
return newService({
|
||||
url: '/assets/asset/sku/createAssetSku',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取 SKU 详情
|
||||
export function getAssetSku(id: string) {
|
||||
return newService({
|
||||
url: '/assets/asset/sku/getAssetSku',
|
||||
method: 'get',
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
// 修改 SKU
|
||||
export function updateAssetSku(data: CreateSkuParams & { id: string }) {
|
||||
return newService({
|
||||
url: '/assets/asset/sku/updateAssetSku',
|
||||
method: 'put',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除 SKU
|
||||
export function deleteAssetSku(id: string) {
|
||||
return newService({
|
||||
url: '/assets/asset/sku/deleteAssetSku',
|
||||
method: 'delete',
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -665,7 +665,7 @@ const rules: FormRules = {
|
||||
type: [{ required: true, message: '请选择资产类型', trigger: 'change' }],
|
||||
categoryId: [{ required: true, message: '请选择资产分类', trigger: 'change' }],
|
||||
offlineTime: [{ validator: validateOfflineTime, trigger: 'change' }],
|
||||
mainImage: [{ required: false, message: '请上传主图', trigger: 'change' }],
|
||||
mainImage: [{ required: true, message: '请上传主图', trigger: 'change' }],
|
||||
'serviceAssetConfig.serviceAssetArrivalConfig.booking.minAdvance': [{ required: true, message: '请输入最小提前时间', trigger: 'blur' }],
|
||||
'serviceAssetConfig.serviceAssetArrivalConfig.booking.minDuration': [{ required: true, message: '请输入最小时长', trigger: 'blur' }],
|
||||
'serviceAssetConfig.serviceAssetArrivalConfig.booking.cancelWindow': [{ required: true, message: '请输入取消提前时间', trigger: 'blur' }],
|
||||
@@ -1091,23 +1091,25 @@ const buildRequestBody = async (): Promise<any> => {
|
||||
|
||||
// 主图 (已在上传时直接赋值给 ruleForm.mainImage)
|
||||
if (ruleForm.mainImage) {
|
||||
body.fileURL = ruleForm.mainImage;
|
||||
body.imageURL = ruleForm.mainImage;
|
||||
}
|
||||
|
||||
// 图片列表
|
||||
const imageUrls: string[] = [];
|
||||
for (const file of imageFileList.value) {
|
||||
if (file.response) {
|
||||
// 新上传的图片使用原始 URL
|
||||
imageUrls.push(file.response as string);
|
||||
} else if (file.url && !file.url.startsWith('blob:')) {
|
||||
// 已有图片保留原 URL
|
||||
imageUrls.push(file.url);
|
||||
let url = (file.response as string) || '';
|
||||
if (!url && file.url && !file.url.startsWith('blob:')) {
|
||||
url = file.url;
|
||||
}
|
||||
if (url) {
|
||||
// 移除前缀,确保提交相对路径
|
||||
if (fileAddressPrefix.value && url.startsWith(fileAddressPrefix.value)) {
|
||||
url = url.substring(fileAddressPrefix.value.length);
|
||||
}
|
||||
imageUrls.push(url);
|
||||
}
|
||||
}
|
||||
if (imageUrls.length > 0) {
|
||||
body.images = imageUrls;
|
||||
}
|
||||
body.images = imageUrls;
|
||||
|
||||
// 根据类型添加配置
|
||||
if (ruleForm.type === 'physical') {
|
||||
|
||||
558
src/views/assets/asset/component/skuDialog.vue
Normal file
558
src/views/assets/asset/component/skuDialog.vue
Normal file
@@ -0,0 +1,558 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" :title="`SKU管理 - ${assetName}`" width="1000px" :close-on-click-modal="false" @close="onClose">
|
||||
<!-- 搜索区域 -->
|
||||
<div class="sku-search mb15">
|
||||
<el-button type="primary" @click="onOpenAddSku">
|
||||
<el-icon><ele-Plus /></el-icon>
|
||||
添加SKU
|
||||
</el-button>
|
||||
<el-input v-model="queryParams.keyword" placeholder="请输入SKU名称" clearable style="width: 180px; margin-left: 10px" />
|
||||
<el-select v-model="queryParams.status" placeholder="状态" clearable style="width: 100px; margin-left: 10px">
|
||||
<el-option label="激活" :value="1" />
|
||||
<el-option label="未激活" :value="0" />
|
||||
</el-select>
|
||||
<el-button type="primary" style="margin-left: 10px" @click="getSkuList">
|
||||
<el-icon><ele-Search /></el-icon>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="onResetQuery">重置</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 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">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.specValues && Object.keys(scope.row.specValues).length > 0">
|
||||
<el-tag v-for="(value, key) in scope.row.specValues" :key="key" size="small" style="margin-right: 4px">
|
||||
{{ key }}: {{ value }}
|
||||
</el-tag>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="price" label="价格" width="100" align="center">
|
||||
<template #default="scope">
|
||||
¥{{ (scope.row.price / 100).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="stock" label="库存数量" width="100" align="center">
|
||||
<template #default="scope">
|
||||
{{ scope.row.unlimitedStock ? '无限' : scope.row.stock }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
|
||||
{{ scope.row.status === 1 ? '激活' : '未激活' }}
|
||||
</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 label="操作" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-button size="small" text type="primary" @click="onEditSku(scope.row)">编辑</el-button>
|
||||
<el-button size="small" text type="danger" @click="onDeleteSku(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="mt15" style="text-align: right">
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.page"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="onSizeChange"
|
||||
@current-change="onCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑 SKU 弹窗 -->
|
||||
<el-dialog v-model="skuFormVisible" :title="isEditSku ? '编辑SKU' : '添加SKU'" width="500px" :close-on-click-modal="false" append-to-body>
|
||||
<el-form ref="skuFormRef" :model="skuForm" :rules="skuRules" label-width="80px" v-loading="editLoading">
|
||||
<el-form-item label="资产名称">
|
||||
<el-input v-model="assetName" disabled />
|
||||
</el-form-item>
|
||||
<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">
|
||||
<div class="spec-values-container">
|
||||
<div v-for="attr in assetSpecAttrs" :key="attr.name" class="spec-item">
|
||||
<span class="spec-label">{{ attr.name }}:</span>
|
||||
<el-select v-if="attr.options && attr.options.length > 0" v-model="specValuesMap[attr.name]" placeholder="请选择" style="width: 120px" filterable allow-create clearable>
|
||||
<el-option v-for="opt in attr.options" :key="opt" :label="opt" :value="opt" />
|
||||
</el-select>
|
||||
<el-input v-else v-model="specValuesMap[attr.name]" placeholder="请输入" style="width: 120px" />
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</el-form-item>
|
||||
<el-form-item label="库存数量" prop="stock">
|
||||
<el-input-number v-model="skuForm.stock" :min="0" :disabled="skuForm.unlimitedStock" controls-position="right" />
|
||||
<el-checkbox v-model="skuForm.unlimitedStock" style="margin-left: 10px">无限库存</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="skuForm.status">
|
||||
<el-radio :value="1">激活</el-radio>
|
||||
<el-radio :value="0">未激活</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="skuForm.sort" :min="0" controls-position="right" />
|
||||
<span style="margin-left: 8px; color: #909399; font-size: 12px">数值越小越靠前</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片" prop="imageUrl" required>
|
||||
<el-upload
|
||||
class="sku-image-uploader"
|
||||
:show-file-list="false"
|
||||
:http-request="handleSkuImageUpload"
|
||||
accept="image/*"
|
||||
>
|
||||
<img v-if="skuImagePreview" :src="skuImagePreview" class="sku-image" />
|
||||
<el-icon v-else class="sku-image-uploader-icon"><ele-Plus /></el-icon>
|
||||
</el-upload>
|
||||
<el-button v-if="skuImagePreview" type="danger" text size="small" style="margin-left: 10px" @click="removeSkuImage">删除</el-button>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="描述">
|
||||
<el-input v-model="skuForm.description" type="textarea" :rows="3" placeholder="请输入SKU描述" />
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="skuFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="onSubmitSku">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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 type { UploadRequestOptions, UploadUserFile } from 'element-plus';
|
||||
|
||||
interface SpecValueItem {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface AssetSpecAttr {
|
||||
name: string;
|
||||
options?: string[];
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const loading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const editLoading = ref(false);
|
||||
const skuFormVisible = ref(false);
|
||||
const isEditSku = ref(false);
|
||||
const editSkuId = ref('');
|
||||
|
||||
const assetId = ref('');
|
||||
const assetName = ref('');
|
||||
const assetSpecAttrs = ref<AssetSpecAttr[]>([]);
|
||||
const fileAddressPrefix = ref('');
|
||||
const skuImagePreview = ref('');
|
||||
const tableData = ref<any[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive({
|
||||
keyword: '',
|
||||
status: undefined as number | undefined,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const skuFormRef = ref<FormInstance>();
|
||||
const skuForm = reactive({
|
||||
skuName: '',
|
||||
price: 0,
|
||||
stock: 0,
|
||||
unlimitedStock: false,
|
||||
status: 1,
|
||||
sort: 0,
|
||||
imageUrl: '',
|
||||
description: '',
|
||||
});
|
||||
|
||||
const specValuesList = ref<SpecValueItem[]>([{ key: '', value: '' }]);
|
||||
const specValuesMap = reactive<Record<string, string>>({});
|
||||
|
||||
const skuRules: FormRules = {
|
||||
skuName: [{ required: true, message: '请输入SKU名称', trigger: 'blur' }],
|
||||
price: [{ required: true, message: '请输入价格', trigger: 'blur' }],
|
||||
stock: [{ required: true, message: '请输入库存数量', trigger: 'blur' }],
|
||||
imageUrl: [{ required: true, message: '请上传SKU图片', trigger: 'change' }],
|
||||
|
||||
};
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (row: { id: string; name: string }) => {
|
||||
assetId.value = row.id;
|
||||
assetName.value = row.name;
|
||||
dialogVisible.value = true;
|
||||
resetQuery();
|
||||
getSkuList();
|
||||
fetchAssetSpecAttrs();
|
||||
};
|
||||
|
||||
// 获取资产规格属性(只获取多选类型)
|
||||
const fetchAssetSpecAttrs = () => {
|
||||
getAsset(assetId.value)
|
||||
.then((res: any) => {
|
||||
const data = res.data || {};
|
||||
// 设置图片前缀
|
||||
fileAddressPrefix.value = data.fileAddressPrefix || data.imgAddressPrefix || '';
|
||||
// 从资产详情中获取规格属性列表
|
||||
if (data.specAttrs && Array.isArray(data.specAttrs)) {
|
||||
assetSpecAttrs.value = data.specAttrs;
|
||||
} else if (data.metadata && Array.isArray(data.metadata)) {
|
||||
// 只获取 multi_select 类型的属性
|
||||
assetSpecAttrs.value = data.metadata
|
||||
.filter((item: any) => item.type === 'multi_select')
|
||||
.map((item: any) => ({
|
||||
name: item.name,
|
||||
options: item.options?.map((opt: any) => opt.label || opt.value) || [],
|
||||
}));
|
||||
} else {
|
||||
assetSpecAttrs.value = [];
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
assetSpecAttrs.value = [];
|
||||
});
|
||||
};
|
||||
|
||||
// 获取 SKU 列表
|
||||
const getSkuList = () => {
|
||||
loading.value = true;
|
||||
listAssetSkus({
|
||||
assetId: assetId.value,
|
||||
...queryParams,
|
||||
})
|
||||
.then((res: any) => {
|
||||
tableData.value = res.data?.list ?? [];
|
||||
total.value = res.data?.total ?? 0;
|
||||
})
|
||||
.catch(() => {
|
||||
tableData.value = [];
|
||||
total.value = 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const resetQuery = () => {
|
||||
queryParams.keyword = '';
|
||||
queryParams.status = undefined;
|
||||
queryParams.page = 1;
|
||||
};
|
||||
|
||||
const onResetQuery = () => {
|
||||
resetQuery();
|
||||
getSkuList();
|
||||
};
|
||||
|
||||
// 分页
|
||||
const onSizeChange = (size: number) => {
|
||||
queryParams.pageSize = size;
|
||||
queryParams.page = 1;
|
||||
getSkuList();
|
||||
};
|
||||
|
||||
const onCurrentChange = (page: number) => {
|
||||
queryParams.page = page;
|
||||
getSkuList();
|
||||
};
|
||||
|
||||
// 打开添加 SKU 弹窗
|
||||
const onOpenAddSku = () => {
|
||||
isEditSku.value = false;
|
||||
resetSkuForm();
|
||||
skuFormVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑 SKU
|
||||
const onEditSku = async (row: any) => {
|
||||
isEditSku.value = true;
|
||||
editSkuId.value = row.id || '';
|
||||
resetSkuForm();
|
||||
skuFormVisible.value = true;
|
||||
editLoading.value = true;
|
||||
|
||||
try {
|
||||
const res: any = await getAssetSku(row.id);
|
||||
const data = res.data || {};
|
||||
skuForm.skuName = data.skuName || '';
|
||||
skuForm.price = (data.price || 0) / 100;
|
||||
skuForm.stock = data.stock || 0;
|
||||
skuForm.unlimitedStock = data.unlimitedStock || false;
|
||||
skuForm.status = data.status ?? 1;
|
||||
skuForm.sort = data.sort || 0;
|
||||
skuForm.imageUrl = data.imageUrl || '';
|
||||
skuForm.description = data.description || '';
|
||||
// 图片预览回显
|
||||
if (data.imageUrl) {
|
||||
skuImagePreview.value = formatImageUrl(data.imageUrl);
|
||||
}
|
||||
|
||||
// 处理规格属性
|
||||
if (data.specValues && Object.keys(data.specValues).length > 0) {
|
||||
specValuesList.value = Object.entries(data.specValues).map(([key, value]) => ({
|
||||
key,
|
||||
value: String(value),
|
||||
}));
|
||||
// 回显到 specValuesMap
|
||||
Object.entries(data.specValues).forEach(([key, value]) => {
|
||||
specValuesMap[key] = String(value);
|
||||
});
|
||||
} else {
|
||||
specValuesList.value = [{ key: '', value: '' }];
|
||||
}
|
||||
} catch (error) {
|
||||
skuFormVisible.value = false;
|
||||
} finally {
|
||||
editLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 删除 SKU
|
||||
const onDeleteSku = (row: any) => {
|
||||
ElMessageBox.confirm(`确定要删除SKU "${row.skuName}" 吗?`, '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
deleteAssetSku(row.id).then(() => {
|
||||
ElMessage.success('删除成功');
|
||||
getSkuList();
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 重置 SKU 表单
|
||||
const resetSkuForm = () => {
|
||||
skuForm.skuName = '';
|
||||
skuForm.price = 0;
|
||||
skuForm.stock = 0;
|
||||
skuForm.unlimitedStock = false;
|
||||
skuForm.status = 1;
|
||||
skuForm.sort = 0;
|
||||
skuForm.imageUrl = '';
|
||||
skuForm.description = '';
|
||||
specValuesList.value = [{ key: '', value: '' }];
|
||||
// 清空 specValuesMap
|
||||
Object.keys(specValuesMap).forEach((key) => delete specValuesMap[key]);
|
||||
skuImagePreview.value = '';
|
||||
skuFormRef.value?.clearValidate();
|
||||
};
|
||||
|
||||
// 格式化图片 URL
|
||||
const formatImageUrl = (url?: string) => {
|
||||
if (!url) return '';
|
||||
if (/^https?:\/\//i.test(url)) return url;
|
||||
if (/^blob:/i.test(url)) return url;
|
||||
return `${fileAddressPrefix.value || ''}${url}`;
|
||||
};
|
||||
|
||||
// 上传图片
|
||||
const uploadImage = async (file: File): Promise<string> => {
|
||||
const res: any = await uploadAssetImage(file);
|
||||
|
||||
if (res.fileAddressPrefix) {
|
||||
fileAddressPrefix.value = res.fileAddressPrefix;
|
||||
} else if (res.data && typeof res.data === 'object' && res.data.fileAddressPrefix) {
|
||||
fileAddressPrefix.value = res.data.fileAddressPrefix;
|
||||
}
|
||||
|
||||
if (res.fileURL) return res.fileURL;
|
||||
if (res.data && typeof res.data === 'object') {
|
||||
if (res.data.fileURL) return res.data.fileURL;
|
||||
if (res.data.url) return res.data.url;
|
||||
}
|
||||
if (typeof res.data === 'string') return res.data;
|
||||
return '';
|
||||
};
|
||||
|
||||
// 处理 SKU 图片上传
|
||||
const handleSkuImageUpload = async (options: UploadRequestOptions) => {
|
||||
try {
|
||||
const url = await uploadImage(options.file);
|
||||
if (url) {
|
||||
skuForm.imageUrl = url;
|
||||
skuImagePreview.value = formatImageUrl(url);
|
||||
skuFormRef.value?.validateField('imageUrl');
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('图片上传失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 删除 SKU 图片
|
||||
const removeSkuImage = () => {
|
||||
skuForm.imageUrl = '';
|
||||
skuImagePreview.value = '';
|
||||
skuFormRef.value?.validateField('imageUrl');
|
||||
};
|
||||
|
||||
// 获取属性的可选值
|
||||
const getAttrOptions = (key: string): string[] => {
|
||||
if (!key) return [];
|
||||
const attr = assetSpecAttrs.value.find((a) => a.name === key);
|
||||
return attr?.options || [];
|
||||
};
|
||||
|
||||
// 属性名称变更时清空属性值
|
||||
const onSpecKeyChange = (index: number) => {
|
||||
specValuesList.value[index].value = '';
|
||||
};
|
||||
|
||||
// 添加规格属性
|
||||
const addSpecValue = () => {
|
||||
specValuesList.value.push({ key: '', value: '' });
|
||||
};
|
||||
|
||||
// 删除规格属性
|
||||
const removeSpecValue = (index: number) => {
|
||||
specValuesList.value.splice(index, 1);
|
||||
};
|
||||
|
||||
// 提交 SKU
|
||||
const onSubmitSku = async () => {
|
||||
const form = skuFormRef.value;
|
||||
if (!form) return;
|
||||
|
||||
await form.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
|
||||
submitLoading.value = true;
|
||||
|
||||
// 构建规格属性对象(优先使用 specValuesMap)
|
||||
const specValues: Record<string, string> = {};
|
||||
// 从 specValuesMap 获取(直接展示的属性)
|
||||
Object.entries(specValuesMap).forEach(([key, value]) => {
|
||||
if (key && value) {
|
||||
specValues[key] = value;
|
||||
}
|
||||
});
|
||||
// 兼容旧的 specValuesList
|
||||
specValuesList.value.forEach((item) => {
|
||||
if (item.key.trim() && !specValues[item.key.trim()]) {
|
||||
specValues[item.key.trim()] = item.value.trim();
|
||||
}
|
||||
});
|
||||
|
||||
const data = {
|
||||
assetId: assetId.value,
|
||||
assetName: assetName.value,
|
||||
skuName: skuForm.skuName,
|
||||
imageUrl: skuForm.imageUrl || undefined,
|
||||
specValues: Object.keys(specValues).length > 0 ? specValues : undefined,
|
||||
price: Math.round(skuForm.price * 100),
|
||||
unlimitedStock: skuForm.unlimitedStock,
|
||||
stock: skuForm.stock,
|
||||
sort: skuForm.sort,
|
||||
status: skuForm.status,
|
||||
description:skuForm.description,
|
||||
};
|
||||
|
||||
try {
|
||||
if (isEditSku.value) {
|
||||
await updateAssetSku({ ...data, id: editSkuId.value });
|
||||
} else {
|
||||
await createAssetSku(data);
|
||||
}
|
||||
ElMessage.success(isEditSku.value ? '编辑成功' : '添加成功');
|
||||
skuFormVisible.value = false;
|
||||
getSkuList();
|
||||
} catch (error) {
|
||||
// 错误已由拦截器处理
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const onClose = () => {
|
||||
tableData.value = [];
|
||||
total.value = 0;
|
||||
resetQuery();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sku-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.spec-values-container {
|
||||
.spec-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.spec-label {
|
||||
min-width: 60px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.sku-image-uploader {
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sku-image-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
}
|
||||
|
||||
.sku-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
@@ -84,6 +84,7 @@
|
||||
</el-card>
|
||||
</div>
|
||||
<EditAsset ref="editAssetRef" @getAssetList="getAssetList" />
|
||||
<SkuDialog ref="skuDialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -98,6 +99,7 @@ import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { listAssets, updateAssetStatus, deleteAsset } from '/@/api/assets/asset';
|
||||
import EditAsset from './component/editAsset.vue';
|
||||
import SkuDialog from './component/skuDialog.vue';
|
||||
|
||||
interface AssetRow {
|
||||
id: string;
|
||||
@@ -115,6 +117,7 @@ interface AssetRow {
|
||||
}
|
||||
|
||||
const editAssetRef = ref();
|
||||
const skuDialogRef = ref();
|
||||
|
||||
const tableData = reactive({
|
||||
data: [] as AssetRow[],
|
||||
@@ -217,10 +220,9 @@ const onEdit = (row: AssetRow) => {
|
||||
editAssetRef.value.openDialog(row, true);
|
||||
};
|
||||
|
||||
// 添加SKU(待定)
|
||||
// 管理SKU
|
||||
const onAddSku = (row: AssetRow) => {
|
||||
ElMessage.info('添加SKU功能待开发');
|
||||
console.log('添加SKU:', row);
|
||||
skuDialogRef.value.openDialog(row);
|
||||
};
|
||||
|
||||
// 分页大小改变
|
||||
|
||||
Reference in New Issue
Block a user