完善主图预览删除操作,优化元数据处理逻辑
This commit is contained in:
@@ -69,7 +69,11 @@
|
|||||||
<el-divider content-position="left">分类属性</el-divider>
|
<el-divider content-position="left">分类属性</el-divider>
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
<el-col :span="8" v-for="(attr, index) in categoryAttrs" :key="index">
|
<el-col :span="8" v-for="(attr, index) in categoryAttrs" :key="index">
|
||||||
<el-form-item :label="getAttrLabel(attr)">
|
<el-form-item
|
||||||
|
:label="getAttrLabel(attr)"
|
||||||
|
:prop="'metadata.' + getAttrKey(attr)"
|
||||||
|
:rules="[{ required: true, message: getAttrLabel(attr) + '不能为空', trigger: ['blur', 'change'] }]"
|
||||||
|
>
|
||||||
<!-- 单选类型 -->
|
<!-- 单选类型 -->
|
||||||
<el-select
|
<el-select
|
||||||
v-if="attr.type === 'select'"
|
v-if="attr.type === 'select'"
|
||||||
@@ -127,15 +131,33 @@
|
|||||||
v-model="ruleForm.metadata[getAttrKey(attr)]"
|
v-model="ruleForm.metadata[getAttrKey(attr)]"
|
||||||
/>
|
/>
|
||||||
<!-- 图片类型 -->
|
<!-- 图片类型 -->
|
||||||
|
<div v-else-if="attr.type === 'image'" class="w100">
|
||||||
<el-upload
|
<el-upload
|
||||||
v-else-if="attr.type === 'image'"
|
|
||||||
class="attr-image-uploader"
|
class="attr-image-uploader"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
|
:on-change="onAttrImageChange(attr)"
|
||||||
>
|
>
|
||||||
<el-button type="primary" size="small">上传图片</el-button>
|
<img
|
||||||
|
v-if="ruleForm.metadata[getAttrKey(attr)]"
|
||||||
|
:src="formatImageUrl(ruleForm.metadata[getAttrKey(attr)])"
|
||||||
|
class="avatar"
|
||||||
|
style="width: 80px; height: 80px"
|
||||||
|
/>
|
||||||
|
<el-icon v-else class="avatar-uploader-icon" style="width: 80px; height: 80px; line-height: 80px"><Plus /></el-icon>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
<el-button
|
||||||
|
v-if="ruleForm.metadata[getAttrKey(attr)]"
|
||||||
|
type="danger"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
@click.stop="removeAttrImage(attr)"
|
||||||
|
style="margin-top: 5px"
|
||||||
|
>
|
||||||
|
删除图片
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -146,6 +168,7 @@
|
|||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="主图" prop="mainImage">
|
<el-form-item label="主图" prop="mainImage">
|
||||||
|
<div class="w100">
|
||||||
<el-upload
|
<el-upload
|
||||||
class="avatar-uploader"
|
class="avatar-uploader"
|
||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
@@ -156,6 +179,11 @@
|
|||||||
<img v-if="mainImagePreview" :src="mainImagePreview" class="avatar" />
|
<img v-if="mainImagePreview" :src="mainImagePreview" class="avatar" />
|
||||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
<div v-if="mainImagePreview" style="margin-top: 5px">
|
||||||
|
<el-button type="primary" link size="small" @click.stop="previewMainImage">预览</el-button>
|
||||||
|
<el-button type="danger" link size="small" @click.stop="removeMainImage">删除</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
@@ -395,6 +423,7 @@ const getAttrLabel = (attr: CategoryAttr): string => {
|
|||||||
const mainImageFile = ref<File | null>(null);
|
const mainImageFile = ref<File | null>(null);
|
||||||
const mainImagePreview = ref('');
|
const mainImagePreview = ref('');
|
||||||
const imageFileList = ref<UploadUserFile[]>([]);
|
const imageFileList = ref<UploadUserFile[]>([]);
|
||||||
|
const attrImageFiles = ref<Record<string, File>>({}); // 暂存属性图片文件
|
||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
const dialogImageUrl = ref('');
|
const dialogImageUrl = ref('');
|
||||||
// 图片拼接
|
// 图片拼接
|
||||||
@@ -403,6 +432,7 @@ const imgAddressPrefix = ref('');
|
|||||||
const formatImageUrl = (url?: string) => {
|
const formatImageUrl = (url?: string) => {
|
||||||
if (!url) return '';
|
if (!url) return '';
|
||||||
if (/^https?:\/\//i.test(url)) return url;
|
if (/^https?:\/\//i.test(url)) return url;
|
||||||
|
if (/^blob:/i.test(url)) return url; // 支持本地预览地址
|
||||||
return `${imgAddressPrefix.value || ''}${url}`;
|
return `${imgAddressPrefix.value || ''}${url}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -516,6 +546,22 @@ const handleMainImageChange = (file: UploadFile) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 主图预览
|
||||||
|
const previewMainImage = () => {
|
||||||
|
if (mainImagePreview.value) {
|
||||||
|
dialogImageUrl.value = mainImagePreview.value;
|
||||||
|
dialogVisible.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 主图删除
|
||||||
|
const removeMainImage = () => {
|
||||||
|
mainImageFile.value = null;
|
||||||
|
mainImagePreview.value = '';
|
||||||
|
ruleForm.mainImage = '';
|
||||||
|
formRef.value?.validateField('mainImage');
|
||||||
|
};
|
||||||
|
|
||||||
// 图片列表预览
|
// 图片列表预览
|
||||||
const handlePictureCardPreview = (file: UploadFile) => {
|
const handlePictureCardPreview = (file: UploadFile) => {
|
||||||
console.log(file,'111');
|
console.log(file,'111');
|
||||||
@@ -532,6 +578,32 @@ const handleRemove = (file: UploadFile) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 属性图片上传处理
|
||||||
|
const handleAttrImageChange = (file: UploadFile, attr: CategoryAttr) => {
|
||||||
|
if (file.raw) {
|
||||||
|
const key = getAttrKey(attr);
|
||||||
|
const url = URL.createObjectURL(file.raw);
|
||||||
|
ruleForm.metadata[key] = url;
|
||||||
|
attrImageFiles.value[key] = file.raw;
|
||||||
|
formRef.value?.validateField(`metadata.${key}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生成属性图片上传回调
|
||||||
|
const onAttrImageChange = (attr: CategoryAttr) => {
|
||||||
|
return (file: UploadFile) => handleAttrImageChange(file, attr);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 移除属性图片
|
||||||
|
const removeAttrImage = (attr: CategoryAttr) => {
|
||||||
|
const key = getAttrKey(attr);
|
||||||
|
ruleForm.metadata[key] = '';
|
||||||
|
if (attrImageFiles.value[key]) {
|
||||||
|
delete attrImageFiles.value[key];
|
||||||
|
}
|
||||||
|
formRef.value?.validateField(`metadata.${key}`);
|
||||||
|
};
|
||||||
|
|
||||||
// 时间段操作
|
// 时间段操作
|
||||||
const addTimeSlot = () => {
|
const addTimeSlot = () => {
|
||||||
if (!ruleForm.serviceAssetConfig.schedule) {
|
if (!ruleForm.serviceAssetConfig.schedule) {
|
||||||
@@ -591,6 +663,7 @@ const resetForm = () => {
|
|||||||
mainImageFile.value = null;
|
mainImageFile.value = null;
|
||||||
mainImagePreview.value = '';
|
mainImagePreview.value = '';
|
||||||
imageFileList.value = [];
|
imageFileList.value = [];
|
||||||
|
attrImageFiles.value = {};
|
||||||
categoryAttrs.value = [];
|
categoryAttrs.value = [];
|
||||||
imgAddressPrefix.value = '';
|
imgAddressPrefix.value = '';
|
||||||
};
|
};
|
||||||
@@ -618,6 +691,13 @@ const onCategoryChange = (categoryId: string) => {
|
|||||||
const data = res.data;
|
const data = res.data;
|
||||||
if (data?.attrs && Array.isArray(data.attrs)) {
|
if (data?.attrs && Array.isArray(data.attrs)) {
|
||||||
categoryAttrs.value = data.attrs;
|
categoryAttrs.value = data.attrs;
|
||||||
|
// 初始化属性值,确保 boolean 类型默认为 false
|
||||||
|
data.attrs.forEach((attr: CategoryAttr) => {
|
||||||
|
const key = getAttrKey(attr);
|
||||||
|
if (attr.type === 'boolean') {
|
||||||
|
ruleForm.metadata[key] = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -708,6 +788,13 @@ const openDialog = (row?: any, edit?: boolean) => {
|
|||||||
const catData = catRes.data;
|
const catData = catRes.data;
|
||||||
if (catData?.attrs && Array.isArray(catData.attrs)) {
|
if (catData?.attrs && Array.isArray(catData.attrs)) {
|
||||||
categoryAttrs.value = catData.attrs;
|
categoryAttrs.value = catData.attrs;
|
||||||
|
// 初始化属性值,确保 boolean 类型默认为 false
|
||||||
|
catData.attrs.forEach((attr: CategoryAttr) => {
|
||||||
|
const key = getAttrKey(attr);
|
||||||
|
if (attr.type === 'boolean' && ruleForm.metadata[key] === undefined) {
|
||||||
|
ruleForm.metadata[key] = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -773,9 +860,22 @@ const buildFormData = (): FormData => {
|
|||||||
formData.append('serviceAssetConfig', JSON.stringify(ruleForm.serviceAssetConfig));
|
formData.append('serviceAssetConfig', JSON.stringify(ruleForm.serviceAssetConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 属性图片
|
||||||
|
Object.keys(attrImageFiles.value).forEach((key) => {
|
||||||
|
formData.append(key, attrImageFiles.value[key]);
|
||||||
|
});
|
||||||
|
|
||||||
// 元数据(分类属性值)
|
// 元数据(分类属性值)
|
||||||
if (Object.keys(ruleForm.metadata).length > 0) {
|
if (Object.keys(ruleForm.metadata).length > 0) {
|
||||||
formData.append('metadata', JSON.stringify(ruleForm.metadata));
|
// 过滤掉 blob url,避免传给后端
|
||||||
|
const metadataToSend = { ...ruleForm.metadata };
|
||||||
|
Object.keys(metadataToSend).forEach((key) => {
|
||||||
|
if (typeof metadataToSend[key] === 'string' && metadataToSend[key].startsWith('blob:')) {
|
||||||
|
// 如果有对应的文件在 attrImageFiles 中,则置空或不传,这里选择置空
|
||||||
|
metadataToSend[key] = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
formData.append('metadata', JSON.stringify(metadataToSend));
|
||||||
}
|
}
|
||||||
|
|
||||||
return formData;
|
return formData;
|
||||||
|
|||||||
Reference in New Issue
Block a user