refactor: 将 @iconify/vue 图标替换为 lucide-vue-next 本地组件
- 所有 lucide: 前缀的图标字符串替换为 lucide-vue-next 组件直接引用
- 动态图标绑定改为 <component :is> 渲染
- h(Icon, { icon: 'lucide:xxx' }) 改为 h(Xxx, { class: ... })
- app-card.vue 的 icon_url 改为 <img> 标签(实际是图片路径)
- 修复 Webhook 标识符冲突(重命名为 WebhookIcon)
- payment-channels 保留 @iconify/vue 用于品牌图标(支付宝、微信、Stripe、PayPal、USDT)
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { ChevronsUpDown, Database, LogOut, UserRoundCog, Zap } from 'lucide-vue-next'
|
||||
import { Crown, ChevronsUpDown, Database, LogOut, Ticket, UserRoundCog, Zap } from 'lucide-vue-next'
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -49,7 +48,7 @@ function handleLogout() {
|
||||
<UiCardContent class="p-3 space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Icon icon="lucide:crown" class="size-4 text-primary" />
|
||||
<Crown class="size-4 text-primary" />
|
||||
<span class="text-xs font-medium">{{ subscription.plan }}</span>
|
||||
</div>
|
||||
<UiBadge
|
||||
@@ -144,7 +143,7 @@ function handleLogout() {
|
||||
个人中心
|
||||
</UiDropdownMenuItem>
|
||||
<UiDropdownMenuItem @click="router.push('/admin/tickets')">
|
||||
<Icon icon="lucide:ticket" class="mr-2 h-4 w-4" />
|
||||
<Ticket class="mr-2 h-4 w-4" />
|
||||
工单系统
|
||||
</UiDropdownMenuItem>
|
||||
</UiDropdownMenuGroup>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts" setup>
|
||||
import { Icon } from '@iconify/vue'
|
||||
import {
|
||||
ChevronRight,
|
||||
} from 'lucide-vue-next'
|
||||
@@ -48,8 +47,7 @@ function isActive(menu: NavItem): boolean {
|
||||
<UiTooltipTrigger as-child>
|
||||
<UiSidebarMenuButton as-child :is-active="isActive(menu)">
|
||||
<router-link :to="menu.url">
|
||||
<component :is="menu.icon" v-if="typeof menu.icon === 'function'" class="size-4" />
|
||||
<Icon v-else-if="menu.icon" :icon="menu.icon" />
|
||||
<component :is="menu.icon" v-if="menu.icon" class="size-4" />
|
||||
<span>{{ menu.title }}</span>
|
||||
</router-link>
|
||||
</UiSidebarMenuButton>
|
||||
@@ -60,8 +58,7 @@ function isActive(menu: NavItem): boolean {
|
||||
</UiTooltip>
|
||||
<UiSidebarMenuButton v-else as-child :is-active="isActive(menu)">
|
||||
<router-link :to="menu.url">
|
||||
<component :is="menu.icon" v-if="typeof menu.icon === 'function'" class="size-4" />
|
||||
<Icon v-else-if="menu.icon" :icon="menu.icon" />
|
||||
<component :is="menu.icon" v-if="menu.icon" class="size-4" />
|
||||
<span>{{ menu.title }}</span>
|
||||
</router-link>
|
||||
</UiSidebarMenuButton>
|
||||
@@ -77,8 +74,7 @@ function isActive(menu: NavItem): boolean {
|
||||
<UiSidebarMenuItem>
|
||||
<UiCollapsibleTrigger as-child>
|
||||
<UiSidebarMenuButton>
|
||||
<component :is="menu.icon" v-if="typeof menu.icon === 'function'" class="size-4" />
|
||||
<Icon v-else-if="menu.icon" :icon="menu.icon" />
|
||||
<component :is="menu.icon" v-if="menu.icon" class="size-4" />
|
||||
<span>{{ menu.title }}</span>
|
||||
<ChevronRight
|
||||
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
|
||||
@@ -91,8 +87,7 @@ function isActive(menu: NavItem): boolean {
|
||||
<UiSidebarMenuSubItem v-for="subItem in menu.items" :key="subItem.title">
|
||||
<UiSidebarMenuSubButton as-child :is-active="isActive(subItem as NavItem)">
|
||||
<router-link :to="subItem?.url || '/'">
|
||||
<component :is="subItem.icon" v-if="typeof subItem.icon === 'function'" class="size-4" />
|
||||
<Icon v-else-if="subItem.icon" :icon="subItem.icon" />
|
||||
<component :is="subItem.icon" v-if="subItem.icon" class="size-4" />
|
||||
<span>{{ subItem.title }}</span>
|
||||
</router-link>
|
||||
</UiSidebarMenuSubButton>
|
||||
@@ -104,8 +99,7 @@ function isActive(menu: NavItem): boolean {
|
||||
<UiTooltip v-else>
|
||||
<UiTooltipTrigger as-child>
|
||||
<UiSidebarMenuButton>
|
||||
<component :is="menu.icon" v-if="typeof menu.icon === 'function'" class="size-4" />
|
||||
<Icon v-else-if="menu.icon" :icon="menu.icon" />
|
||||
<component :is="menu.icon" v-if="menu.icon" class="size-4" />
|
||||
<span class="sr-only">{{ menu.title }}</span>
|
||||
</UiSidebarMenuButton>
|
||||
</UiTooltipTrigger>
|
||||
@@ -122,8 +116,7 @@ function isActive(menu: NavItem): boolean {
|
||||
<UiDropdownMenuSeparator />
|
||||
<UiDropdownMenuItem v-for="subItem in menu.items" :key="subItem.title" as-child>
|
||||
<router-link :to="subItem?.url || '/'">
|
||||
<component :is="subItem.icon" v-if="typeof subItem.icon === 'function'" class="size-4" />
|
||||
<Icon v-else-if="subItem.icon" :icon="subItem.icon" />
|
||||
<component :is="subItem.icon" v-if="subItem.icon" class="size-4" />
|
||||
<span>{{ subItem.title }}</span>
|
||||
</router-link>
|
||||
</UiDropdownMenuItem>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts" setup>
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import type { Team } from './types'
|
||||
@@ -18,8 +17,7 @@ const activeTeam = ref<Team>(teams[0])
|
||||
<div
|
||||
class="flex items-center justify-center rounded-lg aspect-square size-8 bg-sidebar-primary text-sidebar-primary-foreground"
|
||||
>
|
||||
<component :is="activeTeam.logo" v-if="typeof activeTeam.logo === 'function'" class="size-4" />
|
||||
<Icon v-else :icon="activeTeam.logo" class="size-4" />
|
||||
<component :is="activeTeam.logo" v-if="activeTeam.logo" class="size-4" />
|
||||
</div>
|
||||
<div class="grid flex-1 text-sm leading-tight">
|
||||
<span class="font-semibold truncate">{{ activeTeam.name }}</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { Settings } from 'lucide-vue-next'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
@@ -19,7 +19,7 @@ import ToggleColorMode from './toggle-color-mode.vue'
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button variant="ghost" size="icon" class="h-9 w-9">
|
||||
<Icon icon="lucide:settings" class="h-5 w-5" />
|
||||
<Settings class="h-5 w-5" />
|
||||
<span class="sr-only">设置</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { AcceptableValue } from 'reka-ui'
|
||||
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import FlagIcon from 'vue3-flag-icons'
|
||||
|
||||
import type { Language } from '@/plugins/i18n'
|
||||
|
||||
@@ -26,8 +26,8 @@ function handleLocaleChange(val: AcceptableValue) {
|
||||
appLocale.value = val as Language
|
||||
}
|
||||
|
||||
const currentFlag = computed(() => {
|
||||
return locale.value === 'zh' ? 'flag:cn-4x3' : 'flag:us-4x3'
|
||||
const currentFlagCode = computed(() => {
|
||||
return locale.value === 'zh' ? 'cn' : 'us'
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -35,7 +35,7 @@ const currentFlag = computed(() => {
|
||||
<UiDropdownMenu>
|
||||
<UiDropdownMenuTrigger as-child>
|
||||
<UiButton variant="ghost" size="icon" class="h-9 w-9">
|
||||
<Icon :icon="currentFlag" class="h-5 w-5" />
|
||||
<FlagIcon :code="currentFlagCode" :size="20" />
|
||||
<span class="sr-only">切换语言</span>
|
||||
</UiButton>
|
||||
</UiDropdownMenuTrigger>
|
||||
@@ -47,11 +47,11 @@ const currentFlag = computed(() => {
|
||||
@update:model-value="handleLocaleChange"
|
||||
>
|
||||
<UiDropdownMenuRadioItem value="en">
|
||||
<Icon icon="flag:us-4x3" />
|
||||
<FlagIcon code="us" :size="18" />
|
||||
<span class="ml-2">English</span>
|
||||
</UiDropdownMenuRadioItem>
|
||||
<UiDropdownMenuRadioItem value="zh">
|
||||
<Icon icon="flag:cn-4x3" />
|
||||
<FlagIcon code="cn" :size="18" />
|
||||
<span class="ml-2">中文</span>
|
||||
</UiDropdownMenuRadioItem>
|
||||
</UiDropdownMenuRadioGroup>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { Mail, Send } from 'lucide-vue-next'
|
||||
import { useColorMode } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -65,11 +65,11 @@ const links = computed(() => [
|
||||
</h3>
|
||||
<ul class="space-y-2 text-sm text-muted-foreground">
|
||||
<li class="flex items-center">
|
||||
<Icon icon="lucide:mail" class="h-4 w-4 mr-2" />
|
||||
<Mail class="h-4 w-4 mr-2" />
|
||||
admin@weishouquan.com
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<Icon icon="lucide:send" class="h-4 w-4 mr-2" />
|
||||
<Send class="h-4 w-4 mr-2" />
|
||||
weishouquan
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { BookOpen, Boxes, ChevronDown, Code, CreditCard, DollarSign, FileLock, Gauge, GitBranch, Hash, Home, Key, LayoutDashboard, LogOut, Megaphone, Menu, MessageSquare, Monitor, Moon, Plug, ScrollText, Share2, Shield, Sun, User, Users, Variable, Wifi } from 'lucide-vue-next'
|
||||
import { useColorMode } from '@vueuse/core'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -66,42 +66,42 @@ const adminMenus = computed(() => ({
|
||||
main: {
|
||||
title: t('nav.mainFeatures'),
|
||||
items: [
|
||||
{ title: t('nav.console'), url: '/admin', icon: 'lucide:gauge' },
|
||||
{ title: t('nav.applications'), url: '/admin/applications', icon: 'lucide:boxes' },
|
||||
{ title: t('nav.cardTypes'), url: '/admin/card-types', icon: 'lucide:hash' },
|
||||
{ title: t('nav.cards'), url: '/admin/cards', icon: 'lucide:key' },
|
||||
{ title: t('nav.announcements'), url: '/admin/announcements', icon: 'lucide:megaphone' },
|
||||
{ title: t('nav.versions'), url: '/admin/versions', icon: 'lucide:git-branch' },
|
||||
{ title: t('nav.users'), url: '/admin/users', icon: 'lucide:users' },
|
||||
{ title: t('nav.devices'), url: '/admin/devices', icon: 'lucide:monitor' },
|
||||
{ title: t('nav.sessions'), url: '/admin/sessions', icon: 'lucide:wifi' },
|
||||
{ title: t('nav.agentApps'), url: '/admin/agent-apps', icon: 'lucide:share-2' },
|
||||
{ title: t('nav.console'), url: '/admin', icon: Gauge },
|
||||
{ title: t('nav.applications'), url: '/admin/applications', icon: Boxes },
|
||||
{ title: t('nav.cardTypes'), url: '/admin/card-types', icon: Hash },
|
||||
{ title: t('nav.cards'), url: '/admin/cards', icon: Key },
|
||||
{ title: t('nav.announcements'), url: '/admin/announcements', icon: Megaphone },
|
||||
{ title: t('nav.versions'), url: '/admin/versions', icon: GitBranch },
|
||||
{ title: t('nav.users'), url: '/admin/users', icon: Users },
|
||||
{ title: t('nav.devices'), url: '/admin/devices', icon: Monitor },
|
||||
{ title: t('nav.sessions'), url: '/admin/sessions', icon: Wifi },
|
||||
{ title: t('nav.agentApps'), url: '/admin/agent-apps', icon: Share2 },
|
||||
],
|
||||
},
|
||||
business: {
|
||||
title: t('nav.businessManagement'),
|
||||
items: [
|
||||
{ title: t('nav.finance'), url: '/admin/finance', icon: 'lucide:dollar-sign' },
|
||||
{ title: t('nav.logs'), url: '/admin/logs', icon: 'lucide:scroll-text' },
|
||||
{ title: t('nav.tickets'), url: '/admin/tickets', icon: 'lucide:message-square' },
|
||||
{ title: t('nav.finance'), url: '/admin/finance', icon: DollarSign },
|
||||
{ title: t('nav.logs'), url: '/admin/logs', icon: ScrollText },
|
||||
{ title: t('nav.tickets'), url: '/admin/tickets', icon: MessageSquare },
|
||||
],
|
||||
},
|
||||
advanced: {
|
||||
title: t('nav.advancedFeatures'),
|
||||
items: [
|
||||
{ title: t('nav.cloudConstants'), url: '/admin/cloud-constants', icon: 'lucide:file-lock' },
|
||||
{ title: t('nav.cloudVariables'), url: '/admin/cloud-variables', icon: 'lucide:variable' },
|
||||
{ title: t('nav.cloudFunction'), url: '/admin/cloud-function', icon: 'lucide:code' },
|
||||
{ title: t('nav.riskControl'), url: '/admin/risk-control', icon: 'lucide:shield' },
|
||||
{ title: t('nav.extension'), url: '/admin/extension', icon: 'lucide:plug' },
|
||||
{ title: t('nav.cloudConstants'), url: '/admin/cloud-constants', icon: FileLock },
|
||||
{ title: t('nav.cloudVariables'), url: '/admin/cloud-variables', icon: Variable },
|
||||
{ title: t('nav.cloudFunction'), url: '/admin/cloud-function', icon: Code },
|
||||
{ title: t('nav.riskControl'), url: '/admin/risk-control', icon: Shield },
|
||||
{ title: t('nav.extension'), url: '/admin/extension', icon: Plug },
|
||||
],
|
||||
},
|
||||
}))
|
||||
|
||||
const publicMenus = computed(() => [
|
||||
{ title: t('nav.home'), url: '/', icon: 'lucide:home' },
|
||||
{ title: t('nav.docs'), url: '/docs', icon: 'lucide:book-open' },
|
||||
{ title: t('nav.pricing'), url: '/pricing', icon: 'lucide:credit-card' },
|
||||
{ title: t('nav.home'), url: '/', icon: Home },
|
||||
{ title: t('nav.docs'), url: '/docs', icon: BookOpen },
|
||||
{ title: t('nav.pricing'), url: '/pricing', icon: CreditCard },
|
||||
])
|
||||
|
||||
function isMenuActive(items: { url: string }[]) {
|
||||
@@ -207,7 +207,7 @@ watch(route, () => {
|
||||
:class="isMenuActive(adminMenus.main.items) ? 'text-foreground bg-accent' : 'text-foreground/60'"
|
||||
>
|
||||
{{ adminMenus.main.title }}
|
||||
<Icon icon="lucide:chevron-down" class="h-4 w-4" />
|
||||
<ChevronDown class="h-4 w-4" />
|
||||
</button>
|
||||
</UiDropdownMenuTrigger>
|
||||
<UiDropdownMenuContent align="start" class="w-40">
|
||||
@@ -217,7 +217,7 @@ watch(route, () => {
|
||||
:class="route.path === item.url ? 'bg-accent' : ''"
|
||||
@click="router.push(item.url)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="mr-2 h-4 w-4" />
|
||||
<component :is="item.icon" class="mr-2 h-4 w-4" />
|
||||
{{ item.title }}
|
||||
</UiDropdownMenuItem>
|
||||
</UiDropdownMenuContent>
|
||||
@@ -230,7 +230,7 @@ watch(route, () => {
|
||||
:class="isMenuActive(adminMenus.business.items) ? 'text-foreground bg-accent' : 'text-foreground/60'"
|
||||
>
|
||||
{{ adminMenus.business.title }}
|
||||
<Icon icon="lucide:chevron-down" class="h-4 w-4" />
|
||||
<ChevronDown class="h-4 w-4" />
|
||||
</button>
|
||||
</UiDropdownMenuTrigger>
|
||||
<UiDropdownMenuContent align="start" class="w-40">
|
||||
@@ -240,7 +240,7 @@ watch(route, () => {
|
||||
:class="route.path === item.url ? 'bg-accent' : ''"
|
||||
@click="router.push(item.url)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="mr-2 h-4 w-4" />
|
||||
<component :is="item.icon" class="mr-2 h-4 w-4" />
|
||||
{{ item.title }}
|
||||
</UiDropdownMenuItem>
|
||||
</UiDropdownMenuContent>
|
||||
@@ -253,7 +253,7 @@ watch(route, () => {
|
||||
:class="isMenuActive(adminMenus.advanced.items) ? 'text-foreground bg-accent' : 'text-foreground/60'"
|
||||
>
|
||||
{{ adminMenus.advanced.title }}
|
||||
<Icon icon="lucide:chevron-down" class="h-4 w-4" />
|
||||
<ChevronDown class="h-4 w-4" />
|
||||
</button>
|
||||
</UiDropdownMenuTrigger>
|
||||
<UiDropdownMenuContent align="start" class="w-40">
|
||||
@@ -263,7 +263,7 @@ watch(route, () => {
|
||||
:class="route.path === item.url ? 'bg-accent' : ''"
|
||||
@click="router.push(item.url)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="mr-2 h-4 w-4" />
|
||||
<component :is="item.icon" class="mr-2 h-4 w-4" />
|
||||
{{ item.title }}
|
||||
</UiDropdownMenuItem>
|
||||
</UiDropdownMenuContent>
|
||||
@@ -276,7 +276,7 @@ watch(route, () => {
|
||||
class="md:hidden mr-2 flex items-center justify-center size-9 rounded-md hover:bg-accent"
|
||||
@click="mobileMenuOpen = true"
|
||||
>
|
||||
<Icon icon="lucide:menu" class="size-5" />
|
||||
<Menu class="size-5" />
|
||||
</button>
|
||||
|
||||
<router-link to="/" class="mr-6 flex items-center space-x-2 md:hidden">
|
||||
@@ -295,12 +295,10 @@ watch(route, () => {
|
||||
class="h-9 w-9"
|
||||
@click="mode = mode === 'dark' ? 'light' : 'dark'"
|
||||
>
|
||||
<Icon
|
||||
icon="lucide:sun"
|
||||
<Sun
|
||||
class="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
|
||||
/>
|
||||
<Icon
|
||||
icon="lucide:moon"
|
||||
<Moon
|
||||
class="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
|
||||
/>
|
||||
<span class="sr-only">切换主题</span>
|
||||
@@ -331,20 +329,20 @@ watch(route, () => {
|
||||
</UiDropdownMenuLabel>
|
||||
<UiDropdownMenuSeparator />
|
||||
<UiDropdownMenuItem @click="router.push('/profile')">
|
||||
<Icon icon="lucide:user" class="mr-2 h-4 w-4" />
|
||||
<User class="mr-2 h-4 w-4" />
|
||||
<span>{{ t('nav.profile') }}</span>
|
||||
</UiDropdownMenuItem>
|
||||
<UiDropdownMenuItem v-if="currentUser?.role === 'admin'" @click="router.push('/admin')">
|
||||
<Icon icon="lucide:layout-dashboard" class="mr-2 h-4 w-4" />
|
||||
<LayoutDashboard class="mr-2 h-4 w-4" />
|
||||
<span>{{ t('nav.adminDashboard') }}</span>
|
||||
</UiDropdownMenuItem>
|
||||
<UiDropdownMenuItem v-if="currentUser?.role === 'agent'" @click="router.push('/agent')">
|
||||
<Icon icon="lucide:layout-dashboard" class="mr-2 h-4 w-4" />
|
||||
<LayoutDashboard class="mr-2 h-4 w-4" />
|
||||
<span>{{ t('nav.agentDashboard') }}</span>
|
||||
</UiDropdownMenuItem>
|
||||
<UiDropdownMenuSeparator />
|
||||
<UiDropdownMenuItem @click="handleLogout">
|
||||
<Icon icon="lucide:log-out" class="mr-2 h-4 w-4" />
|
||||
<LogOut class="mr-2 h-4 w-4" />
|
||||
<span>{{ t('nav.logout') }}</span>
|
||||
</UiDropdownMenuItem>
|
||||
</UiDropdownMenuContent>
|
||||
@@ -389,7 +387,7 @@ watch(route, () => {
|
||||
:class="isActive(item.url) ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-accent hover:text-foreground'"
|
||||
@click="navigateTo(item.url)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="size-4" />
|
||||
<component :is="item.icon" class="size-4" />
|
||||
{{ item.title }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -406,7 +404,7 @@ watch(route, () => {
|
||||
:class="isActive(item.url) ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-accent hover:text-foreground'"
|
||||
@click="navigateTo(item.url)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="size-4" />
|
||||
<component :is="item.icon" class="size-4" />
|
||||
{{ item.title }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -422,7 +420,7 @@ watch(route, () => {
|
||||
:class="isActive(item.url) ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-accent hover:text-foreground'"
|
||||
@click="navigateTo(item.url)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="size-4" />
|
||||
<component :is="item.icon" class="size-4" />
|
||||
{{ item.title }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -438,8 +436,8 @@ watch(route, () => {
|
||||
:class="isActive(item.url) ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-accent hover:text-foreground'"
|
||||
@click="navigateTo(item.url)"
|
||||
>
|
||||
<Icon :icon="item.icon" class="size-4" />
|
||||
{{ item.title }}
|
||||
<component :is="item.icon" class="size-4" />
|
||||
{{ item.title }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -474,7 +472,7 @@ watch(route, () => {
|
||||
</div>
|
||||
</div>
|
||||
<UiButton variant="outline" class="w-full mt-2" @click="handleLogout">
|
||||
<Icon icon="lucide:log-out" class="mr-2 h-4 w-4" />
|
||||
<LogOut class="mr-2 h-4 w-4" />
|
||||
{{ t('nav.logout') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { Moon, Sun } from 'lucide-vue-next'
|
||||
import { useColorMode } from '@vueuse/core'
|
||||
|
||||
const mode = useColorMode()
|
||||
@@ -12,12 +12,10 @@ const mode = useColorMode()
|
||||
class="h-9 w-9"
|
||||
@click="mode = mode === 'dark' ? 'light' : 'dark'"
|
||||
>
|
||||
<Icon
|
||||
icon="lucide:sun"
|
||||
<Sun
|
||||
class="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
|
||||
/>
|
||||
<Icon
|
||||
icon="lucide:moon"
|
||||
<Moon
|
||||
class="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
|
||||
/>
|
||||
<span class="sr-only">切换主题</span>
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { DateValue } from '@internationalized/date'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { parseDate } from '@internationalized/date'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { Calendar } from 'lucide-vue-next'
|
||||
import { computed } from 'vue'
|
||||
import UiButton from '@/components/ui/button/Button.vue'
|
||||
import UiPopover from '@/components/ui/popover/Popover.vue'
|
||||
@@ -73,7 +73,7 @@ function handleSelect(date: DateValue | undefined) {
|
||||
)"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<Icon icon="lucide:calendar" class="mr-2 h-4 w-4" />
|
||||
<Calendar class="mr-2 h-4 w-4" />
|
||||
{{ formattedDate || placeholder || '选择日期' }}
|
||||
</UiButton>
|
||||
</UiPopoverTrigger>
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { DateValue } from '@internationalized/date'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { parseDate } from '@internationalized/date'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { CalendarClock } from 'lucide-vue-next'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import UiButton from '@/components/ui/button/Button.vue'
|
||||
import UiPopover from '@/components/ui/popover/Popover.vue'
|
||||
@@ -95,7 +95,7 @@ watch(() => props.modelValue, initFromModelValue, { immediate: true })
|
||||
)"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<Icon icon="lucide:calendar-clock" class="mr-2 h-4 w-4 shrink-0" />
|
||||
<CalendarClock class="mr-2 h-4 w-4 shrink-0" />
|
||||
<span class="truncate text-sm">{{ displayText || placeholder || '选择日期时间' }}</span>
|
||||
</UiButton>
|
||||
</UiPopoverTrigger>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { Clock } from 'lucide-vue-next'
|
||||
import { computed } from 'vue'
|
||||
import UiButton from '@/components/ui/button/Button.vue'
|
||||
import UiPopover from '@/components/ui/popover/Popover.vue'
|
||||
@@ -53,7 +53,7 @@ function handleTimeChange(time: string | number) {
|
||||
)"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<Icon icon="lucide:clock" class="mr-2 h-4 w-4" />
|
||||
<Clock class="mr-2 h-4 w-4" />
|
||||
{{ formattedTime || placeholder || '选择时间' }}
|
||||
</UiButton>
|
||||
</UiPopoverTrigger>
|
||||
|
||||
Reference in New Issue
Block a user