
輕量級(jí)方案單圖改多圖前后端改動(dòng)最小化一、 核心設(shè)計(jì)思想設(shè)計(jì)原則上傳接口保持“一次傳一張、返回一個(gè) URL”不變僅在應(yīng)用層把“單張”循環(huán)擴(kuò)展為“多張”。前端策略收集 N 個(gè) URL 組成數(shù)組通過并發(fā)上傳獲取完整 URL 列表后端策略將 URL 數(shù)組序列化為 JSON 字符串存入單個(gè)數(shù)據(jù)庫(kù)字段讀取時(shí)反序列化回 List方案評(píng)估無需修改上傳底層邏輯、無需新建關(guān)聯(lián)表改動(dòng)最小。雖然犧牲了圖文 SQL 深入檢索的能力但滿足記錄、動(dòng)態(tài)展示等“按條展示”的業(yè)務(wù)場(chǎng)景。二、 后端改動(dòng)Spring Boot MyBatis-Plus1. 實(shí)體 / 數(shù)據(jù)庫(kù)設(shè)計(jì)將原來的單 URL 字段修改為存儲(chǔ) JSON 數(shù)組字符串的字段// Gain.java private String imageUrls; // 數(shù)據(jù)庫(kù)實(shí)際存儲(chǔ)值[http://img1.png,http://img2.png]2. DTO / VO 傳輸層定義// GainCreateRequest / GainUpdateRequest (請(qǐng)求入?yún)? private ListString imageUrls; // 原字段private String imageUrl // GainVO (響應(yīng)出參) private ListString imageUrls;3. Service 層業(yè)務(wù)邏輯與類型轉(zhuǎn)換注入ObjectMapper在業(yè)務(wù)邊界處理List ? JSON 字符串的相互轉(zhuǎn)換// 1. 寫庫(kù)ListString - JSON 字符串 g.setImageUrls(toJson(req.getImageUrls())); // 2. 讀庫(kù)JSON 字符串 - ListString v.setImageUrls(parseImages(g.getImageUrls())); // ------------------- 序列化 / 反序列化工具方法 ------------------- private String toJson(ListString urls) { if (urls null) { return []; } try { return objectMapper.writeValueAsString(urls); } catch (Exception e) { throw new RuntimeException(圖片列表序列化失敗, e); } } private ListString parseImages(String json) { if (json null || json.isBlank()) { return List.of(); } try { return objectMapper.readValue(json, new TypeReferenceListString() {}); } catch (Exception e) { // 兜底機(jī)制解析失敗時(shí)返回空列表避免單條數(shù)據(jù)異常導(dǎo)致整頁(yè)崩掉 return List.of(); } }??Update 判空細(xì)節(jié)提醒更新方法中判斷邏輯需同步調(diào)整為if (req.getImageUrls() ! null)。注意不要使用isEmpty()因?yàn)椤翱樟斜韀]” 代表用戶意圖“清空所有圖片”屬于合法的更新邏輯三、 前端改動(dòng)Vue 31. 節(jié)點(diǎn)屬性與響應(yīng)式數(shù)據(jù)綁定在input節(jié)點(diǎn)上加上multiple屬性并將單對(duì)象修改為數(shù)組裝載input typefile acceptimage/* hidden multiple changeonPickImage /const pendingImages ref([]) // 原const pendingImage ref(null) function onPickImage(e) { const files Array.from(e.target.files || []) e.target.value // 關(guān)鍵操作重置 input 框 value保證可重復(fù)選擇同名/同一張圖片 files.forEach((f) { if (!f.type.startsWith(image/)) { ui.toast(請(qǐng)選擇圖片文件) return } pendingImages.value.push({ file: f, url: URL.createObjectURL(f) }) }) }2. 提交保存并發(fā)上傳圖片并收集 URL// 利用 Promise.all 并發(fā)上傳多張圖片并行處理提升速度 const imageUrls await Promise.all( pendingImages.value.map((p) uploadApi.image(p.file).then((r) (r r.url) || ) ) ) // 組裝參數(shù)提交后端 await gainStore.add({ content, imageUrls, audioUrl, audioDur, tags })3. 視圖渲染與預(yù)覽刪除!-- 列表展示從 v-if 升級(jí)為 v-for 循環(huán)渲染 -- img v-for(u, i) in g.imageUrls :keyi :srcu alt圖片 /預(yù)覽區(qū)移除單張圖片邏輯直接對(duì)本地待上傳數(shù)組調(diào)用pendingImages.value.splice(i, 1)即可。四、 通用套路總結(jié)步驟維度 單圖舊 多圖新①數(shù)據(jù)存儲(chǔ)數(shù)據(jù)庫(kù)字段存單個(gè) URL 字符串?dāng)?shù)據(jù)庫(kù)字段存JSON 數(shù)組字符串②上傳接口不動(dòng)保持原樣仍然是“一次一張、返回 URL”③DTO / VO 邊界String imageUrlListString imageUrls(序列化 / 反序列化在 Service 邊界完成)④前端處理ref(null) 單次上傳ref([])multiplePromise.all批量并發(fā) v-for