feat: 重构云端变量记录页面,支持动态列和服务端分页
- 使用项目 DataTable 组件替换原生 table - 根据记录数据动态生成表格列 - 支持服务端分页、搜索、日期筛选 - 添加单条/批量删除确认对话框 - 添加路由配置和国际化文本 - 更新 pnpm-lock.yaml 修复 Docker 构建问题
This commit is contained in:
Generated
-1337
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Search, Trash2 } from 'lucide-vue-next'
|
import type { ColumnDef } from '@tanstack/vue-table'
|
||||||
import { onMounted, ref } from 'vue'
|
|
||||||
|
import { List, Loader2, 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'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
|
import ConfirmDialog from '@/components/confirm-dialog.vue'
|
||||||
|
import DataTable from '@/components/data-table/data-table.vue'
|
||||||
|
import { SelectColumn } from '@/components/data-table/table-columns'
|
||||||
|
import { generateVueTable } from '@/components/data-table/use-generate-vue-table'
|
||||||
import { BasicPage } from '@/components/global-layout'
|
import { BasicPage } from '@/components/global-layout'
|
||||||
|
import Badge from '@/components/ui/badge/Badge.vue'
|
||||||
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
import api from '@/services/api'
|
import api from '@/services/api'
|
||||||
|
|
||||||
interface Variable {
|
interface Variable {
|
||||||
@@ -28,16 +38,159 @@ interface VariableRecord {
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const variable = ref<Variable | null>(null)
|
const variable = ref<Variable | null>(null)
|
||||||
const records = ref<VariableRecord[]>([])
|
const records = ref<VariableRecord[]>([])
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
const pageSize = ref(20)
|
const pageSize = ref(10)
|
||||||
const startDate = ref('')
|
const startDate = ref('')
|
||||||
const endDate = ref('')
|
const endDate = ref('')
|
||||||
|
const searchQuery = ref('')
|
||||||
const selectedIds = ref<number[]>([])
|
const selectedIds = ref<number[]>([])
|
||||||
|
const deleteDialogOpen = ref(false)
|
||||||
|
const deleteTargetId = ref<number | null>(null)
|
||||||
|
const batchDeleteDialogOpen = ref(false)
|
||||||
|
|
||||||
|
const dynamicKeys = computed(() => {
|
||||||
|
const keys = new Set<string>()
|
||||||
|
records.value.forEach((record) => {
|
||||||
|
if (record.data && typeof record.data === 'object') {
|
||||||
|
Object.keys(record.data).forEach(key => keys.add(key))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return Array.from(keys)
|
||||||
|
})
|
||||||
|
|
||||||
|
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)),
|
||||||
|
size: 80,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (variable.value?.scope === 'user') {
|
||||||
|
cols.push({
|
||||||
|
accessorKey: 'user',
|
||||||
|
header: () => t('admin.cloudVariables.records.user'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const user = row.original.user
|
||||||
|
if (user) {
|
||||||
|
return h(Badge, { variant: 'outline' }, () => user.username)
|
||||||
|
}
|
||||||
|
return h('span', { class: 'text-muted-foreground' }, '-')
|
||||||
|
},
|
||||||
|
size: 120,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicKeys.value.forEach((key) => {
|
||||||
|
cols.push({
|
||||||
|
id: `data_${key}`,
|
||||||
|
header: () => key,
|
||||||
|
accessorFn: row => row.data?.[key],
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const value = row.original.data?.[key]
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return h(Badge, { variant: value ? 'default' : 'secondary' }, () => String(value))
|
||||||
|
}
|
||||||
|
return h('span', { class: 'truncate max-w-xs', title: String(value) }, String(value))
|
||||||
|
},
|
||||||
|
size: 150,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (dynamicKeys.value.length === 0) {
|
||||||
|
cols.push({
|
||||||
|
id: 'data',
|
||||||
|
header: () => t('admin.cloudVariables.records.data'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const data = row.original.data
|
||||||
|
if (!data) {
|
||||||
|
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))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
cols.push({
|
||||||
|
accessorKey: 'created_at',
|
||||||
|
header: () => t('admin.cloudVariables.records.createdAt'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdAt = row.original.created_at
|
||||||
|
if (!createdAt) return '-'
|
||||||
|
try {
|
||||||
|
const date = new Date(createdAt)
|
||||||
|
if (Number.isNaN(date.getTime())) return '-'
|
||||||
|
return h('span', { class: 'text-muted-foreground text-xs' }, date.toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
size: 160,
|
||||||
|
})
|
||||||
|
|
||||||
|
cols.push({
|
||||||
|
id: 'actions',
|
||||||
|
header: () => h('span', { class: 'sr-only' }, t('common.actions')),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return h(Button, {
|
||||||
|
variant: 'ghost',
|
||||||
|
size: 'icon',
|
||||||
|
class: 'h-8 w-8 text-destructive hover:text-destructive',
|
||||||
|
onClick: () => confirmDelete(row.original.id),
|
||||||
|
}, () => h(Trash2, { class: 'h-4 w-4' }))
|
||||||
|
},
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
size: 50,
|
||||||
|
})
|
||||||
|
|
||||||
|
return cols
|
||||||
|
})
|
||||||
|
|
||||||
|
const serverPagination = computed(() => ({
|
||||||
|
page: page.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
total: total.value,
|
||||||
|
onPageChange: (newPage: number) => {
|
||||||
|
page.value = newPage
|
||||||
|
},
|
||||||
|
onPageSizeChange: (newPageSize: number) => {
|
||||||
|
pageSize.value = newPageSize
|
||||||
|
page.value = 1
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const table = generateVueTable<VariableRecord>({
|
||||||
|
get data() { return records.value },
|
||||||
|
get loading() { return loading.value },
|
||||||
|
get columns() { return columns.value },
|
||||||
|
serverPagination: serverPagination.value,
|
||||||
|
})
|
||||||
|
|
||||||
|
watch([page, pageSize], () => {
|
||||||
|
fetchRecords()
|
||||||
|
})
|
||||||
|
|
||||||
async function fetchVariable() {
|
async function fetchVariable() {
|
||||||
try {
|
try {
|
||||||
@@ -45,7 +198,7 @@ async function fetchVariable() {
|
|||||||
const vars = data?.variables || []
|
const vars = data?.variables || []
|
||||||
variable.value = vars.find((v: Variable) => String(v.id) === route.params.id) || null
|
variable.value = vars.find((v: Variable) => String(v.id) === route.params.id) || null
|
||||||
if (variable.value && variable.value.var_type !== 'stream') {
|
if (variable.value && variable.value.var_type !== 'stream') {
|
||||||
toast.error('该变量不是记录类型')
|
toast.error(t('admin.cloudVariables.records.notStreamType'))
|
||||||
router.back()
|
router.back()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,18 +208,16 @@ async function fetchVariable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchRecords() {
|
async function fetchRecords() {
|
||||||
if (!variable.value)
|
if (!variable.value) return
|
||||||
return
|
|
||||||
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.append('page', String(page.value))
|
params.append('page', String(page.value))
|
||||||
params.append('page_size', String(pageSize.value))
|
params.append('page_size', String(pageSize.value))
|
||||||
if (startDate.value)
|
if (startDate.value) params.append('start_date', startDate.value)
|
||||||
params.append('start_date', startDate.value)
|
if (endDate.value) params.append('end_date', endDate.value)
|
||||||
if (endDate.value)
|
if (searchQuery.value) params.append('search', searchQuery.value)
|
||||||
params.append('end_date', endDate.value)
|
|
||||||
|
|
||||||
const data = await api.get<{ records: VariableRecord[], total: number }>(`/dev/cloud-variables/${variable.value.id}/records?${params}`)
|
const data = await api.get<{ records: VariableRecord[], total: number }>(`/dev/cloud-variables/${variable.value.id}/records?${params}`)
|
||||||
records.value = data?.records || []
|
records.value = data?.records || []
|
||||||
@@ -80,55 +231,61 @@ async function fetchRecords() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleDeleteSelected() {
|
function confirmDelete(id: number) {
|
||||||
if (selectedIds.value.length === 0) {
|
deleteTargetId.value = id
|
||||||
toast.error('请选择要删除的记录')
|
deleteDialogOpen.value = true
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
async function handleDelete() {
|
||||||
|
if (!deleteTargetId.value || !variable.value) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.delete(`/dev/cloud-variables/${variable.value?.id}/records`, {
|
await api.delete(`/dev/cloud-variables/${variable.value.id}/records`, {
|
||||||
ids: selectedIds.value,
|
ids: [deleteTargetId.value],
|
||||||
})
|
})
|
||||||
toast.success('删除成功')
|
toast.success(t('admin.cloudVariables.records.deleteSuccess'))
|
||||||
selectedIds.value = []
|
|
||||||
fetchRecords()
|
fetchRecords()
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
toast.error(error.message || '删除失败')
|
toast.error(error.message || t('admin.cloudVariables.records.deleteFailed'))
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
deleteTargetId.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelectAll() {
|
function openBatchDeleteDialog() {
|
||||||
if (selectedIds.value.length === records.value.length) {
|
const selected = table.getSelectedRowModel().rows.map(row => row.original.id)
|
||||||
|
if (selected.length === 0) {
|
||||||
|
toast.error(t('admin.cloudVariables.records.noSelection'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
selectedIds.value = selected
|
||||||
|
batchDeleteDialogOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
if (!variable.value || selectedIds.value.length === 0) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.delete(`/dev/cloud-variables/${variable.value.id}/records`, {
|
||||||
|
ids: selectedIds.value,
|
||||||
|
})
|
||||||
|
toast.success(t('admin.cloudVariables.records.batchDeleteSuccess'))
|
||||||
|
table.resetRowSelection()
|
||||||
|
fetchRecords()
|
||||||
|
}
|
||||||
|
catch (error: any) {
|
||||||
|
toast.error(error.message || t('admin.cloudVariables.records.batchDeleteFailed'))
|
||||||
|
}
|
||||||
|
finally {
|
||||||
selectedIds.value = []
|
selectedIds.value = []
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
selectedIds.value = records.value.map(r => r.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelect(id: number) {
|
function handleSearch() {
|
||||||
const index = selectedIds.value.indexOf(id)
|
page.value = 1
|
||||||
if (index > -1) {
|
fetchRecords()
|
||||||
selectedIds.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
selectedIds.value.push(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(date: string) {
|
|
||||||
return new Date(date).toLocaleString('zh-CN')
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatData(data: Record<string, any>) {
|
|
||||||
try {
|
|
||||||
return JSON.stringify(data, null, 2)
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return String(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -141,134 +298,101 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicPage
|
<BasicPage
|
||||||
title="变量记录"
|
:title="t('admin.cloudVariables.records.title')"
|
||||||
description="查看云端变量的记录数据"
|
:description="t('admin.cloudVariables.records.description', { key: variable?.key || '' })"
|
||||||
:breadcrumbs="[
|
:breadcrumbs="[
|
||||||
{ title: '云端变量', href: '/admin/cloud-variables' },
|
{ title: t('admin.cloudVariables.title'), href: '/admin/cloud-variables' },
|
||||||
{ title: '变量记录' },
|
{ title: t('admin.cloudVariables.records.title') },
|
||||||
]"
|
]"
|
||||||
|
sticky
|
||||||
>
|
>
|
||||||
<div class="space-y-6">
|
<div class="space-y-4">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="relative">
|
||||||
<UiLabel class="text-sm">开始日期</UiLabel>
|
<Search class="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||||
<UiInput
|
<Input
|
||||||
v-model="startDate"
|
v-model="searchQuery"
|
||||||
type="date"
|
:placeholder="t('admin.cloudVariables.records.searchPlaceholder')"
|
||||||
class="w-40"
|
class="pl-8 w-[250px] h-9"
|
||||||
|
@keydown.enter="handleSearch"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<Input
|
||||||
<UiLabel class="text-sm">结束日期</UiLabel>
|
v-model="startDate"
|
||||||
<UiInput
|
type="date"
|
||||||
v-model="endDate"
|
class="w-36 h-9"
|
||||||
type="date"
|
:placeholder="t('admin.cloudVariables.records.startDate')"
|
||||||
class="w-40"
|
/>
|
||||||
/>
|
<Input
|
||||||
</div>
|
v-model="endDate"
|
||||||
<UiButton @click="fetchRecords">
|
type="date"
|
||||||
<Search class="h-4 w-4 mr-2" />
|
class="w-36 h-9"
|
||||||
查询
|
:placeholder="t('admin.cloudVariables.records.endDate')"
|
||||||
</UiButton>
|
/>
|
||||||
|
<Button variant="outline" size="sm" class="h-9" @click="handleSearch">
|
||||||
|
<Search class="h-4 w-4 mr-1" />
|
||||||
|
{{ t('admin.cloudVariables.records.search') }}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<UiButton
|
<Button
|
||||||
v-if="selectedIds.length > 0"
|
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
@click="handleDeleteSelected"
|
size="sm"
|
||||||
|
:disabled="table.getSelectedRowModel().rows.length === 0"
|
||||||
|
@click="openBatchDeleteDialog"
|
||||||
>
|
>
|
||||||
<Trash2 class="h-4 w-4 mr-2" />
|
<Trash2 class="h-4 w-4 mr-1" />
|
||||||
删除选中 ({{ selectedIds.length }})
|
{{ t('admin.cloudVariables.records.batchDelete') }}
|
||||||
</UiButton>
|
<span v-if="table.getSelectedRowModel().rows.length > 0" class="ml-1">
|
||||||
|
({{ table.getSelectedRowModel().rows.length }})
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border rounded-lg">
|
<div v-if="variable" class="flex items-center gap-4 text-sm text-muted-foreground">
|
||||||
<div class="overflow-x-auto">
|
<div class="flex items-center gap-1.5">
|
||||||
<table class="w-full text-sm">
|
<List class="h-4 w-4" />
|
||||||
<thead class="bg-muted/50">
|
<span>{{ t('admin.cloudVariables.types.stream') }}</span>
|
||||||
<tr>
|
|
||||||
<th class="w-10 p-3 text-left">
|
|
||||||
<UiCheckbox
|
|
||||||
:checked="selectedIds.length === records.length && records.length > 0"
|
|
||||||
@update:checked="toggleSelectAll"
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th class="p-3 text-left font-medium">
|
|
||||||
ID
|
|
||||||
</th>
|
|
||||||
<th v-if="variable?.scope === 'user'" class="p-3 text-left font-medium">
|
|
||||||
用户
|
|
||||||
</th>
|
|
||||||
<th class="p-3 text-left font-medium">
|
|
||||||
数据
|
|
||||||
</th>
|
|
||||||
<th class="p-3 text-left font-medium">
|
|
||||||
创建时间
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="record in records"
|
|
||||||
:key="record.id"
|
|
||||||
class="border-t hover:bg-muted/30"
|
|
||||||
>
|
|
||||||
<td class="p-3">
|
|
||||||
<UiCheckbox
|
|
||||||
:checked="selectedIds.includes(record.id)"
|
|
||||||
@update:checked="toggleSelect(record.id)"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="p-3 text-muted-foreground">
|
|
||||||
{{ record.id }}
|
|
||||||
</td>
|
|
||||||
<td v-if="variable?.scope === 'user'" class="p-3">
|
|
||||||
<span v-if="record.user">{{ record.user.username }}</span>
|
|
||||||
<span v-else class="text-muted-foreground">-</span>
|
|
||||||
</td>
|
|
||||||
<td class="p-3">
|
|
||||||
<pre class="text-xs bg-muted p-2 rounded max-w-md overflow-auto max-h-32">{{ formatData(record.data) }}</pre>
|
|
||||||
</td>
|
|
||||||
<td class="p-3 text-muted-foreground">
|
|
||||||
{{ formatDate(record.created_at) }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="records.length === 0">
|
|
||||||
<td :colspan="variable?.scope === 'user' ? 5 : 4" class="p-8 text-center text-muted-foreground">
|
|
||||||
暂无记录
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span>·</span>
|
||||||
|
<span>{{ t('admin.cloudVariables.records.total', { count: total }) }}</span>
|
||||||
|
<span v-if="variable.max_records > 0">·</span>
|
||||||
|
<span v-if="variable.max_records > 0">{{ t('admin.cloudVariables.records.maxRecords', { count: variable.max_records }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-between">
|
<DataTable :columns="columns" :data="records" :loading="loading" :table="table" :server-pagination="serverPagination" @refresh="fetchRecords" />
|
||||||
<p class="text-sm text-muted-foreground">
|
|
||||||
共 {{ total }} 条记录
|
|
||||||
</p>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<UiButton
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
:disabled="page <= 1"
|
|
||||||
@click="page--; fetchRecords()"
|
|
||||||
>
|
|
||||||
上一页
|
|
||||||
</UiButton>
|
|
||||||
<span class="text-sm">第 {{ page }} 页</span>
|
|
||||||
<UiButton
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
:disabled="page * pageSize >= total"
|
|
||||||
@click="page++; fetchRecords()"
|
|
||||||
>
|
|
||||||
下一页
|
|
||||||
</UiButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
v-model:open="deleteDialogOpen"
|
||||||
|
destructive
|
||||||
|
:confirm-button-text="t('admin.cloudVariables.delete')"
|
||||||
|
:cancel-button-text="t('common.cancel')"
|
||||||
|
@confirm="handleDelete"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
{{ t('admin.cloudVariables.records.deleteRecord') }}
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
{{ t('admin.cloudVariables.records.deleteConfirm') }}
|
||||||
|
</template>
|
||||||
|
</ConfirmDialog>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
v-model:open="batchDeleteDialogOpen"
|
||||||
|
destructive
|
||||||
|
:confirm-button-text="t('admin.cloudVariables.delete')"
|
||||||
|
:cancel-button-text="t('common.cancel')"
|
||||||
|
@confirm="handleBatchDelete"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
{{ t('admin.cloudVariables.records.batchDelete') }}
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
{{ t('admin.cloudVariables.records.batchDeleteConfirm', { count: selectedIds.length }) }}
|
||||||
|
</template>
|
||||||
|
</ConfirmDialog>
|
||||||
</BasicPage>
|
</BasicPage>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1935,7 +1935,30 @@
|
|||||||
"appRequired": "Please select application",
|
"appRequired": "Please select application",
|
||||||
"scope": "Scope"
|
"scope": "Scope"
|
||||||
},
|
},
|
||||||
"createDescription": "Add a new cloud variable"
|
"createDescription": "Add a new cloud variable",
|
||||||
|
"records": {
|
||||||
|
"title": "Variable Records",
|
||||||
|
"description": "View records for variable {key}",
|
||||||
|
"user": "User",
|
||||||
|
"data": "Data",
|
||||||
|
"createdAt": "Created At",
|
||||||
|
"search": "Search",
|
||||||
|
"searchPlaceholder": "Search records...",
|
||||||
|
"startDate": "Start Date",
|
||||||
|
"endDate": "End Date",
|
||||||
|
"total": "{count} records total",
|
||||||
|
"maxRecords": "Max {count} records",
|
||||||
|
"notStreamType": "This variable is not a record type",
|
||||||
|
"deleteRecord": "Delete Record",
|
||||||
|
"deleteConfirm": "Are you sure you want to delete this record?",
|
||||||
|
"deleteSuccess": "Deleted successfully",
|
||||||
|
"deleteFailed": "Failed to delete",
|
||||||
|
"batchDelete": "Batch Delete",
|
||||||
|
"batchDeleteConfirm": "Are you sure you want to delete {count} selected records?",
|
||||||
|
"batchDeleteSuccess": "Batch delete successful",
|
||||||
|
"batchDeleteFailed": "Batch delete failed",
|
||||||
|
"noSelection": "Please select records to delete"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"cloudFunction": {
|
"cloudFunction": {
|
||||||
"title": "Cloud Function",
|
"title": "Cloud Function",
|
||||||
|
|||||||
@@ -1924,7 +1924,30 @@
|
|||||||
"appRequired": "请选择应用",
|
"appRequired": "请选择应用",
|
||||||
"scope": "作用域"
|
"scope": "作用域"
|
||||||
},
|
},
|
||||||
"createDescription": "添加新的云端变量"
|
"createDescription": "添加新的云端变量",
|
||||||
|
"records": {
|
||||||
|
"title": "变量记录",
|
||||||
|
"description": "查看变量 {key} 的记录数据",
|
||||||
|
"user": "用户",
|
||||||
|
"data": "数据",
|
||||||
|
"createdAt": "创建时间",
|
||||||
|
"search": "查询",
|
||||||
|
"searchPlaceholder": "搜索记录...",
|
||||||
|
"startDate": "开始日期",
|
||||||
|
"endDate": "结束日期",
|
||||||
|
"total": "共 {count} 条记录",
|
||||||
|
"maxRecords": "最大 {count} 条",
|
||||||
|
"notStreamType": "该变量不是记录类型",
|
||||||
|
"deleteRecord": "删除记录",
|
||||||
|
"deleteConfirm": "确定要删除该条记录吗?",
|
||||||
|
"deleteSuccess": "删除成功",
|
||||||
|
"deleteFailed": "删除失败",
|
||||||
|
"batchDelete": "批量删除",
|
||||||
|
"batchDeleteConfirm": "确定要删除选中的 {count} 条记录吗?",
|
||||||
|
"batchDeleteSuccess": "批量删除成功",
|
||||||
|
"batchDeleteFailed": "批量删除失败",
|
||||||
|
"noSelection": "请选择要删除的记录"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"cloudFunction": {
|
"cloudFunction": {
|
||||||
"title": "云端函数",
|
"title": "云端函数",
|
||||||
|
|||||||
@@ -281,6 +281,12 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: () => import('@/pages/admin/cloud-variables/[id].vue'),
|
component: () => import('@/pages/admin/cloud-variables/[id].vue'),
|
||||||
meta: { title: '编辑云端变量 - 管理后台' },
|
meta: { title: '编辑云端变量 - 管理后台' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'cloud-variables/:id/records',
|
||||||
|
name: 'AdminCloudVariableRecords',
|
||||||
|
component: () => import('@/pages/admin/cloud-variables/[id]/records.vue'),
|
||||||
|
meta: { title: '变量记录 - 管理后台' },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'cloud-function',
|
path: 'cloud-function',
|
||||||
name: 'AdminCloudFunction',
|
name: 'AdminCloudFunction',
|
||||||
|
|||||||
Vendored
-3
@@ -14,9 +14,6 @@ import type {
|
|||||||
ParamValueZeroOrMore,
|
ParamValueZeroOrMore,
|
||||||
ParamValueZeroOrOne,
|
ParamValueZeroOrOne,
|
||||||
} from 'vue-router'
|
} from 'vue-router'
|
||||||
import type {
|
|
||||||
_ExtractParamParserType,
|
|
||||||
} from 'vue-router/experimental'
|
|
||||||
|
|
||||||
declare module 'vue-router' {
|
declare module 'vue-router' {
|
||||||
interface TypesConfig {
|
interface TypesConfig {
|
||||||
|
|||||||
Reference in New Issue
Block a user