mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-26 03:44:55 +08:00
update: 优化添加资源
This commit is contained in:
14
web/components.d.ts
vendored
14
web/components.d.ts
vendored
@@ -8,6 +8,20 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
|
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||||
|
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
|
NIcon: typeof import('naive-ui')['NIcon']
|
||||||
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
|
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
|
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
|
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||||
|
NPagination: typeof import('naive-ui')['NPagination']
|
||||||
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
}
|
}
|
||||||
|
|||||||
38
web/components/ApiDocumentation.vue
Normal file
38
web/components/ApiDocumentation.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="text-gray-700 dark:text-gray-300 text-sm">
|
||||||
|
<p>你可以通过API批量添加资源:</p>
|
||||||
|
<pre class="bg-gray-100 dark:bg-gray-800 p-3 rounded text-xs overflow-x-auto mt-2">
|
||||||
|
POST /api/resources/batch
|
||||||
|
Content-Type: application/json
|
||||||
|
Body:
|
||||||
|
[
|
||||||
|
{ "title": "资源A", "url": "https://a.com", "file_type": "pan", ... },
|
||||||
|
{ "title": "资源B", "url": "https://b.com", ... }
|
||||||
|
]
|
||||||
|
</pre>
|
||||||
|
<p>参数说明:<br/>
|
||||||
|
title: 标题<br/>
|
||||||
|
url: 资源链接<br/>
|
||||||
|
file_type: 类型(pan/link/other)<br/>
|
||||||
|
tags: 标签数组(可选)<br/>
|
||||||
|
description: 描述(可选)<br/>
|
||||||
|
... 其他字段参考文档
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end space-x-3 pt-4">
|
||||||
|
<button type="button" @click="$emit('cancel')" class="btn-secondary">关闭</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const emit = defineEmits(['cancel'])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.btn-secondary {
|
||||||
|
@apply px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md transition-colors;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
73
web/components/BatchAddResource.vue
Normal file
73
web/components/BatchAddResource.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">输入格式说明:</label>
|
||||||
|
<div class="bg-gray-50 dark:bg-gray-800 p-3 rounded text-sm text-gray-600 dark:text-gray-300 mb-4">
|
||||||
|
<p class="mb-2"><strong>格式1:</strong>标题和URL两行一组</p>
|
||||||
|
<pre class="bg-white dark:bg-gray-800 p-2 rounded border text-xs">
|
||||||
|
电影标题1
|
||||||
|
https://pan.baidu.com/s/123456
|
||||||
|
电影标题2
|
||||||
|
https://pan.baidu.com/s/789012</pre>
|
||||||
|
<p class="mt-2 mb-2"><strong>格式2:</strong>只有URL,系统自动判断</p>
|
||||||
|
<pre class="bg-white dark:bg-gray-800 p-2 rounded border text-xs">
|
||||||
|
https://pan.baidu.com/s/123456
|
||||||
|
https://pan.baidu.com/s/789012
|
||||||
|
https://pan.baidu.com/s/345678</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">资源内容:</label>
|
||||||
|
<textarea
|
||||||
|
v-model="batchInput"
|
||||||
|
rows="15"
|
||||||
|
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-900 dark:text-gray-100"
|
||||||
|
placeholder="请输入资源内容,支持两种格式..."
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end space-x-3 pt-4">
|
||||||
|
<button type="button" @click="$emit('cancel')" class="btn-secondary">取消</button>
|
||||||
|
<button type="button" @click="handleSubmit" class="btn-primary" :disabled="loading">
|
||||||
|
{{ loading ? '保存中...' : '批量添加' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useReadyResourceApi } from '~/composables/useApi'
|
||||||
|
|
||||||
|
const emit = defineEmits(['success', 'error', 'cancel'])
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const batchInput = ref('')
|
||||||
|
|
||||||
|
const readyResourceApi = useReadyResourceApi()
|
||||||
|
|
||||||
|
// 批量添加提交
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
if (!batchInput.value.trim()) throw new Error('请输入资源内容')
|
||||||
|
const res: any = await readyResourceApi.createReadyResourcesFromText(batchInput.value)
|
||||||
|
emit('success', `成功添加 ${res.count || 0} 个资源,资源已进入待处理列表,处理完成后会自动入库`)
|
||||||
|
batchInput.value = ''
|
||||||
|
} catch (e: any) {
|
||||||
|
emit('error', e.message || '批量添加失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.btn-primary {
|
||||||
|
@apply px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors disabled:opacity-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md transition-colors;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
123
web/components/SingleAddResource.vue
Normal file
123
web/components/SingleAddResource.vue
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<template>
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">标题</label>
|
||||||
|
<input v-model="form.title" class="input-field dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700" placeholder="输入标题" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">描述</label>
|
||||||
|
<textarea v-model="form.description" rows="3" class="input-field dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700" placeholder="输入资源描述"></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">类型</label>
|
||||||
|
<select v-model="form.file_type" class="input-field dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700">
|
||||||
|
<option value="">选择类型</option>
|
||||||
|
<option value="pan">网盘</option>
|
||||||
|
<option value="link">直链</option>
|
||||||
|
<option value="other">其他</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">标签</label>
|
||||||
|
<div class="flex flex-wrap gap-2 mb-2">
|
||||||
|
<span v-for="tag in form.tags" :key="tag" class="bg-blue-100 text-blue-700 px-2 py-1 rounded text-xs flex items-center">
|
||||||
|
{{ tag }}
|
||||||
|
<button type="button" class="ml-1 text-xs" @click="removeTag(tag)">×</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input v-model="newTag" @keyup.enter.prevent="addTag" class="input-field dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700" placeholder="输入标签后回车添加" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">链接(可多行,每行一个链接)</label>
|
||||||
|
<textarea v-model="form.url" rows="3" class="input-field dark:bg-gray-900 dark:text-gray-100 dark:border-gray-700" placeholder="https://a.com https://b.com"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end space-x-3 pt-4">
|
||||||
|
<button type="button" @click="$emit('cancel')" class="btn-secondary">取消</button>
|
||||||
|
<button type="button" @click="handleSubmit" class="btn-primary" :disabled="loading">
|
||||||
|
{{ loading ? '保存中...' : '添加' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useResourceStore } from '~/stores/resource'
|
||||||
|
|
||||||
|
const emit = defineEmits(['success', 'error', 'cancel'])
|
||||||
|
|
||||||
|
const store = useResourceStore()
|
||||||
|
const loading = ref(false)
|
||||||
|
const newTag = ref('')
|
||||||
|
|
||||||
|
// 单个添加表单
|
||||||
|
const form = ref({
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
url: '', // 多行
|
||||||
|
category_id: '',
|
||||||
|
tags: [] as string[],
|
||||||
|
file_path: '',
|
||||||
|
file_type: '',
|
||||||
|
file_size: 0,
|
||||||
|
is_public: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const addTag = () => {
|
||||||
|
const tag = newTag.value.trim()
|
||||||
|
if (tag && !form.value.tags.includes(tag)) {
|
||||||
|
form.value.tags.push(tag)
|
||||||
|
newTag.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeTag = (tag: string) => {
|
||||||
|
const index = form.value.tags.indexOf(tag)
|
||||||
|
if (index > -1) {
|
||||||
|
form.value.tags.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单个添加提交
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 多行链接
|
||||||
|
const urls = form.value.url.split(/\r?\n/).map(l => l.trim()).filter(Boolean)
|
||||||
|
if (!urls.length) throw new Error('请输入至少一个链接')
|
||||||
|
for (const url of urls) {
|
||||||
|
await store.createResource({
|
||||||
|
...form.value,
|
||||||
|
url,
|
||||||
|
tags: [...form.value.tags],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
emit('success', '资源已进入待处理列表,处理完成后会自动入库')
|
||||||
|
// 清空表单
|
||||||
|
form.value.title = ''
|
||||||
|
form.value.description = ''
|
||||||
|
form.value.url = ''
|
||||||
|
form.value.tags = []
|
||||||
|
form.value.file_type = ''
|
||||||
|
} catch (e: any) {
|
||||||
|
emit('error', e.message || '添加失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.input-field {
|
||||||
|
@apply w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
@apply px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors disabled:opacity-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md transition-colors;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
137
web/pages/add-resource.vue
Normal file
137
web/pages/add-resource.vue
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-100">
|
||||||
|
<!-- 页面头部 -->
|
||||||
|
<div class="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<div class="max-w-4xl mx-auto px-4 py-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<button
|
||||||
|
@click="$router.back()"
|
||||||
|
class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100"
|
||||||
|
>
|
||||||
|
<i class="fas fa-arrow-left text-xl"></i>
|
||||||
|
</button>
|
||||||
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">添加资源</h1>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<NuxtLink
|
||||||
|
to="/admin"
|
||||||
|
class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md transition-colors text-sm"
|
||||||
|
>
|
||||||
|
<i class="fas fa-cog mr-1"></i>管理后台
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 主要内容 -->
|
||||||
|
<div class="max-w-4xl mx-auto px-4 py-8">
|
||||||
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg">
|
||||||
|
<!-- Tab 切换 -->
|
||||||
|
<div class="border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<div class="flex">
|
||||||
|
<button
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.value"
|
||||||
|
:class="[
|
||||||
|
'px-6 py-4 text-sm font-medium border-b-2 transition-colors',
|
||||||
|
mode === tab.value
|
||||||
|
? 'border-blue-500 text-blue-600 dark:text-blue-400'
|
||||||
|
: 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
|
||||||
|
]"
|
||||||
|
@click="mode = tab.value"
|
||||||
|
>
|
||||||
|
{{ tab.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<div class="p-6">
|
||||||
|
<!-- 批量添加 -->
|
||||||
|
<BatchAddResource
|
||||||
|
v-if="mode === 'batch'"
|
||||||
|
@success="handleSuccess"
|
||||||
|
@error="handleError"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 单个添加 -->
|
||||||
|
<SingleAddResource
|
||||||
|
v-else-if="mode === 'single'"
|
||||||
|
@success="handleSuccess"
|
||||||
|
@error="handleError"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- API说明 -->
|
||||||
|
<ApiDocumentation
|
||||||
|
v-else
|
||||||
|
@cancel="handleCancel"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 成功/失败提示 -->
|
||||||
|
<SuccessToast v-if="showSuccess" :message="successMsg" @close="showSuccess = false" />
|
||||||
|
<ErrorToast v-if="showError" :message="errorMsg" @close="showError = false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import BatchAddResource from '~/components/BatchAddResource.vue'
|
||||||
|
import SingleAddResource from '~/components/SingleAddResource.vue'
|
||||||
|
import ApiDocumentation from '~/components/ApiDocumentation.vue'
|
||||||
|
import SuccessToast from '~/components/SuccessToast.vue'
|
||||||
|
import ErrorToast from '~/components/ErrorToast.vue'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const showSuccess = ref(false)
|
||||||
|
const successMsg = ref('')
|
||||||
|
const showError = ref(false)
|
||||||
|
const errorMsg = ref('')
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{ label: '批量添加', value: 'batch' },
|
||||||
|
{ label: '单个添加', value: 'single' },
|
||||||
|
{ label: 'API说明', value: 'api' },
|
||||||
|
]
|
||||||
|
const mode = ref('batch')
|
||||||
|
|
||||||
|
// 检查用户权限
|
||||||
|
onMounted(() => {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
if (!userStore.isAuthenticated) {
|
||||||
|
router.push('/login')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 事件处理
|
||||||
|
const handleSuccess = (message: string) => {
|
||||||
|
successMsg.value = message
|
||||||
|
showSuccess.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleError = (message: string) => {
|
||||||
|
errorMsg.value = message
|
||||||
|
showError.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置页面标题
|
||||||
|
useHead({
|
||||||
|
title: '添加资源 - 网盘资源管理系统'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 自定义样式 */
|
||||||
|
</style>
|
||||||
@@ -42,12 +42,12 @@
|
|||||||
>
|
>
|
||||||
<i class="fas fa-home"></i> 返回首页
|
<i class="fas fa-home"></i> 返回首页
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<button
|
<NuxtLink
|
||||||
@click="showAddResourceModal = true"
|
to="/add-resource"
|
||||||
class="w-full sm:w-auto px-4 py-2 bg-green-600 hover:bg-green-700 rounded-md transition-colors text-center flex items-center justify-center gap-2"
|
class="w-full sm:w-auto px-4 py-2 bg-green-600 hover:bg-green-700 rounded-md transition-colors text-center flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
<i class="fas fa-plus"></i> 添加资源
|
<i class="fas fa-plus"></i> 添加资源
|
||||||
</button>
|
</NuxtLink>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -71,12 +71,12 @@
|
|||||||
<i class="fas fa-chevron-right text-gray-400"></i>
|
<i class="fas fa-chevron-right text-gray-400"></i>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button @click="showAddResourceModal = true" class="w-full text-left p-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
<NuxtLink to="/add-resource" class="w-full text-left p-3 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors block">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">添加新资源</span>
|
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">批量添加资源</span>
|
||||||
<i class="fas fa-plus text-gray-400"></i>
|
<i class="fas fa-plus text-gray-400"></i>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -269,16 +269,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 模态框组件 -->
|
|
||||||
<ResourceModal v-if="showAddResourceModal" @close="showAddResourceModal = false" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import ResourceModal from '~/components/ResourceModal.vue'
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: 'auth'
|
middleware: 'auth'
|
||||||
})
|
})
|
||||||
@@ -292,7 +287,6 @@ const { $api } = useNuxtApp()
|
|||||||
|
|
||||||
const user = ref(null)
|
const user = ref(null)
|
||||||
const stats = ref(null)
|
const stats = ref(null)
|
||||||
const showAddResourceModal = ref(false)
|
|
||||||
const pageLoading = ref(true) // 添加页面加载状态
|
const pageLoading = ref(true) // 添加页面加载状态
|
||||||
const systemConfig = ref(null) // 添加系统配置状态
|
const systemConfig = ref(null) // 添加系统配置状态
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user