feat: 将产品图/说明书备注字段从JSON数组中分离为独立数据库列
- 数据库新增 product_image_remark 和 manual_link_remark 两个TEXT列 - 备注文字不再与上传文件URL混存在 generalImage/generalManual JSON数组中 - 前端输入框改为textarea,绑定独立的备注字段 - 修复 handlePasteLink 忽略field参数的bug - 新增 isInternalFile 辅助函数,用于区分上传文件与纯文本 - 同步修改 list.vue 和 buyOdoo.vue 两个页面
This commit is contained in:
@ -510,13 +510,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="imageExternalUrl"
|
||||
placeholder="如有外部图片链接,请在此输入"
|
||||
v-model="form.productImageRemark"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入产品图备注信息"
|
||||
style="margin-top: 8px;"
|
||||
clearable
|
||||
>
|
||||
<template #prefix><el-icon><Link /></el-icon></template>
|
||||
</el-input>
|
||||
/>
|
||||
<div style="color: #409EFF; font-size: 12px; margin-top: 4px;">支持将鼠标悬停于虚线框内通过 Ctrl+V 粘贴图片快速上传</div>
|
||||
</el-form-item>
|
||||
|
||||
@ -564,13 +564,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="manualExternalUrl"
|
||||
placeholder="如有外部说明书链接,请在此输入"
|
||||
v-model="form.manualLinkRemark"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入说明书备注信息"
|
||||
style="margin-top: 8px;"
|
||||
clearable
|
||||
>
|
||||
<template #prefix><el-icon><Link /></el-icon></template>
|
||||
</el-input>
|
||||
/>
|
||||
<div style="color: #409EFF; font-size: 12px; margin-top: 4px;">支持将鼠标悬停于虚线框内通过 Ctrl+V 粘贴图片快速上传</div>
|
||||
</el-form-item>
|
||||
|
||||
@ -674,7 +674,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, nextTick, computed, watch } from 'vue';
|
||||
import { Plus, Document, DocumentCopy, Refresh, Setting, Rank, Camera, Link, Download, Bell, CircleCheck, Files, ZoomIn, Delete, Picture, FolderOpened } from '@element-plus/icons-vue';
|
||||
import { Plus, Document, DocumentCopy, Refresh, Setting, Rank, Camera, Download, Bell, CircleCheck, Files, ZoomIn, Delete, Picture, FolderOpened } from '@element-plus/icons-vue';
|
||||
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
@ -786,8 +786,6 @@ const operatorOptions = ref([
|
||||
|
||||
const fileListImage = ref<any[]>([]);
|
||||
const fileListManual = ref<any[]>([]);
|
||||
const imageExternalUrl = ref('');
|
||||
const manualExternalUrl = ref('');
|
||||
const dialogVisibleImage = ref(false);
|
||||
const dialogImageUrl = ref('');
|
||||
const cameraDialogVisible = ref(false);
|
||||
@ -1025,7 +1023,8 @@ const dialog = reactive({ visible: false, title: '' });
|
||||
const formRef = ref<FormInstance>();
|
||||
const initForm = {
|
||||
id: undefined, companyName: '', name: '', commonName: '', category: '', type: '', spec: '', unit: '',
|
||||
visibilityLevel: 0, generalManual: [] as string[], generalImage: [] as string[], isEnabled: true
|
||||
visibilityLevel: 0, generalManual: [] as string[], generalImage: [] as string[], isEnabled: true,
|
||||
productImageRemark: '', manualLinkRemark: ''
|
||||
};
|
||||
const form = ref({...initForm});
|
||||
|
||||
@ -1176,13 +1175,12 @@ const handleEdit = (row: MaterialBaseVO) => {
|
||||
} else { tempCategoryPrefix.value = []; tempCategorySuffix.value = ''; }
|
||||
|
||||
const images = row.generalImage || []; const manuals = row.generalManual || [];
|
||||
const imgFiles = images.filter(u => !isExternalLink(u)); const imgLinks = images.filter(u => isExternalLink(u));
|
||||
const manualFiles = manuals.filter(u => !isExternalLink(u)); const manualLinks = manuals.filter(u => isExternalLink(u));
|
||||
// 只显示真正的上传文件,备注字段已由 Object.assign 自动回显
|
||||
const imgFiles = images.filter(u => isInternalFile(u));
|
||||
const manualFiles = manuals.filter(u => isInternalFile(u));
|
||||
|
||||
fileListImage.value = imgFiles.map(url => ({ name: url.split('/').pop(), url: getImageUrl(url) }));
|
||||
imageExternalUrl.value = imgLinks.length > 0 ? imgLinks[0] : '';
|
||||
fileListManual.value = manualFiles.map(url => ({ name: url.split('/').pop(), url: getImageUrl(url) }));
|
||||
manualExternalUrl.value = manualLinks.length > 0 ? manualLinks[0] : '';
|
||||
});
|
||||
};
|
||||
|
||||
@ -1210,7 +1208,7 @@ const isArraysEqual = (a: any[], b: any[]): boolean => {
|
||||
|
||||
const buildPartialPayload = (current: any, original: any): any => {
|
||||
const payload: any = { id: current.id };
|
||||
const compareFields = ['name', 'commonName', 'category', 'type', 'spec', 'unit', 'visibilityLevel', 'isEnabled', 'isInspectionRequired', 'generalImage', 'generalManual', 'companyName'];
|
||||
const compareFields = ['name', 'commonName', 'category', 'type', 'spec', 'unit', 'visibilityLevel', 'isEnabled', 'isInspectionRequired', 'generalImage', 'generalManual', 'companyName', 'productImageRemark', 'manualLinkRemark'];
|
||||
for (const key of compareFields) {
|
||||
const currentVal = current[key]; const originalVal = original[key];
|
||||
if (Array.isArray(currentVal) && Array.isArray(originalVal)) { if (!isArraysEqual(currentVal, originalVal)) payload[key] = currentVal; }
|
||||
@ -1228,10 +1226,9 @@ const submitForm = async () => {
|
||||
const isDuplicate = await checkDuplicate(form.value.name, form.value.spec);
|
||||
if (isDuplicate) { submitLoading.value = false; return; }
|
||||
|
||||
const finalImageList = form.value.generalImage.filter(item => !isExternalLink(item));
|
||||
if (imageExternalUrl.value) finalImageList.push(imageExternalUrl.value);
|
||||
const finalManualList = form.value.generalManual.filter(item => !isExternalLink(item));
|
||||
if (manualExternalUrl.value) finalManualList.push(manualExternalUrl.value);
|
||||
// 只保留上传文件,过滤掉旧数据中混入的备注文字和外部链接
|
||||
const finalImageList = form.value.generalImage.filter(item => isInternalFile(item));
|
||||
const finalManualList = form.value.generalManual.filter(item => isInternalFile(item));
|
||||
|
||||
const prefixStr = tempCategoryPrefix.value.join('/'); const suffixStr = tempCategorySuffix.value.trim();
|
||||
let fullCategory = '';
|
||||
@ -1272,7 +1269,7 @@ const resetForm = () => {
|
||||
form.value = JSON.parse(JSON.stringify(initForm));
|
||||
fileListImage.value = []; fileListManual.value = [];
|
||||
tempCategoryPrefix.value = []; tempCategorySuffix.value = '';
|
||||
imageExternalUrl.value = ''; manualExternalUrl.value = ''; originalForm.value = null;
|
||||
originalForm.value = null;
|
||||
if (formRef.value) formRef.value.resetFields();
|
||||
};
|
||||
|
||||
@ -1337,6 +1334,7 @@ const tableRowClassName = ({ row }: { row: MaterialBaseVO }) => {
|
||||
|
||||
const getImageUrl = (url: string) => { return !url ? '' : (url.startsWith('http') ? url : url) }
|
||||
const isExternalLink = (str: string) => { return str && (str.startsWith('http://') || str.startsWith('https://')) && !str.includes('/api/v1/common/files') }
|
||||
const isInternalFile = (str: string) => { return str && (str.includes('/api/v1/common/files') || /\.(jpg|jpeg|png|gif|webp|bmp|pdf|zip|rar|7z)$/i.test(str)) }
|
||||
const isImageFile = (url: string) => { return /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(url) }
|
||||
const isCompressedFile = (url: string) => { return /\.(zip|rar|7z)$/i.test(url) }
|
||||
const getImagesOnly = (list: string[]) => { return !list ? [] : list.filter(item => !isExternalLink(item) && isImageFile(item)) }
|
||||
@ -1378,9 +1376,9 @@ const customUpload = async (options: any, targetField: 'generalImage' | 'general
|
||||
} catch (e) { ElMessage.error('网络错误'); onError(e) } finally { isUploading.value = false }
|
||||
}
|
||||
|
||||
const handlePasteLink = (link: string, field: string) => { imageExternalUrl.value = link }
|
||||
usePasteUpload(customUpload, 'generalImage', '#upload-generalImage', handlePasteLink)
|
||||
usePasteUpload(customUpload, 'generalManual', '#upload-generalManual', handlePasteLink)
|
||||
// 粘贴上传处理器(仅处理实际图片文件,不再将链接自动填入备注框)
|
||||
usePasteUpload(customUpload, 'generalImage', '#upload-generalImage')
|
||||
usePasteUpload(customUpload, 'generalManual', '#upload-generalManual')
|
||||
|
||||
const handleRemoveImage = async (uploadFile: any, targetField: 'generalImage' | 'generalManual') => {
|
||||
const fileName = uploadFile.name || uploadFile.url?.split('/').pop() || '此文件'
|
||||
|
||||
Reference in New Issue
Block a user