feat: 活动日志撤销+详情+排版优化
All checks were successful
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 6s
PR Preview / deploy-preview (pull_request) Successful in 16s
Test / e2e-test (push) Successful in 50s
All checks were successful
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 6s
PR Preview / deploy-preview (pull_request) Successful in 16s
Test / e2e-test (push) Successful in 50s
活动日志: - 删除配方/用户/精油加撤销按钮 - 编辑配方记录修改了哪些字段(名称/成分/标签/备注/英文名) - 创建标签记入日志 - 注册记入日志(已有) 配方卡片: - 精油按字母排序 - 容量移到名字后面 - 标签对viewer不可见 - 管理配方标签排序+容量显示 其他: - 管理员共享直接显示已共享 - 点击轮播清除搜索 - 编辑overlay防误关 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
<div class="log-detail">
|
||||
<span v-if="log.target_name" class="log-target-name">{{ log.target_name }}</span>
|
||||
<span v-if="parsedDetail(log)" class="log-extra">{{ parsedDetail(log) }}</span>
|
||||
<button v-if="canUndo(log)" class="undo-btn" @click="undoAction(log)">↩ 撤销</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="filteredLogs.length === 0" class="empty-hint">暂无日志记录</div>
|
||||
@@ -66,6 +67,10 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { api } from '../composables/useApi'
|
||||
import { showConfirm } from '../composables/useDialog'
|
||||
import { useUiStore } from '../stores/ui'
|
||||
|
||||
const ui = useUiStore()
|
||||
|
||||
const logs = ref([])
|
||||
const loading = ref(false)
|
||||
@@ -158,6 +163,7 @@ function parsedDetail(log) {
|
||||
if (d.from_user) parts.push(`来自: ${d.from_user}`)
|
||||
if (d.reason) parts.push(`原因: ${d.reason}`)
|
||||
if (d.business_name) parts.push(`商户: ${d.business_name}`)
|
||||
if (d.changed) parts.push(`修改: ${d.changed}`)
|
||||
if (d.display_name) parts.push(`显示名: ${d.display_name}`)
|
||||
if (d.original_log_id) parts.push(`恢复自 #${d.original_log_id}`)
|
||||
if (parts.length) return parts.join(' · ')
|
||||
@@ -169,6 +175,30 @@ function parsedDetail(log) {
|
||||
}
|
||||
}
|
||||
|
||||
function canUndo(log) {
|
||||
return ['delete_recipe', 'delete_user', 'delete_oil'].includes(log.action)
|
||||
}
|
||||
|
||||
async function undoAction(log) {
|
||||
const ok = await showConfirm(`确定撤销此操作?将恢复「${log.target_name}」`)
|
||||
if (!ok) return
|
||||
try {
|
||||
const res = await api(`/api/audit-log/${log.id}/undo`, { method: 'POST' })
|
||||
if (res.ok) {
|
||||
ui.showToast('已撤销')
|
||||
logs.value = []
|
||||
page.value = 0
|
||||
hasMore.value = true
|
||||
await fetchLogs()
|
||||
} else {
|
||||
const err = await res.json().catch(() => ({}))
|
||||
ui.showToast('撤销失败: ' + (err.detail || err.message || ''))
|
||||
}
|
||||
} catch {
|
||||
ui.showToast('撤销失败')
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(t) {
|
||||
if (!t) return ''
|
||||
return new Date(t + 'Z').toLocaleString('zh-CN', {
|
||||
@@ -236,6 +266,11 @@ onMounted(() => fetchLogs())
|
||||
.log-detail { font-size: 13px; color: #6b6375; margin-top: 2px; }
|
||||
.log-target-name { font-weight: 500; color: #3e3a44; margin-right: 8px; }
|
||||
.log-extra { color: #999; font-size: 12px; }
|
||||
.undo-btn {
|
||||
margin-left: 8px; padding: 2px 8px; border: 1px solid #d4cfc7; border-radius: 6px;
|
||||
background: #fff; font-size: 11px; cursor: pointer; color: #6b6375; font-family: inherit;
|
||||
}
|
||||
.undo-btn:hover { border-color: #7ec6a4; color: #4a9d7e; }
|
||||
|
||||
.load-more { text-align: center; margin-top: 16px; }
|
||||
.btn-outline {
|
||||
|
||||
Reference in New Issue
Block a user