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,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { Activity, ArrowDownLeft, ArrowRight, ArrowUpRight, Calendar, Camera, Copy, Crown, Eye, EyeOff, HardDrive, Key, Loader2, Mail, Receipt, RefreshCw, RotateCcw, Save, Shield, User, Zap } from 'lucide-vue-next'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
@@ -346,7 +346,7 @@ onMounted(() => {
|
||||
<div class="min-h-screen bg-background">
|
||||
<div class="mx-auto max-w-5xl px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div v-if="loading" class="flex items-center justify-center py-20">
|
||||
<Icon icon="lucide:loader-2" class="size-10 animate-spin text-muted-foreground" />
|
||||
<Loader2 class="size-10 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
@@ -364,22 +364,22 @@ onMounted(() => {
|
||||
alt="Avatar"
|
||||
class="size-full object-cover"
|
||||
>
|
||||
<Icon v-else icon="lucide:user" class="size-12 text-muted-foreground" />
|
||||
<User v-else class="size-12 text-muted-foreground" />
|
||||
<div class="absolute inset-0 bg-black/50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<Icon icon="lucide:camera" class="size-8 text-white" />
|
||||
<Camera class="size-8 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="uploadingAvatar"
|
||||
class="absolute inset-0 rounded-2xl bg-black/50 flex items-center justify-center"
|
||||
>
|
||||
<Icon icon="lucide:loader-2" class="size-8 animate-spin text-white" />
|
||||
<Loader2 class="size-8 animate-spin text-white" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute -bottom-12 right-8 flex items-center gap-2">
|
||||
<UiButton variant="outline" size="sm" @click="showPasswordDialog = true">
|
||||
<Icon icon="lucide:key" class="mr-2 h-4 w-4" />
|
||||
<Key class="mr-2 h-4 w-4" />
|
||||
修改密码
|
||||
</UiButton>
|
||||
</div>
|
||||
@@ -403,15 +403,15 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="flex items-center gap-4 mt-2 text-sm text-muted-foreground">
|
||||
<span class="flex items-center gap-1">
|
||||
<Icon icon="lucide:shield" class="size-4" />
|
||||
<Shield class="size-4" />
|
||||
{{ getRoleName(user?.role || '') }}
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<Icon icon="lucide:mail" class="size-4" />
|
||||
<Mail class="size-4" />
|
||||
{{ user?.email || '-' }}
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<Icon icon="lucide:calendar" class="size-4" />
|
||||
<Calendar class="size-4" />
|
||||
加入于 {{ formatDateShort(user?.created_at || '') }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -422,7 +422,7 @@ onMounted(() => {
|
||||
<UiCard class="overflow-hidden">
|
||||
<div class="bg-muted/30 px-6 py-4 border-b">
|
||||
<h2 class="font-semibold flex items-center gap-2">
|
||||
<Icon icon="lucide:user" class="size-5 text-primary" />
|
||||
<User class="size-5 text-primary" />
|
||||
基本信息
|
||||
</h2>
|
||||
</div>
|
||||
@@ -472,8 +472,8 @@ onMounted(() => {
|
||||
:disabled="saving || !isProfileFormValid"
|
||||
@click="handleUpdateProfile"
|
||||
>
|
||||
<Icon v-if="saving" icon="lucide:loader-2" class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Icon v-else icon="lucide:save" class="mr-2 h-4 w-4" />
|
||||
<Loader2 v-if="saving" class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Save v-else class="mr-2 h-4 w-4" />
|
||||
保存修改
|
||||
</UiButton>
|
||||
</div>
|
||||
@@ -483,7 +483,7 @@ onMounted(() => {
|
||||
<UiCard class="overflow-hidden">
|
||||
<div class="bg-muted/30 px-6 py-4 border-b">
|
||||
<h2 class="font-semibold flex items-center gap-2">
|
||||
<Icon icon="lucide:key" class="size-5 text-primary" />
|
||||
<Key class="size-5 text-primary" />
|
||||
API Token
|
||||
</h2>
|
||||
</div>
|
||||
@@ -504,7 +504,8 @@ onMounted(() => {
|
||||
size="icon"
|
||||
@click="showApiToken = !showApiToken"
|
||||
>
|
||||
<Icon :icon="showApiToken ? 'lucide:eye-off' : 'lucide:eye'" class="size-4" />
|
||||
<EyeOff v-if="showApiToken" class="size-4" />
|
||||
<Eye v-else class="size-4" />
|
||||
</UiButton>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
@@ -521,7 +522,7 @@ onMounted(() => {
|
||||
:disabled="!user?.api_token"
|
||||
@click="copyApiToken"
|
||||
>
|
||||
<Icon icon="lucide:copy" class="size-4" />
|
||||
<Copy class="size-4" />
|
||||
</UiButton>
|
||||
</UiTooltipTrigger>
|
||||
<UiTooltipContent>
|
||||
@@ -536,8 +537,8 @@ onMounted(() => {
|
||||
:disabled="regeneratingToken"
|
||||
@click="handleRegenerateApiToken"
|
||||
>
|
||||
<Icon v-if="regeneratingToken" icon="lucide:loader-2" class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Icon v-else icon="lucide:refresh-cw" class="mr-2 h-4 w-4" />
|
||||
<Loader2 v-if="regeneratingToken" class="mr-2 h-4 w-4 animate-spin" />
|
||||
<RefreshCw v-else class="mr-2 h-4 w-4" />
|
||||
{{ user?.api_token ? '重新生成' : '生成 Token' }}
|
||||
</UiButton>
|
||||
</div>
|
||||
@@ -548,13 +549,13 @@ onMounted(() => {
|
||||
<UiCard class="overflow-hidden">
|
||||
<div class="bg-muted/30 px-6 py-4 border-b flex items-center justify-between">
|
||||
<h2 class="font-semibold flex items-center gap-2">
|
||||
<Icon icon="lucide:receipt" class="size-5 text-primary" />
|
||||
<Receipt class="size-5 text-primary" />
|
||||
最近交易
|
||||
</h2>
|
||||
<UiButton variant="ghost" size="sm" as-child>
|
||||
<router-link to="/admin/finance" class="text-primary">
|
||||
查看全部
|
||||
<Icon icon="lucide:arrow-right" class="ml-1 h-4 w-4" />
|
||||
<ArrowRight class="ml-1 h-4 w-4" />
|
||||
</router-link>
|
||||
</UiButton>
|
||||
</div>
|
||||
@@ -571,8 +572,18 @@ onMounted(() => {
|
||||
class="size-10 rounded-xl flex items-center justify-center"
|
||||
:class="tx.type === 'recharge' ? 'bg-green-500/10' : tx.type === 'refund' ? 'bg-blue-500/10' : 'bg-red-500/10'"
|
||||
>
|
||||
<Icon
|
||||
:icon="tx.type === 'recharge' ? 'lucide:arrow-down-left' : tx.type === 'refund' ? 'lucide:rotate-ccw' : 'lucide:arrow-up-right'"
|
||||
<ArrowDownLeft
|
||||
v-if="tx.type === 'recharge'"
|
||||
:class="getTransactionTypeColor(tx.type)"
|
||||
class="size-5"
|
||||
/>
|
||||
<RotateCcw
|
||||
v-else-if="tx.type === 'refund'"
|
||||
:class="getTransactionTypeColor(tx.type)"
|
||||
class="size-5"
|
||||
/>
|
||||
<ArrowUpRight
|
||||
v-else
|
||||
:class="getTransactionTypeColor(tx.type)"
|
||||
class="size-5"
|
||||
/>
|
||||
@@ -592,7 +603,7 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-12 text-muted-foreground">
|
||||
<Icon icon="lucide:receipt" class="size-16 mx-auto mb-3 opacity-30" />
|
||||
<Receipt class="size-16 mx-auto mb-3 opacity-30" />
|
||||
<p>暂无交易记录</p>
|
||||
</div>
|
||||
</UiCardContent>
|
||||
@@ -612,7 +623,7 @@ onMounted(() => {
|
||||
</p>
|
||||
</div>
|
||||
<div class="size-12 rounded-xl bg-primary/10 flex items-center justify-center">
|
||||
<Icon icon="lucide:crown" class="size-6 text-primary" />
|
||||
<Crown class="size-6 text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground mt-2">
|
||||
@@ -628,7 +639,7 @@ onMounted(() => {
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between text-sm">
|
||||
<span class="text-muted-foreground flex items-center gap-1">
|
||||
<Icon icon="lucide:zap" class="size-4" />
|
||||
<Zap class="size-4" />
|
||||
API 调用
|
||||
</span>
|
||||
<span class="font-medium">{{ formatNumber(subscription?.api_used || 0) }} / {{ formatNumber(subscription?.api_quota || 10000) }}</span>
|
||||
@@ -645,7 +656,7 @@ onMounted(() => {
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between text-sm">
|
||||
<span class="text-muted-foreground flex items-center gap-1">
|
||||
<Icon icon="lucide:hard-drive" class="size-4" />
|
||||
<HardDrive class="size-4" />
|
||||
存储空间
|
||||
</span>
|
||||
<span class="font-medium">{{ formatBytes(subscription?.storage_used || 0) }} / {{ formatBytes(subscription?.storage_quota || 100 * 1024 * 1024) }}</span>
|
||||
@@ -664,7 +675,7 @@ onMounted(() => {
|
||||
<UiCard class="overflow-hidden">
|
||||
<div class="bg-muted/30 px-6 py-4 border-b">
|
||||
<h2 class="font-semibold flex items-center gap-2">
|
||||
<Icon icon="lucide:activity" class="size-5 text-primary" />
|
||||
<Activity class="size-5 text-primary" />
|
||||
账户统计
|
||||
</h2>
|
||||
</div>
|
||||
@@ -745,7 +756,7 @@ onMounted(() => {
|
||||
:disabled="changingPassword || !isPasswordFormValid"
|
||||
@click="handleChangePassword"
|
||||
>
|
||||
<Icon v-if="changingPassword" icon="lucide:loader-2" class="mr-2 h-4 w-4 animate-spin" />
|
||||
<Loader2 v-if="changingPassword" class="mr-2 h-4 w-4 animate-spin" />
|
||||
确认修改
|
||||
</UiButton>
|
||||
</UiDialogFooter>
|
||||
|
||||
Reference in New Issue
Block a user