feat: 优化授权管理和代理后台
- 授权管理页面显示具体卡密类型名称、授权时间、备注 - 重新设计添加/编辑授权页面,参考版本管理和卡类管理页面样式 - 添加备注字段支持 - 移除代理后台的应用管理页面和路由 - 从代理后台侧边栏删除应用管理导航
This commit is contained in:
@@ -53,11 +53,6 @@ const navMain = computed(() => [
|
|||||||
url: '/agent',
|
url: '/agent',
|
||||||
icon: Gauge,
|
icon: Gauge,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t('nav.applications'),
|
|
||||||
url: '/agent/apps',
|
|
||||||
icon: Boxes,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('nav.cards'),
|
title: t('nav.cards'),
|
||||||
url: '/agent/cards',
|
url: '/agent/cards',
|
||||||
@@ -88,7 +83,6 @@ const breadcrumbs = computed(() => {
|
|||||||
const crumbs = [{ title: t('nav.console'), path: '/agent' }]
|
const crumbs = [{ title: t('nav.console'), path: '/agent' }]
|
||||||
|
|
||||||
const titleMap: Record<string, string> = {
|
const titleMap: Record<string, string> = {
|
||||||
apps: t('nav.applications'),
|
|
||||||
cards: t('nav.cards'),
|
cards: t('nav.cards'),
|
||||||
users: t('nav.users'),
|
users: t('nav.users'),
|
||||||
finance: t('nav.finance'),
|
finance: t('nav.finance'),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Check, CheckCircle, Loader2 } from 'lucide-vue-next'
|
import { CheckCircle, Loader2 } from 'lucide-vue-next'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
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'
|
||||||
@@ -30,7 +30,6 @@ interface CardType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const agentAppId = computed(() => route.params.id as string)
|
const agentAppId = computed(() => route.params.id as string)
|
||||||
const isEdit = computed(() => !!agentAppId.value)
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
|
|
||||||
@@ -41,6 +40,7 @@ const cardTypes = ref<CardType[]>([])
|
|||||||
const form = ref({
|
const form = ref({
|
||||||
agent_id: '',
|
agent_id: '',
|
||||||
application_id: '',
|
application_id: '',
|
||||||
|
remark: '',
|
||||||
card_type_prices: {} as Record<string, number>,
|
card_type_prices: {} as Record<string, number>,
|
||||||
card_type_auth: {} as Record<string, boolean>,
|
card_type_auth: {} as Record<string, boolean>,
|
||||||
})
|
})
|
||||||
@@ -63,6 +63,17 @@ async function fetchApplications() {
|
|||||||
catch { /* ignore */ }
|
catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchCardTypes(appId: string) {
|
||||||
|
if (!appId) return
|
||||||
|
try {
|
||||||
|
const data = await api.get<{ card_types: CardType[] }>(`/dev/card-types?application_id=${appId}`)
|
||||||
|
cardTypes.value = data?.card_types || []
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
cardTypes.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchAgentApp() {
|
async function fetchAgentApp() {
|
||||||
if (!agentAppId.value) return
|
if (!agentAppId.value) return
|
||||||
|
|
||||||
@@ -74,17 +85,15 @@ async function fetchAgentApp() {
|
|||||||
if (agentApp) {
|
if (agentApp) {
|
||||||
form.value.agent_id = String(agentApp.agent_id)
|
form.value.agent_id = String(agentApp.agent_id)
|
||||||
form.value.application_id = String(agentApp.application_id)
|
form.value.application_id = String(agentApp.application_id)
|
||||||
|
form.value.remark = agentApp.remark || ''
|
||||||
agentApp.card_types.forEach((ct: any) => {
|
|
||||||
form.value.card_type_auth[String(ct.card_type_id)] = ct.can_generate
|
|
||||||
})
|
|
||||||
|
|
||||||
await fetchCardTypes(String(agentApp.application_id))
|
await fetchCardTypes(String(agentApp.application_id))
|
||||||
|
|
||||||
cardTypes.value.forEach((ct) => {
|
cardTypes.value.forEach((ct) => {
|
||||||
const existingCard = agentApp.card_types.find((c: any) => c.card_type_id === ct.id)
|
const existingCard = agentApp.card_types?.find((c: any) => c.card_type_id === ct.id)
|
||||||
if (existingCard) {
|
if (existingCard) {
|
||||||
form.value.card_type_prices[String(ct.id)] = existingCard.price || ct.price
|
form.value.card_type_prices[String(ct.id)] = existingCard.price || ct.price
|
||||||
|
form.value.card_type_auth[String(ct.id)] = existingCard.can_generate
|
||||||
} else {
|
} else {
|
||||||
form.value.card_type_prices[String(ct.id)] = ct.price
|
form.value.card_type_prices[String(ct.id)] = ct.price
|
||||||
form.value.card_type_auth[String(ct.id)] = false
|
form.value.card_type_auth[String(ct.id)] = false
|
||||||
@@ -101,24 +110,6 @@ async function fetchAgentApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchCardTypes(appId: string) {
|
|
||||||
if (!appId) return
|
|
||||||
try {
|
|
||||||
const data = await api.get<{ card_types: CardType[] }>(`/dev/card-types?application_id=${appId}`)
|
|
||||||
cardTypes.value = data?.card_types || []
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
cardTypes.value = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAppChange(appId: string) {
|
|
||||||
cardTypes.value = []
|
|
||||||
form.value.card_type_prices = {}
|
|
||||||
form.value.card_type_auth = {}
|
|
||||||
if (appId) fetchCardTypes(appId)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
if (!form.value.agent_id) {
|
if (!form.value.agent_id) {
|
||||||
toast.error('请选择代理')
|
toast.error('请选择代理')
|
||||||
@@ -140,26 +131,16 @@ async function handleSave() {
|
|||||||
: ct.price,
|
: ct.price,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if (isEdit.value) {
|
await api.put(`/dev/agent-apps/${agentAppId.value}`, {
|
||||||
await api.put(`/dev/agent-apps/${agentAppId.value}`, {
|
card_types: cardTypesPayload,
|
||||||
card_types: cardTypesPayload,
|
discount: 0,
|
||||||
discount: 0,
|
remark: form.value.remark,
|
||||||
})
|
})
|
||||||
toast.success('授权更新成功')
|
toast.success('授权更新成功')
|
||||||
} else {
|
|
||||||
await api.post('/dev/agent-apps', {
|
|
||||||
agent_id: Number(form.value.agent_id),
|
|
||||||
application_id: Number(form.value.application_id),
|
|
||||||
card_types: cardTypesPayload,
|
|
||||||
discount: 0,
|
|
||||||
})
|
|
||||||
toast.success('授权添加成功')
|
|
||||||
}
|
|
||||||
|
|
||||||
router.push('/admin/agent-apps')
|
router.push('/admin/agent-apps')
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
toast.error(error.message || (isEdit.value ? '更新授权失败' : '添加授权失败'))
|
toast.error(error.message || '更新授权失败')
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
saving.value = false
|
saving.value = false
|
||||||
@@ -169,19 +150,17 @@ async function handleSave() {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchAgents()
|
fetchAgents()
|
||||||
fetchApplications()
|
fetchApplications()
|
||||||
if (isEdit.value) {
|
fetchAgentApp()
|
||||||
fetchAgentApp()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicPage
|
<BasicPage
|
||||||
:title="isEdit ? '编辑授权' : '添加授权'"
|
title="编辑授权"
|
||||||
:description="isEdit ? '修改代理的应用授权配置' : '为代理添加新的应用授权'"
|
description="修改代理的应用授权配置"
|
||||||
:breadcrumbs="[
|
:breadcrumbs="[
|
||||||
{ title: t('nav.agentApps'), href: '/admin/agent-apps' },
|
{ title: t('nav.agentApps'), href: '/admin/agent-apps' },
|
||||||
{ title: isEdit ? '编辑授权' : '添加授权' },
|
{ title: '编辑授权' },
|
||||||
]"
|
]"
|
||||||
sticky
|
sticky
|
||||||
>
|
>
|
||||||
@@ -201,7 +180,7 @@ onMounted(() => {
|
|||||||
<UiLabel>选择代理</UiLabel>
|
<UiLabel>选择代理</UiLabel>
|
||||||
<UiSelect
|
<UiSelect
|
||||||
v-model="form.agent_id"
|
v-model="form.agent_id"
|
||||||
:disabled="isEdit"
|
disabled
|
||||||
>
|
>
|
||||||
<UiSelectTrigger>
|
<UiSelectTrigger>
|
||||||
<UiSelectValue placeholder="请选择代理" />
|
<UiSelectValue placeholder="请选择代理" />
|
||||||
@@ -212,14 +191,14 @@ onMounted(() => {
|
|||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
</UiSelectContent>
|
</UiSelectContent>
|
||||||
</UiSelect>
|
</UiSelect>
|
||||||
|
<p class="text-xs text-muted-foreground">编辑时不可修改代理</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>选择应用</UiLabel>
|
<UiLabel>选择应用</UiLabel>
|
||||||
<UiSelect
|
<UiSelect
|
||||||
v-model="form.application_id"
|
v-model="form.application_id"
|
||||||
@update:model-value="onAppChange"
|
disabled
|
||||||
:disabled="isEdit"
|
|
||||||
>
|
>
|
||||||
<UiSelectTrigger>
|
<UiSelectTrigger>
|
||||||
<UiSelectValue placeholder="请选择应用" />
|
<UiSelectValue placeholder="请选择应用" />
|
||||||
@@ -230,8 +209,18 @@ onMounted(() => {
|
|||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
</UiSelectContent>
|
</UiSelectContent>
|
||||||
</UiSelect>
|
</UiSelect>
|
||||||
|
<p class="text-xs text-muted-foreground">编辑时不可修改应用</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<UiLabel>备注</UiLabel>
|
||||||
|
<UiTextarea
|
||||||
|
v-model="form.remark"
|
||||||
|
placeholder="输入备注信息(可选)"
|
||||||
|
rows="3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</UiCardContent>
|
</UiCardContent>
|
||||||
</UiCard>
|
</UiCard>
|
||||||
|
|
||||||
@@ -290,7 +279,7 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" />
|
<Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" />
|
||||||
<CheckCircle v-else class="mr-2 h-4 w-4" />
|
<CheckCircle v-else class="mr-2 h-4 w-4" />
|
||||||
{{ isEdit ? '保存修改' : '确认授权' }}
|
保存修改
|
||||||
</UiButton>
|
</UiButton>
|
||||||
<UiButton variant="outline" @click="router.back()">
|
<UiButton variant="outline" @click="router.back()">
|
||||||
取消
|
取消
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { Composer } from 'vue-i18n'
|
|||||||
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Check, Pencil, Trash2, X } from 'lucide-vue-next'
|
import { Check, Pencil, Trash2 } from 'lucide-vue-next'
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
|
|
||||||
import type { AgentApp } from '../data/schema'
|
import type { AgentApp } from '../data/schema'
|
||||||
@@ -46,19 +46,24 @@ export function getColumns(actions: {
|
|||||||
header: () => h('span', {}, '卡密类型'),
|
header: () => h('span', {}, '卡密类型'),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const cardTypes = row.getValue('card_types') as AgentApp['card_types']
|
const cardTypes = row.getValue('card_types') as AgentApp['card_types']
|
||||||
const total = cardTypes?.length || 0
|
if (!cardTypes || cardTypes.length === 0) {
|
||||||
const authorized = cardTypes?.filter(c => c.can_generate).length || 0
|
return h('span', { class: 'text-muted-foreground' }, '-')
|
||||||
return h('div', { class: 'flex items-center gap-2' }, [
|
}
|
||||||
h('span', { class: 'text-sm' }, `${authorized} / ${total}`),
|
|
||||||
])
|
return h('div', { class: 'flex flex-wrap gap-1' },
|
||||||
},
|
cardTypes.map(ct =>
|
||||||
},
|
h('span', {
|
||||||
{
|
class: `inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs ${
|
||||||
accessorKey: 'balance',
|
ct.can_generate
|
||||||
header: () => h('span', {}, '余额'),
|
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
|
||||||
cell: ({ row }) => {
|
: 'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400'
|
||||||
const balance = row.getValue('balance') as number
|
}`
|
||||||
return h('span', { class: 'text-sm font-medium' }, `¥${(balance || 0).toFixed(2)}`)
|
}, [
|
||||||
|
ct.can_generate && h(Check, { class: 'size-3' }),
|
||||||
|
ct.name || `卡类#${ct.card_type_id}`
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -72,6 +77,24 @@ export function getColumns(actions: {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'created_at',
|
||||||
|
header: () => h('span', {}, '授权时间'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const createdAt = row.getValue('created_at') as string
|
||||||
|
if (!createdAt) return h('span', { class: 'text-muted-foreground' }, '-')
|
||||||
|
const date = new Date(createdAt)
|
||||||
|
return h('span', { class: 'text-sm' }, date.toLocaleString('zh-CN'))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'remark',
|
||||||
|
header: () => h('span', {}, '备注'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const remark = row.getValue('remark') as string
|
||||||
|
return h('span', { class: 'text-sm text-muted-foreground' }, remark || '-')
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
header: () => h('span', { class: 'sr-only' }, '操作'),
|
header: () => h('span', { class: 'sr-only' }, '操作'),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Check, CheckCircle, Loader2 } from 'lucide-vue-next'
|
import { CheckCircle, Loader2 } from 'lucide-vue-next'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
@@ -29,7 +29,6 @@ interface CardType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
|
|
||||||
const agents = ref<Agent[]>([])
|
const agents = ref<Agent[]>([])
|
||||||
const applications = ref<Application[]>([])
|
const applications = ref<Application[]>([])
|
||||||
const cardTypes = ref<CardType[]>([])
|
const cardTypes = ref<CardType[]>([])
|
||||||
@@ -37,10 +36,18 @@ const cardTypes = ref<CardType[]>([])
|
|||||||
const form = ref({
|
const form = ref({
|
||||||
agent_id: '',
|
agent_id: '',
|
||||||
application_id: '',
|
application_id: '',
|
||||||
|
remark: '',
|
||||||
card_type_prices: {} as Record<string, number>,
|
card_type_prices: {} as Record<string, number>,
|
||||||
card_type_auth: {} as Record<string, boolean>,
|
card_type_auth: {} as Record<string, boolean>,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const selectedApplication = computed(() => {
|
||||||
|
if (form.value.application_id) {
|
||||||
|
return applications.value.find(app => String(app.id) === form.value.application_id)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
async function fetchAgents() {
|
async function fetchAgents() {
|
||||||
try {
|
try {
|
||||||
const data = await api.get<{ agents?: Agent[] }>('/dev/agents')
|
const data = await api.get<{ agents?: Agent[] }>('/dev/agents')
|
||||||
@@ -74,12 +81,12 @@ async function fetchCardTypes(appId: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAppChange(appId: string) {
|
watch(() => form.value.application_id, (appId) => {
|
||||||
cardTypes.value = []
|
cardTypes.value = []
|
||||||
form.value.card_type_prices = {}
|
form.value.card_type_prices = {}
|
||||||
form.value.card_type_auth = {}
|
form.value.card_type_auth = {}
|
||||||
if (appId) fetchCardTypes(appId)
|
if (appId) fetchCardTypes(appId)
|
||||||
}
|
})
|
||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
if (!form.value.agent_id) {
|
if (!form.value.agent_id) {
|
||||||
@@ -107,6 +114,7 @@ async function handleSave() {
|
|||||||
application_id: Number(form.value.application_id),
|
application_id: Number(form.value.application_id),
|
||||||
card_types: cardTypesPayload,
|
card_types: cardTypesPayload,
|
||||||
discount: 0,
|
discount: 0,
|
||||||
|
remark: form.value.remark,
|
||||||
})
|
})
|
||||||
toast.success('授权添加成功')
|
toast.success('授权添加成功')
|
||||||
router.push('/admin/agent-apps')
|
router.push('/admin/agent-apps')
|
||||||
@@ -144,7 +152,7 @@ onMounted(() => {
|
|||||||
<UiCardContent class="space-y-6">
|
<UiCardContent class="space-y-6">
|
||||||
<div class="grid gap-6 md:grid-cols-2">
|
<div class="grid gap-6 md:grid-cols-2">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>选择代理</UiLabel>
|
<UiLabel>选择代理 <span class="text-destructive">*</span></UiLabel>
|
||||||
<UiSelect v-model="form.agent_id">
|
<UiSelect v-model="form.agent_id">
|
||||||
<UiSelectTrigger>
|
<UiSelectTrigger>
|
||||||
<UiSelectValue placeholder="请选择代理" />
|
<UiSelectValue placeholder="请选择代理" />
|
||||||
@@ -158,11 +166,8 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>选择应用</UiLabel>
|
<UiLabel>选择应用 <span class="text-destructive">*</span></UiLabel>
|
||||||
<UiSelect
|
<UiSelect v-model="form.application_id">
|
||||||
v-model="form.application_id"
|
|
||||||
@update:model-value="onAppChange"
|
|
||||||
>
|
|
||||||
<UiSelectTrigger>
|
<UiSelectTrigger>
|
||||||
<UiSelectValue placeholder="请选择应用" />
|
<UiSelectValue placeholder="请选择应用" />
|
||||||
</UiSelectTrigger>
|
</UiSelectTrigger>
|
||||||
@@ -174,6 +179,15 @@ onMounted(() => {
|
|||||||
</UiSelect>
|
</UiSelect>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<UiLabel>备注</UiLabel>
|
||||||
|
<UiTextarea
|
||||||
|
v-model="form.remark"
|
||||||
|
placeholder="输入备注信息(可选)"
|
||||||
|
rows="3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</UiCardContent>
|
</UiCardContent>
|
||||||
</UiCard>
|
</UiCard>
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ export interface AgentApp {
|
|||||||
card_types: { card_type_id: number, can_generate: boolean, name?: string }[]
|
card_types: { card_type_id: number, can_generate: boolean, name?: string }[]
|
||||||
created_at?: string
|
created_at?: string
|
||||||
updated_at?: string
|
updated_at?: string
|
||||||
|
remark?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -426,18 +426,6 @@ const routes: RouteRecordRaw[] = [
|
|||||||
component: () => import('@/pages/agent/index.vue'),
|
component: () => import('@/pages/agent/index.vue'),
|
||||||
meta: { title: '控制台 - 代理商后台' },
|
meta: { title: '控制台 - 代理商后台' },
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'apps',
|
|
||||||
name: 'AgentApps',
|
|
||||||
component: () => import('@/pages/agent/apps/index.vue'),
|
|
||||||
meta: { title: '应用管理 - 代理商后台' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'apps/:id',
|
|
||||||
name: 'AgentAppDetail',
|
|
||||||
component: () => import('@/pages/agent/apps/[id].vue'),
|
|
||||||
meta: { title: '应用详情 - 代理商后台' },
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'cards',
|
path: 'cards',
|
||||||
name: 'AgentCards',
|
name: 'AgentCards',
|
||||||
|
|||||||
Reference in New Issue
Block a user