From 79f3986f2f00bda4e6c4fe3390fb7f83205aac5e Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Thu, 16 Jul 2026 14:49:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E7=94=A8=E6=88=B7=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E7=89=88=E6=9C=AC=E6=96=B0=E5=BB=BA=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=BB=A7=E6=89=BF=E4=B8=8A=E4=B8=80=E7=89=88=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新建草稿默认继承同语言上一版,已发布/归档行支持复制为草稿,并补充多语种文案。 Co-authored-by: Cursor --- .../protocol/user-protocol-version.api.ts | 7 + .../locales/langs/en-US/system/protocol.json | 3 + .../locales/langs/id-ID/system/protocol.json | 3 + .../locales/langs/ja-JP/system/protocol.json | 3 + .../locales/langs/ko-KR/system/protocol.json | 3 + .../locales/langs/ms-MY/system/protocol.json | 3 + .../locales/langs/th-TH/system/protocol.json | 3 + .../locales/langs/vi-VN/system/protocol.json | 3 + .../locales/langs/zh-CN/system/protocol.json | 3 + .../locales/langs/zh-HK/system/protocol.json | 3 + .../locales/langs/zh-TW/system/protocol.json | 3 + .../protocol/UserProtocolVersionEdit.vue | 139 ++++++++++++++++-- .../protocol/UserProtocolVersionList.vue | 80 ++++++++-- 13 files changed, 233 insertions(+), 23 deletions(-) diff --git a/apps/daxpay-admin/src/api/system/protocol/user-protocol-version.api.ts b/apps/daxpay-admin/src/api/system/protocol/user-protocol-version.api.ts index e11d226a4..d722f99fd 100644 --- a/apps/daxpay-admin/src/api/system/protocol/user-protocol-version.api.ts +++ b/apps/daxpay-admin/src/api/system/protocol/user-protocol-version.api.ts @@ -18,6 +18,13 @@ export const UserProtocolVersionApi = { add(data: UserProtocolVersion): Promise> { return defHttp.post({ url: '/user/protocol/version/add', data }); }, + /** 查询新建草稿可继承的源版本(已发布优先, 否则最新归档) */ + findInheritSource(protocolId: string, language: string): Promise> { + return defHttp.get({ + url: '/user/protocol/version/find-inherit-source', + params: { protocolId, language }, + }); + }, /** 编辑草稿内容(仅草稿可编辑) */ update(data: UserProtocolVersion): Promise> { return defHttp.post({ url: '/user/protocol/version/update', data }); diff --git a/apps/daxpay-admin/src/locales/langs/en-US/system/protocol.json b/apps/daxpay-admin/src/locales/langs/en-US/system/protocol.json index 184534b1a..843248044 100644 --- a/apps/daxpay-admin/src/locales/langs/en-US/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/en-US/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "Archive this version?", "confirmDelete": "Are you sure to delete this draft?", "confirmClose": "The content has been changed. Close anyway? Unsaved changes will be lost.", + "createFromThis": "Copy as draft", + "confirmReplaceByLanguage": "Switching language will replace the current content. Continue?", + "inheritedTip": "Content loaded from the previous version. Edit and save as a new draft.", "statusDraft": "Draft", "statusPublished": "Published", "statusArchived": "Archived" diff --git a/apps/daxpay-admin/src/locales/langs/id-ID/system/protocol.json b/apps/daxpay-admin/src/locales/langs/id-ID/system/protocol.json index 387e45544..30865eb29 100644 --- a/apps/daxpay-admin/src/locales/langs/id-ID/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/id-ID/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "Arsipkan versi ini?", "confirmDelete": "Apakah Anda yakin akan menghapus draf ini?", "confirmClose": "Kontennya telah diubah. Tutup saja? Perubahan yang belum disimpan akan hilang.", + "createFromThis": "Salin sebagai draf", + "confirmReplaceByLanguage": "Mengganti bahasa akan menimpa konten saat ini. Lanjutkan?", + "inheritedTip": "Konten dimuat dari versi sebelumnya. Edit lalu simpan sebagai draf baru.", "statusDraft": "Draf", "statusPublished": "Diterbitkan", "statusArchived": "Diarsipkan" diff --git a/apps/daxpay-admin/src/locales/langs/ja-JP/system/protocol.json b/apps/daxpay-admin/src/locales/langs/ja-JP/system/protocol.json index 3b115c691..ebd20bf4d 100644 --- a/apps/daxpay-admin/src/locales/langs/ja-JP/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/ja-JP/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "このバージョンをアーカイブしますか?", "confirmDelete": "この下書きを削除してもよろしいですか?", "confirmClose": "内容が変更になりました。とにかく閉じますか?保存されていない変更は失われます。", + "createFromThis": "下書きにコピー", + "confirmReplaceByLanguage": "言語を切り替えると現在の本文が上書きされます。続けますか?", + "inheritedTip": "前のバージョンから内容を読み込みました。編集して新しい下書きとして保存できます", "statusDraft": "下書き", "statusPublished": "発行済み", "statusArchived": "アーカイブ済み" diff --git a/apps/daxpay-admin/src/locales/langs/ko-KR/system/protocol.json b/apps/daxpay-admin/src/locales/langs/ko-KR/system/protocol.json index 902c1a32c..08be03733 100644 --- a/apps/daxpay-admin/src/locales/langs/ko-KR/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/ko-KR/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "이 버전을 보관처리하시겠습니까?", "confirmDelete": "이 초안을 삭제하시겠습니까?", "confirmClose": "내용이 변경되었습니다. 그래도 닫으시겠어요? 저장되지 않은 변경사항은 손실됩니다.", + "createFromThis": "초안으로 복사", + "confirmReplaceByLanguage": "언어를 바꾸면 현재 본문이 덮어씌워집니다. 계속할까요?", + "inheritedTip": "이전 버전에서 내용을 불러왔습니다. 수정 후 새 초안으로 저장하세요", "statusDraft": "초안", "statusPublished": "게시됨", "statusArchived": "보관됨" diff --git a/apps/daxpay-admin/src/locales/langs/ms-MY/system/protocol.json b/apps/daxpay-admin/src/locales/langs/ms-MY/system/protocol.json index f1e33dedd..8ee4ae653 100644 --- a/apps/daxpay-admin/src/locales/langs/ms-MY/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/ms-MY/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "Arkibkan versi ini?", "confirmDelete": "Adakah anda pasti akan memadamkan draf ini?", "confirmClose": "Kandungan telah diubah. Tutup pula? Perubahan yang tidak disimpan akan hilang.", + "createFromThis": "Salin sebagai draf", + "confirmReplaceByLanguage": "Menukar bahasa akan menggantikan kandungan semasa. Teruskan?", + "inheritedTip": "Kandungan dimuatkan dari versi sebelumnya. Edit lalu simpan sebagai draf baharu.", "statusDraft": "Draf", "statusPublished": "Diterbitkan", "statusArchived": "Diarkibkan" diff --git a/apps/daxpay-admin/src/locales/langs/th-TH/system/protocol.json b/apps/daxpay-admin/src/locales/langs/th-TH/system/protocol.json index b8fec00c7..0d3b70ad7 100644 --- a/apps/daxpay-admin/src/locales/langs/th-TH/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/th-TH/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "เก็บเวอร์ชันนี้ไหม", "confirmDelete": "คุณแน่ใจหรือว่าจะลบฉบับร่างนี้", "confirmClose": "เนื้อหามีการเปลี่ยนแปลง ปิดรึยังคะ? การเปลี่ยนแปลงที่ไม่ได้บันทึกจะสูญหาย", + "createFromThis": "คัดลอกเป็นร่าง", + "confirmReplaceByLanguage": "เปลี่ยนภาษาจะเขียนทับเนื้อหาปัจจุบัน ดำเนินการต่อหรือไม่?", + "inheritedTip": "โหลดเนื้อหาจากรุ่นก่อนแล้ว แก้ไขแล้วบันทึกเป็นร่างใหม่ได้", "statusDraft": "ร่าง", "statusPublished": "ที่ตีพิมพ์", "statusArchived": "เก็บถาวรแล้ว" diff --git a/apps/daxpay-admin/src/locales/langs/vi-VN/system/protocol.json b/apps/daxpay-admin/src/locales/langs/vi-VN/system/protocol.json index a390746b6..b1c40444d 100644 --- a/apps/daxpay-admin/src/locales/langs/vi-VN/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/vi-VN/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "Lưu trữ phiên bản này?", "confirmDelete": "Bạn có chắc chắn xóa bản nháp này không?", "confirmClose": "Nội dung đã được thay đổi. Vẫn đóng? Những thay đổi chưa được lưu sẽ bị mất.", + "createFromThis": "Sao chép thành bản nháp", + "confirmReplaceByLanguage": "Đổi ngôn ngữ sẽ ghi đè nội dung hiện tại. Tiếp tục?", + "inheritedTip": "Đã tải nội dung từ phiên bản trước. Chỉnh sửa rồi lưu thành bản nháp mới.", "statusDraft": "Bản nháp", "statusPublished": "Đã xuất bản", "statusArchived": "Đã lưu trữ" diff --git a/apps/daxpay-admin/src/locales/langs/zh-CN/system/protocol.json b/apps/daxpay-admin/src/locales/langs/zh-CN/system/protocol.json index ff5a54c6f..7cd3adbcb 100644 --- a/apps/daxpay-admin/src/locales/langs/zh-CN/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/zh-CN/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "确认归档该版本吗?", "confirmDelete": "确认删除该草稿吗?", "confirmClose": "协议内容已有改动,确认关闭吗?关闭后未保存的内容将丢失。", + "createFromThis": "复制为草稿", + "confirmReplaceByLanguage": "切换语言将覆盖当前正文,是否继续?", + "inheritedTip": "已从上一版本载入内容,可直接修改后保存为新草稿", "statusDraft": "草稿", "statusPublished": "已发布", "statusArchived": "已归档" diff --git a/apps/daxpay-admin/src/locales/langs/zh-HK/system/protocol.json b/apps/daxpay-admin/src/locales/langs/zh-HK/system/protocol.json index 88f8bce6c..af1729481 100644 --- a/apps/daxpay-admin/src/locales/langs/zh-HK/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/zh-HK/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "確認歸檔該版本嗎?", "confirmDelete": "確認刪除該草稿嗎?", "confirmClose": "協議內容已有改動,確認關閉嗎?關閉後未保存的內容將丟失。", + "createFromThis": "複製為草稿", + "confirmReplaceByLanguage": "切換語言將覆蓋當前正文,是否繼續?", + "inheritedTip": "已從上一版本載入內容,可直接修改後保存為新草稿", "statusDraft": "草稿", "statusPublished": "已發佈", "statusArchived": "已歸檔" diff --git a/apps/daxpay-admin/src/locales/langs/zh-TW/system/protocol.json b/apps/daxpay-admin/src/locales/langs/zh-TW/system/protocol.json index bd8c5e63d..e3aebc9eb 100644 --- a/apps/daxpay-admin/src/locales/langs/zh-TW/system/protocol.json +++ b/apps/daxpay-admin/src/locales/langs/zh-TW/system/protocol.json @@ -48,6 +48,9 @@ "confirmArchive": "確認歸檔該版本嗎?", "confirmDelete": "確認刪除該草稿嗎?", "confirmClose": "協議內容已有改動,確認關閉嗎?關閉後未儲存的內容將丟失。", + "createFromThis": "複製為草稿", + "confirmReplaceByLanguage": "切換語言將覆蓋當前正文,是否繼續?", + "inheritedTip": "已從上一版本載入內容,可直接修改後儲存為新草稿", "statusDraft": "草稿", "statusPublished": "已釋出", "statusArchived": "已歸檔" diff --git a/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionEdit.vue b/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionEdit.vue index ad81af6fe..a9ffa2eb3 100644 --- a/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionEdit.vue +++ b/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionEdit.vue @@ -30,6 +30,12 @@ }); // 协议正文初始内容(用于关闭时检测是否有未保存修改) const loadedContent = ref(''); + // 最近一次继承载入的正文(用于语言切换时判断是否可自动覆盖) + const inheritedContent = ref(''); + // 是否已从上一版本载入内容(展示提示) + const inherited = ref(false); + // 语言切换时用于回滚的上一次语言 + const previousLanguage = ref('zh-CN'); // 语言选项(与 SUPPORT_LANGUAGES 对齐) const languageOptions = [ @@ -53,27 +59,132 @@ content: [{ required: true, message: $t('system.protocol.version.inputContent') }], }; - /** 入口 */ - function init(protocolId: string, id: string | undefined, editType: FormEditType) { + /** + * 入口 + * @param protocolId 协议ID + * @param id 版本ID(编辑/查看时) + * @param editType 表单模式 + * @param sourceVersionId 基于指定版本新建时传入源版本ID + */ + function init(protocolId: string, id: string | undefined, editType: FormEditType, sourceVersionId?: string) { initFormEditType(editType); resetForm(); form.value.protocolId = protocolId; - getInfo(id, editType); + previousLanguage.value = form.value.language ?? 'zh-CN'; + getInfo(id, editType, sourceVersionId); + } + + /** 将源版本字段预填到新建草稿表单(不带 id/状态/版本号, summary 清空) */ + function applyInheritSource(source: UserProtocolVersion, protocolId: string) { + const content = source.content ?? ''; + form.value = { + protocolId, + language: source.language || 'zh-CN', + contentFormat: source.contentFormat || 'MARKDOWN', + versionLabel: source.versionLabel, + title: source.title, + content, + contentHtml: source.contentHtml ?? '', + // 变更说明需重写 + summary: '', + }; + inheritedContent.value = content; + loadedContent.value = content; + inherited.value = true; + previousLanguage.value = form.value.language ?? 'zh-CN'; + } + + /** 按协议+语言拉取可继承源版本并预填; 无源则保持空白 */ + async function loadInheritByLanguage(protocolId: string, language: string) { + const { data } = await UserProtocolVersionApi.findInheritSource(protocolId, language); + if (data) { + applyInheritSource(data, protocolId); + // 保持用户刚切换的目标语言(源可能同语言) + form.value.language = language; + previousLanguage.value = language; + } else { + form.value.language = language; + form.value.title = undefined; + form.value.versionLabel = undefined; + form.value.content = ''; + form.value.contentHtml = ''; + form.value.summary = ''; + form.value.contentFormat = 'MARKDOWN'; + inheritedContent.value = ''; + loadedContent.value = ''; + inherited.value = false; + previousLanguage.value = language; + } } /** 获取信息 */ - function getInfo(id: string | undefined, editType: FormEditType) { + function getInfo(id: string | undefined, editType: FormEditType, sourceVersionId?: string) { if ([FormEditType.Edit, FormEditType.Show].includes(editType) && id) { confirmLoading.value = true; - UserProtocolVersionApi.findById(id).then(({ data }) => { - form.value = data; - // 记录正文初始值, 用于关闭时检测变动 - loadedContent.value = data.content ?? ''; + UserProtocolVersionApi.findById(id) + .then(({ data }) => { + form.value = data; + // 记录正文初始值, 用于关闭时检测变动 + loadedContent.value = data.content ?? ''; + inheritedContent.value = ''; + inherited.value = false; + previousLanguage.value = data.language ?? 'zh-CN'; + }) + .finally(() => { + confirmLoading.value = false; + }); + return; + } + + // 新建: 基于指定版本 或 默认继承同语言上一版 + if (editType === FormEditType.Add) { + const protocolId = form.value.protocolId!; + confirmLoading.value = true; + const loadPromise = sourceVersionId + ? UserProtocolVersionApi.findById(sourceVersionId).then(({ data }) => { + applyInheritSource(data, protocolId); + }) + : loadInheritByLanguage(protocolId, form.value.language || 'zh-CN'); + loadPromise.finally(() => { confirmLoading.value = false; }); - } else { - confirmLoading.value = false; + return; } + + confirmLoading.value = false; + } + + /** 新建模式下切换语言: 未改过继承正文则自动覆盖, 否则二次确认 */ + function handleLanguageChange(language: string) { + if (formEditType.value !== FormEditType.Add || !form.value.protocolId) { + previousLanguage.value = language; + return; + } + const content = form.value.content ?? ''; + const canAutoReplace = !content || content === inheritedContent.value; + if (canAutoReplace) { + confirmLoading.value = true; + loadInheritByLanguage(form.value.protocolId, language).finally(() => { + confirmLoading.value = false; + }); + return; + } + // 正文已手动改过, 确认后再覆盖 + confirm({ + title: $t('common.warning'), + // 切换语言将覆盖当前正文 + content: $t('system.protocol.version.confirmReplaceByLanguage'), + onOk: () => { + confirmLoading.value = true; + return loadInheritByLanguage(form.value.protocolId!, language).finally(() => { + confirmLoading.value = false; + }); + }, + onCancel: () => { + // 取消时回滚语言选择 + form.value.language = previousLanguage.value; + }, + }); } /** 重置表单 */ @@ -88,6 +199,9 @@ contentHtml: '', }; loadedContent.value = ''; + inheritedContent.value = ''; + inherited.value = false; + previousLanguage.value = 'zh-CN'; } /** Markdown 渲染后的HTML */ @@ -148,6 +262,10 @@ + +
+ +
@@ -160,6 +278,7 @@ :disabled="showable" :options="languageOptions" :placeholder="$t('common.pleaseSelect')" + @change="handleLanguageChange" /> diff --git a/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionList.vue b/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionList.vue index c931c2816..9dd6b65a7 100644 --- a/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionList.vue +++ b/apps/daxpay-admin/src/views/system/protocol/UserProtocolVersionList.vue @@ -1,8 +1,10 @@