feat(tenant-platform): editable changelog with audit trail in version table

Adds an edit button (pencil icon) on each row's changelog cell.
Clicking it opens a dialog pre-filled with the current text; saving
calls PATCH /app/{id}/changelog and refreshes the list and op-log.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-05-18 16:48:20 +08:00
父节点 7d100a9efc
当前提交 416783047c
共有 2 个文件被更改,包括 57 次插入2 次删除

查看文件

@ -236,6 +236,10 @@ export const updateAdminApi = {
return updateClient.post(`/api/v1/updates/app/${id}/unpublish`, { reason }) return updateClient.post(`/api/v1/updates/app/${id}/unpublish`, { reason })
}, },
patchChangeLog(id: string, changeLog: string) {
return updateClient.patch<{ data: AppVersion }>(`/api/v1/updates/app/${id}/changelog`, { changeLog })
},
grayAppVersion(id: string, body: { grayAppVersion(id: string, body: {
enabled: boolean enabled: boolean
grayMode: GrayMode grayMode: GrayMode

查看文件

@ -106,7 +106,16 @@
</el-tag> </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="changeLog" label="更新说明" show-overflow-tooltip /> <el-table-column label="更新说明" min-width="160">
<template #default="{row}">
<el-tooltip :content="row.changeLog || '(无)'" placement="top" :disabled="!row.changeLog">
<span class="changelog-text">{{ row.changeLog || '—' }}</span>
</el-tooltip>
<el-button link type="primary" size="small" style="margin-left:4px" @click="openChangelogEdit(row)">
<el-icon><Edit /></el-icon>
</el-button>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="上传时间" width="160"> <el-table-column prop="createdAt" label="上传时间" width="160">
<template #default="{row}">{{ formatTime(row.createdAt) }}</template> <template #default="{row}">{{ formatTime(row.createdAt) }}</template>
</el-table-column> </el-table-column>
@ -670,6 +679,22 @@
</template> </template>
</el-dialog> </el-dialog>
<!-- Changelog Edit Dialog -->
<el-dialog v-model="showChangelogEdit" title="修改更新说明" width="480px">
<el-input
v-model="changelogEditValue"
type="textarea"
:rows="5"
placeholder="请输入更新说明(可留空)"
maxlength="2000"
show-word-limit
/>
<template #footer>
<el-button @click="showChangelogEdit = false">取消</el-button>
<el-button type="primary" :loading="savingChangelog" @click="submitChangelogEdit">保存</el-button>
</template>
</el-dialog>
<!-- Store Credential Config Dialog --> <!-- Store Credential Config Dialog -->
<el-dialog <el-dialog
v-model="showStoreConfig" v-model="showStoreConfig"
@ -880,7 +905,7 @@
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue' import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { CircleCheckFilled, Loading, UploadFilled, WarningFilled } from '@element-plus/icons-vue' import { CircleCheckFilled, Edit, Loading, UploadFilled, WarningFilled } from '@element-plus/icons-vue'
import { appApi, type App } from '@/api/app' import { appApi, type App } from '@/api/app'
import { fileApi } from '@/api/file' import { fileApi } from '@/api/file'
import { import {
@ -1471,6 +1496,32 @@ const publishScheduleType = ref<'IMMEDIATE' | 'SCHEDULED'>('IMMEDIATE')
const publishScheduleAt = ref('') const publishScheduleAt = ref('')
const updatingPublishSchedule = ref(false) const updatingPublishSchedule = ref(false)
const showChangelogEdit = ref(false)
const changelogEditId = ref('')
const changelogEditValue = ref('')
const savingChangelog = ref(false)
function openChangelogEdit(row: AppVersion) {
changelogEditId.value = row.id
changelogEditValue.value = row.changeLog ?? ''
showChangelogEdit.value = true
}
async function submitChangelogEdit() {
savingChangelog.value = true
try {
await updateAdminApi.patchChangeLog(changelogEditId.value, changelogEditValue.value)
ElMessage.success('更新说明已保存')
showChangelogEdit.value = false
await loadAppVersions()
await loadOperationLogs()
} catch {
ElMessage.error('保存失败')
} finally {
savingChangelog.value = false
}
}
async function handleCancelReview(storeType?: string) { async function handleCancelReview(storeType?: string) {
if (!storeReviewDetailVersion.value) return if (!storeReviewDetailVersion.value) return
const stores = storeType const stores = storeType