fix: 优化云端变量记录页面样式,对齐其他表格页面
- 移除ID列的font-mono text-xs样式,改用与其他页面一致的truncate样式 - 移除toolbar顶部多余的批量删除按钮,只保留BulkActions组件内的 - 搜索框和日期选择器尺寸对齐其他页面(h-8) - 日期格式化对齐其他页面(zh-CN格式) - 动态字段值显示改用truncate+title,替代pre标签 - 移除raw_data调试字段和诊断卡片
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ColumnDef } from '@tanstack/vue-table'
|
||||
|
||||
import { List, Loader2, Search, Trash2 } from 'lucide-vue-next'
|
||||
import { List, Search, Trash2 } from 'lucide-vue-next'
|
||||
import { computed, h, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
@@ -30,7 +30,6 @@ interface Variable {
|
||||
interface VariableRecord {
|
||||
id: number
|
||||
data: any
|
||||
raw_data?: string
|
||||
user_id?: number
|
||||
user?: {
|
||||
id: number
|
||||
@@ -40,16 +39,7 @@ interface VariableRecord {
|
||||
}
|
||||
|
||||
function parseRecordData(record: VariableRecord): Record<string, any> {
|
||||
let rawData = record.data
|
||||
|
||||
if ((rawData == null || rawData === '') && record.raw_data) {
|
||||
try {
|
||||
rawData = JSON.parse(record.raw_data)
|
||||
}
|
||||
catch {
|
||||
rawData = record.raw_data
|
||||
}
|
||||
}
|
||||
const rawData = record.data
|
||||
|
||||
if (rawData == null || rawData === '') return {}
|
||||
|
||||
@@ -118,24 +108,13 @@ const dynamicKeys = computed(() => {
|
||||
return Array.from(keys).sort()
|
||||
})
|
||||
|
||||
const debugInfo = computed(() => {
|
||||
if (records.value.length === 0 || dynamicKeys.value.length > 0) return null
|
||||
const first = records.value[0]
|
||||
return {
|
||||
dataType: first.data === null ? 'null' : first.data === undefined ? 'undefined' : typeof first.data,
|
||||
dataValue: first.data === null ? 'null' : first.data === undefined ? 'undefined' : typeof first.data === 'object' ? JSON.stringify(first.data) : String(first.data),
|
||||
rawDataField: first.raw_data === undefined ? '(字段不存在)' : first.raw_data === '' ? '(空字符串)' : first.raw_data || '(falsy)',
|
||||
parsedKeys: (() => { const p = parseRecordData(first); return p && typeof p === 'object' ? Object.keys(p) : [] })(),
|
||||
}
|
||||
})
|
||||
|
||||
const columns = computed<ColumnDef<VariableRecord>[]>(() => {
|
||||
const cols: ColumnDef<VariableRecord>[] = [SelectColumn as ColumnDef<VariableRecord>]
|
||||
|
||||
cols.push({
|
||||
accessorKey: 'id',
|
||||
header: () => 'ID',
|
||||
cell: ({ row }) => h('span', { class: 'text-muted-foreground font-mono text-xs' }, String(row.original.id)),
|
||||
cell: ({ row }) => h('div', { class: 'max-w-xs truncate' }, String(row.original.id)),
|
||||
size: 80,
|
||||
})
|
||||
|
||||
@@ -169,12 +148,12 @@ const columns = computed<ColumnDef<VariableRecord>[]>(() => {
|
||||
return h('span', { class: 'text-muted-foreground' }, '-')
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return h('pre', { class: 'text-xs bg-muted px-2 py-1 rounded max-w-xs overflow-auto max-h-20' }, JSON.stringify(value, null, 2))
|
||||
return h('div', { class: 'max-w-xs truncate', title: JSON.stringify(value) }, JSON.stringify(value))
|
||||
}
|
||||
if (typeof value === 'boolean') {
|
||||
return h(Badge, { variant: value ? 'default' : 'secondary' }, () => String(value))
|
||||
}
|
||||
return h('span', { class: 'truncate max-w-xs block', title: String(value) }, String(value))
|
||||
return h('div', { class: 'max-w-xs truncate', title: String(value) }, String(value))
|
||||
},
|
||||
size: 150,
|
||||
})
|
||||
@@ -189,7 +168,7 @@ const columns = computed<ColumnDef<VariableRecord>[]>(() => {
|
||||
if (!data || Object.keys(data).length === 0) {
|
||||
return h('span', { class: 'text-muted-foreground' }, '-')
|
||||
}
|
||||
return h('pre', { class: 'text-xs bg-muted px-2 py-1 rounded max-w-md overflow-auto max-h-20' }, JSON.stringify(data, null, 2))
|
||||
return h('div', { class: 'max-w-md truncate', title: JSON.stringify(data) }, JSON.stringify(data))
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -203,7 +182,13 @@ const columns = computed<ColumnDef<VariableRecord>[]>(() => {
|
||||
try {
|
||||
const date = new Date(createdAt)
|
||||
if (Number.isNaN(date.getTime())) return '-'
|
||||
return h('span', { class: 'text-muted-foreground text-xs' }, date.toLocaleString())
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
}
|
||||
catch {
|
||||
return '-'
|
||||
@@ -440,35 +425,11 @@ onMounted(async () => {
|
||||
</UiCard>
|
||||
</div>
|
||||
|
||||
<div v-if="debugInfo" class="border border-dashed border-yellow-500/50 bg-yellow-500/5 rounded-md p-4 text-sm">
|
||||
<p class="font-medium text-yellow-600 mb-2">数据诊断 (Data Debug)</p>
|
||||
<div class="grid grid-cols-1 gap-2 text-xs font-mono">
|
||||
<div>data 类型: <span class="text-yellow-700">{{ debugInfo.dataType }}</span></div>
|
||||
<div>data 值: <span class="text-yellow-700 break-all">{{ debugInfo.dataValue.substring(0, 500) }}</span></div>
|
||||
<div>raw_data (数据库原始): <span class="text-yellow-700 break-all">{{ debugInfo.rawDataField.substring(0, 500) }}</span></div>
|
||||
<div>解析后 keys: <span class="text-yellow-700">{{ debugInfo.parsedKeys.length > 0 ? debugInfo.parsedKeys.join(', ') : '(空)' }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UiCard>
|
||||
<UiCardContent class="p-6">
|
||||
<DataTable :columns="columns" :data="records" :loading="loading" :table="table" :server-pagination="serverPagination" @refresh="fetchRecords">
|
||||
<template #toolbar>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-end">
|
||||
<UiButton
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
:disabled="table.getSelectedRowModel().rows.length === 0"
|
||||
@click="openBatchDeleteDialog"
|
||||
>
|
||||
<Trash2 class="h-4 w-4 mr-1" />
|
||||
{{ t('admin.cloudVariables.records.batchDelete') }}
|
||||
<span v-if="table.getSelectedRowModel().rows.length > 0" class="ml-1">
|
||||
({{ table.getSelectedRowModel().rows.length }})
|
||||
</span>
|
||||
</UiButton>
|
||||
</div>
|
||||
<BulkActions :table="table" entity-name="records">
|
||||
<UiButton variant="destructive" size="sm" @click="openBatchDeleteDialog">
|
||||
<Trash2 class="h-4 w-4 mr-1" />
|
||||
@@ -482,23 +443,21 @@ onMounted(async () => {
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:placeholder="t('admin.cloudVariables.records.searchPlaceholder')"
|
||||
class="pl-8 w-[250px] h-9"
|
||||
class="pl-8 h-8 w-[150px] lg:w-[250px]"
|
||||
@keydown.enter="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
v-model="startDate"
|
||||
type="date"
|
||||
class="w-36 h-9"
|
||||
:placeholder="t('admin.cloudVariables.records.startDate')"
|
||||
class="h-8 w-[140px]"
|
||||
/>
|
||||
<Input
|
||||
v-model="endDate"
|
||||
type="date"
|
||||
class="w-36 h-9"
|
||||
:placeholder="t('admin.cloudVariables.records.endDate')"
|
||||
class="h-8 w-[140px]"
|
||||
/>
|
||||
<Button variant="outline" size="sm" class="h-9" @click="handleSearch">
|
||||
<Button variant="outline" size="sm" class="h-8" @click="handleSearch">
|
||||
<Search class="h-4 w-4 mr-1" />
|
||||
{{ t('admin.cloudVariables.records.search') }}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user