feat: add delete log comfirm

This commit is contained in:
engigu
2026-03-09 22:08:25 +08:00
parent 4d910b15cc
commit 564f39133f
7 changed files with 138 additions and 31 deletions
+2 -1
View File
@@ -601,7 +601,8 @@ export interface AppLogListResponse {
export const LOG_CATEGORY = { export const LOG_CATEGORY = {
SYSTEM_NOTICE: 'system_notice', SYSTEM_NOTICE: 'system_notice',
PUSH_LOG: 'push_log' PUSH_LOG: 'push_log',
LOGIN_LOG: 'login_log'
} as const } as const
export const LOG_LEVEL = { export const LOG_LEVEL = {
+2 -2
View File
@@ -78,7 +78,7 @@
--accent: oklch(0.97 0 0); --accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0); --accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325); --destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325); --destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.922 0 0); --border: oklch(0.922 0 0);
--input: oklch(0.922 0 0); --input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0); --ring: oklch(0.708 0 0);
@@ -114,7 +114,7 @@
--accent: oklch(0.32 0 0); --accent: oklch(0.32 0 0);
--accent-foreground: oklch(0.985 0 0); --accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216); --destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: oklch(0.637 0.237 25.331); --destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 12%); --border: oklch(1 0 0 / 12%);
--input: oklch(1 0 0 / 15%); --input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0); --ring: oklch(0.556 0 0);
@@ -4,15 +4,20 @@ import type { HTMLAttributes } from "vue"
import { reactiveOmit } from "@vueuse/core" import { reactiveOmit } from "@vueuse/core"
import { AlertDialogAction } from "reka-ui" import { AlertDialogAction } from "reka-ui"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { buttonVariants } from '@/components/ui/button' import { buttonVariants, type ButtonVariants } from '@/components/ui/button'
const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes["class"] }>() const props = defineProps<AlertDialogActionProps & {
class?: HTMLAttributes["class"]
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
}>()
const delegatedProps = reactiveOmit(props, "class") const delegatedProps = reactiveOmit(props, "class", "variant", "size")
</script> </script>
<template> <template>
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)"> <AlertDialogAction v-bind="delegatedProps"
:class="cn(buttonVariants({ variant: props.variant, size: props.size }), props.class)">
<slot /> <slot />
</AlertDialogAction> </AlertDialogAction>
</template> </template>
+2 -4
View File
@@ -588,8 +588,7 @@ watch(() => route.query, (newQuery) => {
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel> <AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction @click="handleClearLogs" <AlertDialogAction @click="handleClearLogs" variant="destructive">
class="bg-red-500 text-white hover:bg-red-600 dark:bg-red-600 dark:text-white dark:hover:bg-red-700">
清空 清空
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
@@ -607,8 +606,7 @@ watch(() => route.query, (newQuery) => {
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel> <AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction @click="handleDeleteLog" <AlertDialogAction @click="handleDeleteLog" variant="destructive">
class="bg-red-500 text-white hover:bg-red-600 dark:bg-red-600 dark:text-white dark:hover:bg-red-700">
删除 删除
</AlertDialogAction> </AlertDialogAction>
</AlertDialogFooter> </AlertDialogFooter>
+50 -2
View File
@@ -3,7 +3,18 @@ import { ref, onMounted } from 'vue'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import Pagination from '@/components/Pagination.vue' import Pagination from '@/components/Pagination.vue'
import { RefreshCw, Search, Loader2 } from 'lucide-vue-next' import { RefreshCw, Search, Loader2, Trash2 } from 'lucide-vue-next'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import { LOG_CATEGORY } from '@/api'
import TextOverflow from '@/components/TextOverflow.vue' import TextOverflow from '@/components/TextOverflow.vue'
import { api } from '@/api' import { api } from '@/api'
import { toast } from 'vue-sonner' import { toast } from 'vue-sonner'
@@ -48,6 +59,7 @@ const filterUsername = ref('')
const currentPage = ref(1) const currentPage = ref(1)
const total = ref(0) const total = ref(0)
const loading = ref(false) const loading = ref(false)
const showClearConfirm = ref(false)
let searchTimer: ReturnType<typeof setTimeout> | null = null let searchTimer: ReturnType<typeof setTimeout> | null = null
// IP 地理位置弹窗 // IP 地理位置弹窗
@@ -103,6 +115,17 @@ function handlePageChange(page: number) {
loadLogs() loadLogs()
} }
async function handleClear() {
try {
await api.appLogs.clear(LOG_CATEGORY.LOGIN_LOG)
toast.success('清空成功')
currentPage.value = 1
loadLogs()
} catch (e: any) {
toast.error('清空失败: ' + (e.message || ''))
}
}
onMounted(loadLogs) onMounted(loadLogs)
</script> </script>
@@ -115,9 +138,34 @@ onMounted(loadLogs)
<Input v-model="filterUsername" placeholder="搜索用户名..." class="h-9 pl-9 w-full sm:w-56 text-sm" <Input v-model="filterUsername" placeholder="搜索用户名..." class="h-9 pl-9 w-full sm:w-56 text-sm"
@input="handleSearch" /> @input="handleSearch" />
</div> </div>
<Button variant="outline" size="icon" class="h-9 w-9 shrink-0" @click="loadLogs" :disabled="loading"> <div class="flex items-center gap-2">
<Button variant="outline" size="icon" class="h-9 w-9 shrink-0" @click="loadLogs" :disabled="loading"
title="刷新">
<RefreshCw class="h-4 w-4" :class="{ 'animate-spin': loading }" /> <RefreshCw class="h-4 w-4" :class="{ 'animate-spin': loading }" />
</Button> </Button>
<AlertDialog :open="showClearConfirm" @update:open="showClearConfirm = $event">
<Button variant="outline"
class="h-9 px-4 shrink-0 text-sm text-destructive hover:bg-destructive/10 hover:text-destructive border-destructive/20"
@click="showClearConfirm = true">
<Trash2 class="h-4 w-4 sm:mr-2" /> <span class="hidden sm:inline"
style="padding-left: 2px;">清空记录</span>
</Button>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>确认清空所有登录日志</AlertDialogTitle>
<AlertDialogDescription>
此操作将永久清空所有记录操作后无法恢复确认要继续吗
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction @click="handleClear" variant="destructive">
确认清空
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div> </div>
</div> </div>
@@ -23,6 +23,16 @@ import { format } from 'date-fns'
import { import {
RefreshCw, Trash2, Search, Info, AlertTriangle, AlertCircle RefreshCw, Trash2, Search, Info, AlertTriangle, AlertCircle
} from 'lucide-vue-next' } from 'lucide-vue-next'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import { useSiteSettings } from '@/composables/useSiteSettings' import { useSiteSettings } from '@/composables/useSiteSettings'
const { pageSize } = useSiteSettings() const { pageSize } = useSiteSettings()
@@ -31,6 +41,7 @@ const logs = ref<AppLog[]>([])
const selectedLogId = ref<string | null>(null) const selectedLogId = ref<string | null>(null)
const total = ref(0) const total = ref(0)
const loading = ref(false) const loading = ref(false)
const showClearConfirm = ref(false)
const filters = ref({ const filters = ref({
level: 'all', level: 'all',
@@ -182,11 +193,28 @@ function onDialogClose(open: boolean) {
<RefreshCw class="h-4 w-4" :class="{ 'animate-spin': loading }" /> <RefreshCw class="h-4 w-4" :class="{ 'animate-spin': loading }" />
</Button> </Button>
</div> </div>
<AlertDialog :open="showClearConfirm" @update:open="showClearConfirm = $event">
<Button variant="outline" <Button variant="outline"
class="h-9 px-4 shrink-0 text-sm text-destructive hover:bg-destructive/10 hover:text-destructive border-destructive/20" class="h-9 px-4 shrink-0 text-sm text-destructive hover:bg-destructive/10 hover:text-destructive border-destructive/20"
@click="handleClear"> @click="showClearConfirm = true">
<Trash2 class="h-4 w-4 sm:mr-2" /> <span class="hidden sm:inline" style="padding-left: 2px;">清空记录</span> <Trash2 class="h-4 w-4 sm:mr-2" /> <span class="hidden sm:inline"
style="padding-left: 2px;">清空记录</span>
</Button> </Button>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>确认清空所有系统事件</AlertDialogTitle>
<AlertDialogDescription>
此操作将永久清空当前分类下的所有系统事件记录操作后无法恢复确认要继续吗
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction @click="handleClear" variant="destructive">
确认清空
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div> </div>
<div class="rounded-lg border bg-card overflow-x-auto"> <div class="rounded-lg border bg-card overflow-x-auto">
+28 -1
View File
@@ -23,6 +23,16 @@ import { format } from 'date-fns'
import { import {
RefreshCw, Trash2, Check, X, Search RefreshCw, Trash2, Check, X, Search
} from 'lucide-vue-next' } from 'lucide-vue-next'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import { useSiteSettings } from '@/composables/useSiteSettings' import { useSiteSettings } from '@/composables/useSiteSettings'
@@ -32,6 +42,7 @@ const logs = ref<AppLog[]>([])
const selectedLogId = ref<string | null>(null) const selectedLogId = ref<string | null>(null)
const total = ref(0) const total = ref(0)
const loading = ref(false) const loading = ref(false)
const showClearConfirm = ref(false)
const filters = ref({ const filters = ref({
status: 'all', status: 'all',
@@ -172,11 +183,27 @@ function onDialogClose(open: boolean) {
<RefreshCw class="h-4 w-4" :class="{ 'animate-spin': loading }" /> <RefreshCw class="h-4 w-4" :class="{ 'animate-spin': loading }" />
</Button> </Button>
</div> </div>
<AlertDialog :open="showClearConfirm" @update:open="showClearConfirm = $event">
<Button variant="outline" <Button variant="outline"
class="h-9 px-4 shrink-0 text-sm text-destructive hover:bg-destructive/10 hover:text-destructive border-destructive/20" class="h-9 px-4 shrink-0 text-sm text-destructive hover:bg-destructive/10 hover:text-destructive border-destructive/20"
@click="handleClear"> @click="showClearConfirm = true">
<Trash2 class="h-4 w-4 sm:mr-2" /> <span class="hidden sm:inline" style="padding-left: 2px;">清空记录</span> <Trash2 class="h-4 w-4 sm:mr-2" /> <span class="hidden sm:inline" style="padding-left: 2px;">清空记录</span>
</Button> </Button>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>确认清空所有推送日志</AlertDialogTitle>
<AlertDialogDescription>
此操作将永久清空当前分类下的所有消息推送历史记录操作后无法恢复确认要继续吗
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction @click="handleClear" variant="destructive">
确认清空
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div> </div>
<div class="rounded-lg border bg-card overflow-x-auto"> <div class="rounded-lg border bg-card overflow-x-auto">