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">
|
||||
import { Search, Trash2 } from 'lucide-vue-next'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import type { ColumnDef } from '@tanstack/vue-table'
|
||||
|
||||
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 { 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 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'
|
||||
|
||||
interface Variable {
|
||||
@@ -28,16 +38,159 @@ interface VariableRecord {
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
|
||||
const loading = ref(true)
|
||||
const variable = ref<Variable | null>(null)
|
||||
const records = ref<VariableRecord[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const pageSize = ref(10)
|
||||
const startDate = ref('')
|
||||
const endDate = ref('')
|
||||
const searchQuery = ref('')
|
||||
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() {
|
||||
try {
|
||||
@@ -45,7 +198,7 @@ async function fetchVariable() {
|
||||
const vars = data?.variables || []
|
||||
variable.value = vars.find((v: Variable) => String(v.id) === route.params.id) || null
|
||||
if (variable.value && variable.value.var_type !== 'stream') {
|
||||
toast.error('该变量不是记录类型')
|
||||
toast.error(t('admin.cloudVariables.records.notStreamType'))
|
||||
router.back()
|
||||
}
|
||||
}
|
||||
@@ -55,18 +208,16 @@ async function fetchVariable() {
|
||||
}
|
||||
|
||||
async function fetchRecords() {
|
||||
if (!variable.value)
|
||||
return
|
||||
if (!variable.value) return
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const params = new URLSearchParams()
|
||||
params.append('page', String(page.value))
|
||||
params.append('page_size', String(pageSize.value))
|
||||
if (startDate.value)
|
||||
params.append('start_date', startDate.value)
|
||||
if (endDate.value)
|
||||
params.append('end_date', endDate.value)
|
||||
if (startDate.value) params.append('start_date', startDate.value)
|
||||
if (endDate.value) params.append('end_date', endDate.value)
|
||||
if (searchQuery.value) params.append('search', searchQuery.value)
|
||||
|
||||
const data = await api.get<{ records: VariableRecord[], total: number }>(`/dev/cloud-variables/${variable.value.id}/records?${params}`)
|
||||
records.value = data?.records || []
|
||||
@@ -80,55 +231,61 @@ async function fetchRecords() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteSelected() {
|
||||
if (selectedIds.value.length === 0) {
|
||||
toast.error('请选择要删除的记录')
|
||||
return
|
||||
}
|
||||
function confirmDelete(id: number) {
|
||||
deleteTargetId.value = id
|
||||
deleteDialogOpen.value = true
|
||||
}
|
||||
|
||||
async function handleDelete() {
|
||||
if (!deleteTargetId.value || !variable.value) return
|
||||
|
||||
try {
|
||||
await api.delete(`/dev/cloud-variables/${variable.value?.id}/records`, {
|
||||
ids: selectedIds.value,
|
||||
await api.delete(`/dev/cloud-variables/${variable.value.id}/records`, {
|
||||
ids: [deleteTargetId.value],
|
||||
})
|
||||
toast.success('删除成功')
|
||||
selectedIds.value = []
|
||||
toast.success(t('admin.cloudVariables.records.deleteSuccess'))
|
||||
fetchRecords()
|
||||
}
|
||||
catch (error: any) {
|
||||
toast.error(error.message || '删除失败')
|
||||
toast.error(error.message || t('admin.cloudVariables.records.deleteFailed'))
|
||||
}
|
||||
finally {
|
||||
deleteTargetId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelectAll() {
|
||||
if (selectedIds.value.length === records.value.length) {
|
||||
function openBatchDeleteDialog() {
|
||||
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 = []
|
||||
}
|
||||
else {
|
||||
selectedIds.value = records.value.map(r => r.id)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelect(id: number) {
|
||||
const index = selectedIds.value.indexOf(id)
|
||||
if (index > -1) {
|
||||
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)
|
||||
}
|
||||
function handleSearch() {
|
||||
page.value = 1
|
||||
fetchRecords()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -141,134 +298,101 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<BasicPage
|
||||
title="变量记录"
|
||||
description="查看云端变量的记录数据"
|
||||
:title="t('admin.cloudVariables.records.title')"
|
||||
:description="t('admin.cloudVariables.records.description', { key: variable?.key || '' })"
|
||||
:breadcrumbs="[
|
||||
{ title: '云端变量', href: '/admin/cloud-variables' },
|
||||
{ title: '变量记录' },
|
||||
{ title: t('admin.cloudVariables.title'), href: '/admin/cloud-variables' },
|
||||
{ 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 gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<UiLabel class="text-sm">开始日期</UiLabel>
|
||||
<UiInput
|
||||
v-model="startDate"
|
||||
type="date"
|
||||
class="w-40"
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="relative">
|
||||
<Search class="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
:placeholder="t('admin.cloudVariables.records.searchPlaceholder')"
|
||||
class="pl-8 w-[250px] h-9"
|
||||
@keydown.enter="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UiLabel class="text-sm">结束日期</UiLabel>
|
||||
<UiInput
|
||||
v-model="endDate"
|
||||
type="date"
|
||||
class="w-40"
|
||||
/>
|
||||
</div>
|
||||
<UiButton @click="fetchRecords">
|
||||
<Search class="h-4 w-4 mr-2" />
|
||||
查询
|
||||
</UiButton>
|
||||
<Input
|
||||
v-model="startDate"
|
||||
type="date"
|
||||
class="w-36 h-9"
|
||||
:placeholder="t('admin.cloudVariables.records.startDate')"
|
||||
/>
|
||||
<Input
|
||||
v-model="endDate"
|
||||
type="date"
|
||||
class="w-36 h-9"
|
||||
:placeholder="t('admin.cloudVariables.records.endDate')"
|
||||
/>
|
||||
<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 class="flex items-center gap-2">
|
||||
<UiButton
|
||||
v-if="selectedIds.length > 0"
|
||||
<Button
|
||||
variant="destructive"
|
||||
@click="handleDeleteSelected"
|
||||
size="sm"
|
||||
:disabled="table.getSelectedRowModel().rows.length === 0"
|
||||
@click="openBatchDeleteDialog"
|
||||
>
|
||||
<Trash2 class="h-4 w-4 mr-2" />
|
||||
删除选中 ({{ selectedIds.length }})
|
||||
</UiButton>
|
||||
<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>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-lg">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-muted/50">
|
||||
<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 v-if="variable" class="flex items-center gap-4 text-sm text-muted-foreground">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<List class="h-4 w-4" />
|
||||
<span>{{ t('admin.cloudVariables.types.stream') }}</span>
|
||||
</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 class="flex items-center justify-between">
|
||||
<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>
|
||||
<DataTable :columns="columns" :data="records" :loading="loading" :table="table" :server-pagination="serverPagination" @refresh="fetchRecords" />
|
||||
</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>
|
||||
</template>
|
||||
|
||||
@@ -1935,7 +1935,30 @@
|
||||
"appRequired": "Please select application",
|
||||
"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": {
|
||||
"title": "Cloud Function",
|
||||
|
||||
@@ -1924,7 +1924,30 @@
|
||||
"appRequired": "请选择应用",
|
||||
"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": {
|
||||
"title": "云端函数",
|
||||
|
||||
@@ -281,6 +281,12 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import('@/pages/admin/cloud-variables/[id].vue'),
|
||||
meta: { title: '编辑云端变量 - 管理后台' },
|
||||
},
|
||||
{
|
||||
path: 'cloud-variables/:id/records',
|
||||
name: 'AdminCloudVariableRecords',
|
||||
component: () => import('@/pages/admin/cloud-variables/[id]/records.vue'),
|
||||
meta: { title: '变量记录 - 管理后台' },
|
||||
},
|
||||
{
|
||||
path: 'cloud-function',
|
||||
name: 'AdminCloudFunction',
|
||||
|
||||
Vendored
-3
@@ -14,9 +14,6 @@ import type {
|
||||
ParamValueZeroOrMore,
|
||||
ParamValueZeroOrOne,
|
||||
} from 'vue-router'
|
||||
import type {
|
||||
_ExtractParamParserType,
|
||||
} from 'vue-router/experimental'
|
||||
|
||||
declare module 'vue-router' {
|
||||
interface TypesConfig {
|
||||
|
||||
Reference in New Issue
Block a user