feat: add fallback pages and refine page interactions
This commit is contained in:
Generated
+58
@@ -18,6 +18,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.3.0",
|
"@tailwindcss/vite": "^4.3.0",
|
||||||
"@types/node": "^24.12.3",
|
"@types/node": "^24.12.3",
|
||||||
|
"@vicons/tabler": "^0.13.0",
|
||||||
|
"@vicons/utils": "^0.1.4",
|
||||||
"@vitejs/plugin-vue": "^6.0.6",
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
"@vue/tsconfig": "^0.9.1",
|
"@vue/tsconfig": "^0.9.1",
|
||||||
"tailwindcss": "^4.3.0",
|
"tailwindcss": "^4.3.0",
|
||||||
@@ -971,6 +973,26 @@
|
|||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vicons/tabler": {
|
||||||
|
"version": "0.13.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vicons/tabler/-/tabler-0.13.0.tgz",
|
||||||
|
"integrity": "sha512-AykuhiqjszkIoAL/7knIFm6RDOBS1ZmQdJfQ+RNLEah0fVsxykUFCfMBSNZh8lOzC85EtdD1k5g/sv5GYk0Ohg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@vicons/utils": {
|
||||||
|
"version": "0.1.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vicons/utils/-/utils-0.1.4.tgz",
|
||||||
|
"integrity": "sha512-OHI19qVNN6i+uPQ+Y3f2s0dUxwsYnOCcKBW7XOU4yXXO1aU3ZoKpblCc3+4N0qmgoJs5rWKRAaMisipqEXJwAg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@xicons/utils": "^0.1.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "6.0.7",
|
"version": "6.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz",
|
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz",
|
||||||
@@ -1212,6 +1234,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xicons/utils": {
|
||||||
|
"version": "0.1.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@xicons/utils/-/utils-0.1.4.tgz",
|
||||||
|
"integrity": "sha512-uXxKDLz9abr80yJC05XSTq6wlyFcdW+N/1IYJkeHjzzXVc4VQ0sEYMoMMTjAH7HQBOyOkzOB4pf5NGF72lwa8Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-render": "^0.13.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@xicons/utils/node_modules/@types/node": {
|
||||||
|
"version": "14.14.45",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/node/-/node-14.14.45.tgz",
|
||||||
|
"integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@xicons/utils/node_modules/css-render": {
|
||||||
|
"version": "0.13.9",
|
||||||
|
"resolved": "https://registry.npmmirror.com/css-render/-/css-render-0.13.9.tgz",
|
||||||
|
"integrity": "sha512-n3C4ZH59rveBrUlAD7n0Ze9/gUMKa4dlH1C9CWKpGcIHR/xRcIVXzBGy1iw8WWq2ySmn2/ZqOpySQNAK5Pb6sw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/hash": "~0.8.0",
|
||||||
|
"@types/node": "~14.14.31",
|
||||||
|
"csstype": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@xicons/utils/node_modules/csstype": {
|
||||||
|
"version": "3.0.11",
|
||||||
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.0.11.tgz",
|
||||||
|
"integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.16.0",
|
"version": "8.16.0",
|
||||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz",
|
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz",
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.3.0",
|
"@tailwindcss/vite": "^4.3.0",
|
||||||
"@types/node": "^24.12.3",
|
"@types/node": "^24.12.3",
|
||||||
|
"@vicons/tabler": "^0.13.0",
|
||||||
|
"@vicons/utils": "^0.1.4",
|
||||||
"@vitejs/plugin-vue": "^6.0.6",
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
"@vue/tsconfig": "^0.9.1",
|
"@vue/tsconfig": "^0.9.1",
|
||||||
"tailwindcss": "^4.3.0",
|
"tailwindcss": "^4.3.0",
|
||||||
|
|||||||
+11
-1
@@ -62,6 +62,16 @@ const router = createRouter({
|
|||||||
component: () => import('@/views/admin/AdminView.vue'),
|
component: () => import('@/views/admin/AdminView.vue'),
|
||||||
meta: { requiresAuth: true, requiresAdmin: true },
|
meta: { requiresAuth: true, requiresAdmin: true },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/403',
|
||||||
|
name: 'forbidden',
|
||||||
|
component: () => import('@/views/system/ForbiddenView.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/:pathMatch(.*)*',
|
||||||
|
name: 'not-found',
|
||||||
|
component: () => import('@/views/system/NotFoundView.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
scrollBehavior() {
|
scrollBehavior() {
|
||||||
return { top: 0 }
|
return { top: 0 }
|
||||||
@@ -74,7 +84,7 @@ router.beforeEach((to, _from, next) => {
|
|||||||
if (to.meta.requiresAuth && !authStore.isLoggedIn) {
|
if (to.meta.requiresAuth && !authStore.isLoggedIn) {
|
||||||
next({ name: 'login', query: { redirect: to.fullPath } })
|
next({ name: 'login', query: { redirect: to.fullPath } })
|
||||||
} else if (to.meta.requiresAdmin && !authStore.isAdmin) {
|
} else if (to.meta.requiresAdmin && !authStore.isAdmin) {
|
||||||
next({ name: 'map' })
|
next({ name: 'forbidden' })
|
||||||
} else if ((to.name === 'login' || to.name === 'register') && authStore.isLoggedIn) {
|
} else if ((to.name === 'login' || to.name === 'register') && authStore.isLoggedIn) {
|
||||||
next({ name: 'map' })
|
next({ name: 'map' })
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { NAlert, NCard, NEmpty, NProgress, NSkeleton } from 'naive-ui'
|
import { NAlert, NCard, NEmpty, NIcon, NProgress, NSkeleton } from 'naive-ui'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { useEncyclopediaStore } from '@/stores/encyclopedia'
|
import { useEncyclopediaStore } from '@/stores/encyclopedia'
|
||||||
|
import { Lock } from '@vicons/tabler'
|
||||||
import type { CloudType } from '@/types/database'
|
import type { CloudType } from '@/types/database'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -66,7 +67,7 @@ onMounted(async () => {
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="bg-[linear-gradient(180deg,#e0f2fe_0%,#f8fafc_100%)] border-b border-sky-100">
|
<div class="bg-[linear-gradient(180deg,#e0f2fe_0%,#f8fafc_100%)] border-b border-sky-100">
|
||||||
<div class="max-w-6xl mx-auto px-4 py-10">
|
<div class="max-w-6xl mx-auto px-4 py-10">
|
||||||
<div class="grid gap-6 lg:grid-cols-[1.4fr_0.9fr] lg:items-end">
|
<div class="grid gap-6 lg:grid-cols-[1.4fr_0.9fr] lg:items-start">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm font-medium tracking-[0.24em] text-sky-700 uppercase">Cloud Encyclopedia</p>
|
<p class="text-sm font-medium tracking-[0.24em] text-sky-700 uppercase">Cloud Encyclopedia</p>
|
||||||
<h1 class="mt-3 text-4xl font-bold text-slate-900">云朵图鉴</h1>
|
<h1 class="mt-3 text-4xl font-bold text-slate-900">云朵图鉴</h1>
|
||||||
@@ -91,8 +92,7 @@ onMounted(async () => {
|
|||||||
type="line"
|
type="line"
|
||||||
:show-indicator="false"
|
:show-indicator="false"
|
||||||
:percentage="authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0"
|
:percentage="authStore.isLoggedIn ? encyclopediaStore.unlockPercent : 0"
|
||||||
color="linear-gradient(90deg,#0ea5e9 0%,#f59e0b 100%)"
|
color="{stops:['#0ea5e9','#f59e0b']}"
|
||||||
rail-color="#dbe4ee"
|
|
||||||
:height="12"
|
:height="12"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -161,7 +161,14 @@ onMounted(async () => {
|
|||||||
<div class="absolute inset-0 bg-gradient-to-t from-slate-950/70 via-slate-950/10 to-transparent"></div>
|
<div class="absolute inset-0 bg-gradient-to-t from-slate-950/70 via-slate-950/10 to-transparent"></div>
|
||||||
|
|
||||||
<div v-if="!isUnlocked(cloudType.id)" class="absolute inset-0 flex items-center justify-center bg-slate-950/22 backdrop-blur-[2px]">
|
<div v-if="!isUnlocked(cloudType.id)" class="absolute inset-0 flex items-center justify-center bg-slate-950/22 backdrop-blur-[2px]">
|
||||||
<div class="border border-white/45 bg-white/15 px-4 py-2 text-sm font-medium text-white">🔒 尚未解锁</div>
|
<div class="border border-white/45 bg-white/15 px-4 py-2 text-sm font-medium text-white">
|
||||||
|
<span style="display: inline-flex; align-items: center; gap: 4px;">
|
||||||
|
<NIcon size="16" style="display: inline-flex;">
|
||||||
|
<Lock />
|
||||||
|
</NIcon>
|
||||||
|
<span>尚未解锁</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="absolute left-4 top-4">
|
<div class="absolute left-4 top-4">
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import { NAlert, NButton, NEmpty, NSkeleton, NTag } from 'naive-ui'
|
import { NAlert, NButton, NEmpty, NIcon, NSkeleton, NTag } from 'naive-ui'
|
||||||
import ImageDetailModal from '@/components/cloud/ImageDetailModal.vue'
|
import ImageDetailModal from '@/components/cloud/ImageDetailModal.vue'
|
||||||
import { supabase } from '@/lib/supabase'
|
import { supabase } from '@/lib/supabase'
|
||||||
import { useCloudsStore } from '@/stores/clouds'
|
import { useCloudsStore } from '@/stores/clouds'
|
||||||
|
import { Clock, User ,Location} from '@vicons/tabler'
|
||||||
|
|
||||||
import type { CloudType } from '@/types/database'
|
import type { CloudType } from '@/types/database'
|
||||||
|
|
||||||
interface GalleryCloud {
|
interface GalleryCloud {
|
||||||
@@ -260,9 +262,24 @@ onUnmounted(() => {
|
|||||||
{{ rarityMeta[cloud.cloudTypeRarity].label }}
|
{{ rarityMeta[cloud.cloudTypeRarity].label }}
|
||||||
</NTag>
|
</NTag>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-2 truncate text-xs text-white/82">📷 {{ cloud.username }}</p>
|
<p class="mt-2 flex items-center gap-1.5 truncate text-xs text-white/82">
|
||||||
<p class="mt-1 truncate text-xs text-white/82">🕐 {{ formatUploadTime(cloud) }}</p>
|
<NIcon size="14">
|
||||||
<p class="mt-1 truncate text-xs text-white/68">{{ cloud.location_name || '未填写位置' }}</p>
|
<User />
|
||||||
|
</NIcon>
|
||||||
|
<span class="truncate">{{ cloud.username }}</span>
|
||||||
|
</p>
|
||||||
|
<p class="mt-1 flex items-center gap-1.5 truncate text-xs text-white/82">
|
||||||
|
<NIcon size="14">
|
||||||
|
<Clock />
|
||||||
|
</NIcon>
|
||||||
|
<span class="truncate">{{ formatUploadTime(cloud) }}</span>
|
||||||
|
</p>
|
||||||
|
<p class="mt-1 flex items-center gap-1.5 truncate text-xs text-white/82">
|
||||||
|
<NIcon size="14">
|
||||||
|
<Location />
|
||||||
|
</NIcon>
|
||||||
|
<span class="truncate">{{ cloud.location_name || '未填写位置' }}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { ref, onMounted, onUnmounted } from 'vue'
|
|||||||
import ImageDetailModal from '@/components/cloud/ImageDetailModal.vue'
|
import ImageDetailModal from '@/components/cloud/ImageDetailModal.vue'
|
||||||
import { supabase } from '@/lib/supabase'
|
import { supabase } from '@/lib/supabase'
|
||||||
import { loadAMap } from '@/lib/amap'
|
import { loadAMap } from '@/lib/amap'
|
||||||
|
import { NIcon } from 'naive-ui'
|
||||||
|
import { Refresh,Map,Satellite} from '@vicons/tabler'
|
||||||
|
|
||||||
interface CloudMarkerData {
|
interface CloudMarkerData {
|
||||||
id: string
|
id: string
|
||||||
@@ -292,8 +294,19 @@ onUnmounted(() => {
|
|||||||
<div ref="mapEl" class="w-full h-full"></div>
|
<div ref="mapEl" class="w-full h-full"></div>
|
||||||
|
|
||||||
<div class="absolute bottom-6 right-4 flex flex-col gap-2 z-10">
|
<div class="absolute bottom-6 right-4 flex flex-col gap-2 z-10">
|
||||||
<button @click="refresh" class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50" title="刷新"><span class="text-lg">🔄</span></button>
|
<button @click="refresh" class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50" title="刷新">
|
||||||
<button @click="toggleSat" class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50" :title="satelliteOn ? '切换普通视图' : '切换卫星视图'"><span class="text-lg">{{ satelliteOn ? '🗺️' : '🛰️' }}</span></button>
|
<span class="text-lg">
|
||||||
|
<NIcon>
|
||||||
|
<Refresh/>
|
||||||
|
</NIcon>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button @click="toggleSat" class="w-10 h-10 bg-white rounded-lg shadow-md flex items-center justify-center hover:bg-gray-50" :title="satelliteOn ? '切换普通视图' : '切换卫星视图'">
|
||||||
|
<NIcon size="20" style="display: inline-flex; vertical-align: middle;">
|
||||||
|
<Map v-if="satelliteOn" />
|
||||||
|
<Satellite v-else />
|
||||||
|
</NIcon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ImageDetailModal
|
<ImageDetailModal
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { NButton } from 'naive-ui'
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex min-h-[calc(100vh-4rem)] items-center justify-center px-4 py-10">
|
||||||
|
<div class="w-full max-w-2xl border border-slate-200 bg-white px-8 py-14 text-center shadow-sm">
|
||||||
|
<div class="text-7xl leading-none">🖐️</div>
|
||||||
|
<p class="mt-6 text-sm font-medium uppercase tracking-[0.24em] text-rose-600">403 Forbidden</p>
|
||||||
|
<h1 class="mt-3 text-4xl font-bold text-slate-900">这里不让进</h1>
|
||||||
|
<p class="mx-auto mt-4 max-w-xl text-sm leading-7 text-slate-600">
|
||||||
|
你当前没有访问这个页面的权限。通常是因为这个区域只对管理员开放。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
|
||||||
|
<RouterLink to="/">
|
||||||
|
<NButton type="primary" secondary strong>返回地图</NButton>
|
||||||
|
</RouterLink>
|
||||||
|
<RouterLink to="/profile">
|
||||||
|
<NButton secondary strong>前往个人主页</NButton>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { NButton } from 'naive-ui'
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex min-h-[calc(100vh-4rem)] items-center justify-center px-4 py-10">
|
||||||
|
<div class="w-full max-w-2xl border border-slate-200 bg-white px-8 py-14 text-center shadow-sm">
|
||||||
|
<div class="text-7xl leading-none">🤔</div>
|
||||||
|
<p class="mt-6 text-sm font-medium uppercase tracking-[0.24em] text-amber-600">404 Not Found</p>
|
||||||
|
<h1 class="mt-3 text-4xl font-bold text-slate-900">我也没想明白</h1>
|
||||||
|
<p class="mx-auto mt-4 max-w-xl text-sm leading-7 text-slate-600">
|
||||||
|
这个页面不存在,或者它已经被移动到了别的地方。换个入口再试一次。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
|
||||||
|
<RouterLink to="/">
|
||||||
|
<NButton type="primary" secondary strong>返回地图</NButton>
|
||||||
|
</RouterLink>
|
||||||
|
<RouterLink to="/gallery">
|
||||||
|
<NButton secondary strong>去画廊看看</NButton>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -447,7 +447,7 @@ onUnmounted(() => {
|
|||||||
<input
|
<input
|
||||||
v-model="activeItem.customCloudType"
|
v-model="activeItem.customCloudType"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="输入云型名称"
|
placeholder="输入云的类型"
|
||||||
@input="activeItem.errors = {}"
|
@input="activeItem.errors = {}"
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-transparent transition-colors"
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-transparent transition-colors"
|
||||||
/>
|
/>
|
||||||
@@ -517,7 +517,7 @@ onUnmounted(() => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-xs text-gray-400 mt-1">选填,可手动输入,或点击右侧小地图图标选点</p>
|
<p class="text-xs text-gray-400 mt-1">选填,可手动输入,或点击右侧小地图图标选择</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 位置名称 -->
|
<!-- 位置名称 -->
|
||||||
@@ -545,7 +545,7 @@ onUnmounted(() => {
|
|||||||
<!-- 隐身模式 -->
|
<!-- 隐身模式 -->
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<input v-model="activeItem.isHidden" type="checkbox" :id="'isHidden-' + activeItem.id" class="w-4 h-4 text-sky-500 border-gray-300 rounded focus:ring-sky-500" />
|
<input v-model="activeItem.isHidden" type="checkbox" :id="'isHidden-' + activeItem.id" class="w-4 h-4 text-sky-500 border-gray-300 rounded focus:ring-sky-500" />
|
||||||
<label :for="'isHidden-' + activeItem.id" class="text-sm text-gray-600">隐身模式(地图不显示位置)</label>
|
<label :for="'isHidden-' + activeItem.id" class="text-sm text-gray-600">隐身模式(不在地图上显示)</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NCard>
|
</NCard>
|
||||||
@@ -553,7 +553,7 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 flex items-center justify-between">
|
<div class="mt-6 flex items-center justify-between">
|
||||||
<p class="text-sm text-gray-500">共 {{ items.length }} 张图片,类别和拍摄时间为必填项</p>
|
<p class="text-sm text-gray-500">共 {{ items.length }} 张图片</p>
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<NButton secondary strong @click="clearAll()" :disabled="uploading">清空</NButton>
|
<NButton secondary strong @click="clearAll()" :disabled="uploading">清空</NButton>
|
||||||
<NButton type="primary" secondary strong @click="handleSubmit" :disabled="uploading">
|
<NButton type="primary" secondary strong @click="handleSubmit" :disabled="uploading">
|
||||||
|
|||||||
Reference in New Issue
Block a user