feat(theme): 更新设计系统至v3.0并优化UI组件

- 升级主题系统为SionRUI AI创意设计系统v3.0,采用蓝紫科技色调
- 优化TopNav组件,改进毛玻璃效果和边框样式
- 增强VideoPreviewModal的视频预览布局和响应式设计
- 重构IdentifyFace页面,使用shadcn主题和Tailwind风格
- 更新Benchmark相关组件,统一使用设计系统颜色变量
- 移除Agents页面多余的边框样式
- 改进深色模式配色方案,增强霓虹科技感
This commit is contained in:
2026-03-18 23:30:43 +08:00
parent bcfe39b32c
commit 7987cf35f9
8 changed files with 271 additions and 361 deletions

View File

@@ -18,7 +18,8 @@ const shouldShowUser = computed(() => {
<template>
<header
class="p-1 fixed top-0 left-0 right-0 flex items-center px-6 h-[70px] bg-background/95 backdrop-blur-sm border-b border-border z-50"
class="p-1 fixed top-0 left-0 right-0 flex items-center px-6 h-[70px] bg-background/80 border-b border-border/50 z-50"
style="backdrop-filter: blur(12px)"
>
<div class="flex items-center gap-4 flex-1">
<BrandLogo :size="36" />
@@ -40,6 +41,10 @@ const shouldShowUser = computed(() => {
</template>
<style scoped>
header {
backdrop-filter: blur(12px);
}
.theme-toggle {
display: flex;
align-items: center;

View File

@@ -63,6 +63,8 @@ const handleClose = () => {
max-height: 90vh;
border-radius: var(--radius-lg);
overflow: hidden;
display: flex;
flex-direction: column;
}
.video-container {
@@ -71,14 +73,18 @@ const handleClose = () => {
align-items: center;
justify-content: center;
padding: 0;
overflow: hidden;
flex: 1;
min-height: 0;
}
.preview-video {
max-width: 800px;
max-height: 70vh;
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
display: block;
object-fit: contain;
}
// 响应式:小屏幕

View File

@@ -1,6 +1,6 @@
/* ================================
SionRUI 现代设计系统 v2.0
极简 · 稳重 · 专业
SionRUI AI创意设计系统 v3.0
文案 · 视频 · 智能创作
================================ */
@import "tailwindcss";
@@ -16,114 +16,114 @@
基础颜色 - 纯净白底
======================================== */
--background: oklch(0.995 0 0);
--foreground: oklch(0.15 0.005 260);
--foreground: oklch(0.15 0.01 300);
--card: oklch(1 0 0);
--card-foreground: oklch(0.15 0.005 260);
--card-foreground: oklch(0.15 0.01 300);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.15 0.005 260);
--popover-foreground: oklch(0.15 0.01 300);
/* ========================================
主色 - 沉稳深蓝 (低调专业)
主色 - 科技蓝紫 (轻盈活力)
======================================== */
--primary: oklch(0.35 0.06 255);
--primary: oklch(0.55 0.14 270);
--primary-foreground: oklch(0.99 0 0);
--primary-hover: oklch(0.28 0.05 255);
--primary-hover: oklch(0.48 0.16 270);
/* ========================================
次要色 - 冷灰
次要色 - 淡品红
======================================== */
--secondary: oklch(0.96 0.003 260);
--secondary-foreground: oklch(0.22 0.005 260);
--secondary: oklch(0.96 0.02 320);
--secondary-foreground: oklch(0.35 0.12 320);
/* ========================================
静音色
======================================== */
--muted: oklch(0.96 0.002 260);
--muted-foreground: oklch(0.50 0.008 260);
--muted: oklch(0.96 0.008 300);
--muted-foreground: oklch(0.48 0.015 300);
/* ========================================
强调色 - 低饱和蓝灰
强调色 - AI科技青
======================================== */
--accent: oklch(0.94 0.008 255);
--accent-foreground: oklch(0.22 0.010 255);
--accent: oklch(0.90 0.06 195);
--accent-foreground: oklch(0.20 0.08 195);
/* ========================================
功能色 - 柔和
功能色 - 鲜活
======================================== */
--destructive: oklch(0.55 0.18 25);
--destructive: oklch(0.58 0.22 25);
--destructive-foreground: oklch(0.99 0 0);
--success: oklch(0.55 0.12 145);
--warning: oklch(0.65 0.12 80);
--info: oklch(0.50 0.10 255);
--success: oklch(0.62 0.18 155);
--warning: oklch(0.75 0.18 80);
--info: oklch(0.65 0.14 195);
/* ========================================
边框与输入
======================================== */
--border: oklch(0.92 0.002 260);
--input: oklch(0.92 0.002 260);
--ring: oklch(0.35 0.06 255);
--border: oklch(0.92 0.005 300);
--input: oklch(0.92 0.005 300);
--ring: oklch(0.55 0.14 270);
/* ========================================
图表色 - 低调渐变
图表色 - 轻盈渐变
======================================== */
--chart-1: oklch(0.50 0.08 255);
--chart-2: oklch(0.55 0.06 200);
--chart-3: oklch(0.55 0.05 150);
--chart-4: oklch(0.60 0.04 90);
--chart-5: oklch(0.55 0.08 25);
--chart-1: oklch(0.58 0.14 270);
--chart-2: oklch(0.62 0.12 200);
--chart-3: oklch(0.65 0.10 150);
--chart-4: oklch(0.68 0.08 90);
--chart-5: oklch(0.60 0.12 25);
/* ========================================
侧边栏
======================================== */
--sidebar: oklch(0.99 0 0);
--sidebar-foreground: oklch(0.15 0.005 260);
--sidebar-primary: oklch(0.35 0.06 255);
--sidebar: oklch(0.995 0 0);
--sidebar-foreground: oklch(0.15 0.01 300);
--sidebar-primary: oklch(0.55 0.14 270);
--sidebar-primary-foreground: oklch(0.99 0 0);
--sidebar-accent: oklch(0.94 0.008 255);
--sidebar-accent-foreground: oklch(0.22 0.010 255);
--sidebar-border: oklch(0.92 0.002 260);
--sidebar-ring: oklch(0.35 0.06 255);
--sidebar-accent: oklch(0.90 0.06 195);
--sidebar-accent-foreground: oklch(0.20 0.08 195);
--sidebar-border: oklch(0.92 0.005 300);
--sidebar-ring: oklch(0.55 0.14 270);
/* ========================================
品牌色阶 - 蓝渐变
品牌色阶 - 蓝渐变
======================================== */
--color-primary-50: oklch(0.97 0.003 255);
--color-primary-100: oklch(0.93 0.005 255);
--color-primary-200: oklch(0.86 0.008 255);
--color-primary-300: oklch(0.72 0.020 255);
--color-primary-400: oklch(0.55 0.035 255);
--color-primary-500: oklch(0.40 0.055 255);
--color-primary-600: oklch(0.35 0.060 255);
--color-primary-700: oklch(0.28 0.055 255);
--color-primary-50: oklch(0.97 0.008 270);
--color-primary-100: oklch(0.94 0.020 270);
--color-primary-200: oklch(0.87 0.040 270);
--color-primary-300: oklch(0.75 0.08 270);
--color-primary-400: oklch(0.62 0.12 270);
--color-primary-500: oklch(0.55 0.14 270);
--color-primary-600: oklch(0.48 0.16 270);
--color-primary-700: oklch(0.40 0.14 270);
/* ========================================
中性灰 - 偏蓝冷色
中性灰 - 微紫
======================================== */
--color-gray-50: oklch(0.98 0.001 260);
--color-gray-100: oklch(0.96 0.002 260);
--color-gray-200: oklch(0.92 0.003 260);
--color-gray-300: oklch(0.85 0.004 260);
--color-gray-400: oklch(0.68 0.005 260);
--color-gray-500: oklch(0.50 0.006 260);
--color-gray-600: oklch(0.40 0.007 260);
--color-gray-700: oklch(0.32 0.006 260);
--color-gray-800: oklch(0.24 0.005 260);
--color-gray-900: oklch(0.15 0.004 260);
--color-gray-50: oklch(0.98 0.004 300);
--color-gray-100: oklch(0.95 0.006 300);
--color-gray-200: oklch(0.90 0.008 300);
--color-gray-300: oklch(0.82 0.008 300);
--color-gray-400: oklch(0.65 0.006 300);
--color-gray-500: oklch(0.48 0.005 300);
--color-gray-600: oklch(0.38 0.004 300);
--color-gray-700: oklch(0.30 0.003 300);
--color-gray-800: oklch(0.22 0.002 300);
--color-gray-900: oklch(0.15 0.001 300);
/* ========================================
语义化颜色 (兼容旧变量)
======================================== */
--color-bg: oklch(0.985 0.001 260);
--color-bg-page: oklch(0.975 0.001 260);
--color-bg: oklch(0.995 0 0);
--color-bg-page: oklch(0.985 0.002 300);
--color-bg-card: oklch(1 0 0);
--color-surface: oklch(1 0 0);
--color-text: oklch(0.15 0.005 260);
--color-text-primary: oklch(0.15 0.005 260);
--color-text-secondary: oklch(0.42 0.006 260);
--color-text-muted: oklch(0.55 0.005 260);
--color-text-disabled: oklch(0.72 0.003 260);
--color-text: oklch(0.15 0.01 300);
--color-text-primary: oklch(0.15 0.01 300);
--color-text-secondary: oklch(0.40 0.015 300);
--color-text-muted: oklch(0.55 0.01 300);
--color-text-disabled: oklch(0.72 0.005 300);
--color-text-inverse: oklch(0.99 0 0);
--color-border: oklch(0.92 0.002 260);
--color-border: oklch(0.92 0.005 300);
--color-primary-hover: var(--color-primary-400);
/* ========================================
@@ -210,95 +210,95 @@
}
/* ================================
2. 深色模式
2. 深色模式 - 霓虹科技
================================ */
.dark,
[data-theme="dark"] {
/* 基础颜色 */
--background: oklch(0.12 0.004 260);
--foreground: oklch(0.94 0.002 260);
--card: oklch(0.15 0.005 260);
--card-foreground: oklch(0.94 0.002 260);
--popover: oklch(0.15 0.005 260);
--popover-foreground: oklch(0.94 0.002 260);
/* 基础颜色 - 深紫夜空 */
--background: oklch(0.13 0.015 290);
--foreground: oklch(0.94 0.005 300);
--card: oklch(0.17 0.018 290);
--card-foreground: oklch(0.94 0.005 300);
--popover: oklch(0.17 0.018 290);
--popover-foreground: oklch(0.94 0.005 300);
/* 主色 - 深色模式提亮 */
--primary: oklch(0.65 0.08 255);
--primary-foreground: oklch(0.12 0.004 260);
--primary-hover: oklch(0.72 0.07 255);
/* 主色 - 霓虹蓝紫 */
--primary: oklch(0.70 0.14 270);
--primary-foreground: oklch(0.13 0.015 290);
--primary-hover: oklch(0.76 0.12 270);
/* 次要色 */
--secondary: oklch(0.22 0.006 260);
--secondary-foreground: oklch(0.94 0.002 260);
--secondary: oklch(0.24 0.02 290);
--secondary-foreground: oklch(0.94 0.005 300);
/* 静音色 */
--muted: oklch(0.22 0.006 260);
--muted-foreground: oklch(0.62 0.008 260);
--muted: oklch(0.24 0.015 290);
--muted-foreground: oklch(0.64 0.01 300);
/* 强调色 */
--accent: oklch(0.25 0.012 255);
--accent-foreground: oklch(0.94 0.002 260);
/* 强调色 - 霓虹青 */
--accent: oklch(0.28 0.05 195);
--accent-foreground: oklch(0.94 0.005 300);
/* 功能色 */
--destructive: oklch(0.60 0.16 25);
--destructive-foreground: oklch(0.94 0.002 260);
--success: oklch(0.58 0.10 145);
--warning: oklch(0.68 0.10 80);
--info: oklch(0.55 0.08 255);
--destructive: oklch(0.65 0.20 25);
--destructive-foreground: oklch(0.94 0.005 300);
--success: oklch(0.65 0.16 155);
--warning: oklch(0.75 0.16 80);
--info: oklch(0.68 0.12 195);
/* 边框与输入 */
--border: oklch(0.26 0.006 260);
--input: oklch(0.26 0.006 260);
--ring: oklch(0.65 0.08 255);
--border: oklch(0.28 0.015 290);
--input: oklch(0.28 0.015 290);
--ring: oklch(0.70 0.14 270);
/* 图表色 */
--chart-1: oklch(0.62 0.08 255);
--chart-2: oklch(0.60 0.06 200);
--chart-3: oklch(0.58 0.05 150);
--chart-4: oklch(0.65 0.04 90);
--chart-5: oklch(0.58 0.08 25);
/* 图表色 - 霓虹渐变 */
--chart-1: oklch(0.72 0.22 320);
--chart-2: oklch(0.70 0.16 280);
--chart-3: oklch(0.72 0.14 195);
--chart-4: oklch(0.75 0.10 150);
--chart-5: oklch(0.70 0.18 25);
/* 侧边栏 */
--sidebar: oklch(0.12 0.004 260);
--sidebar-foreground: oklch(0.94 0.002 260);
--sidebar-primary: oklch(0.65 0.08 255);
--sidebar-primary-foreground: oklch(0.12 0.004 260);
--sidebar-accent: oklch(0.25 0.012 255);
--sidebar-accent-foreground: oklch(0.94 0.002 260);
--sidebar-border: oklch(0.26 0.006 260);
--sidebar-ring: oklch(0.65 0.08 255);
--sidebar: oklch(0.13 0.015 290);
--sidebar-foreground: oklch(0.94 0.005 300);
--sidebar-primary: oklch(0.70 0.14 270);
--sidebar-primary-foreground: oklch(0.13 0.015 290);
--sidebar-accent: oklch(0.28 0.05 195);
--sidebar-accent-foreground: oklch(0.94 0.005 300);
--sidebar-border: oklch(0.28 0.015 290);
--sidebar-ring: oklch(0.70 0.14 270);
/* 灰色系 */
--color-gray-50: oklch(0.22 0.006 260);
--color-gray-100: oklch(0.28 0.008 260);
--color-gray-200: oklch(0.36 0.008 260);
--color-gray-300: oklch(0.48 0.008 260);
--color-gray-400: oklch(0.62 0.006 260);
--color-gray-500: oklch(0.76 0.004 260);
--color-gray-600: oklch(0.84 0.003 260);
--color-gray-700: oklch(0.90 0.002 260);
--color-gray-800: oklch(0.95 0.001 260);
/* 灰色系 - 反转 */
--color-gray-50: oklch(0.24 0.015 290);
--color-gray-100: oklch(0.30 0.012 290);
--color-gray-200: oklch(0.38 0.010 290);
--color-gray-300: oklch(0.50 0.008 290);
--color-gray-400: oklch(0.64 0.006 290);
--color-gray-500: oklch(0.78 0.004 290);
--color-gray-600: oklch(0.86 0.002 290);
--color-gray-700: oklch(0.91 0.001 290);
--color-gray-800: oklch(0.95 0.001 290);
--color-gray-900: oklch(0.98 0 0);
/* 语义化颜色 */
--color-bg: oklch(0.10 0.004 260);
--color-bg-page: oklch(0.10 0.004 260);
--color-bg-card: oklch(0.15 0.005 260);
--color-surface: oklch(0.15 0.005 260);
--color-text: oklch(0.94 0.002 260);
--color-text-primary: oklch(0.94 0.002 260);
--color-text-secondary: oklch(0.68 0.006 260);
--color-text-muted: oklch(0.50 0.006 260);
--color-text-disabled: oklch(0.36 0.006 260);
--color-text-inverse: oklch(0.12 0.004 260);
--color-border: oklch(0.26 0.006 260);
--color-bg: oklch(0.11 0.015 290);
--color-bg-page: oklch(0.11 0.015 290);
--color-bg-card: oklch(0.17 0.018 290);
--color-surface: oklch(0.17 0.018 290);
--color-text: oklch(0.94 0.005 300);
--color-text-primary: oklch(0.94 0.005 300);
--color-text-secondary: oklch(0.68 0.008 300);
--color-text-muted: oklch(0.50 0.006 300);
--color-text-disabled: oklch(0.36 0.004 300);
--color-text-inverse: oklch(0.13 0.015 290);
--color-border: oklch(0.28 0.015 290);
/* 主色阶 */
--color-primary-50: oklch(0.20 0.015 255);
--color-primary-100: oklch(0.28 0.020 255);
--color-primary-400: oklch(0.60 0.08 255);
--color-primary-500: oklch(0.65 0.08 255);
--color-primary-hover: oklch(0.72 0.07 255);
/* 主色阶 - 深色版本 */
--color-primary-50: oklch(0.22 0.025 270);
--color-primary-100: oklch(0.30 0.04 270);
--color-primary-400: oklch(0.65 0.12 270);
--color-primary-500: oklch(0.70 0.14 270);
--color-primary-hover: oklch(0.76 0.12 270);
}
/* ================================
@@ -417,12 +417,12 @@ html {
/* 选中文本样式 */
::selection {
background: oklch(0.35 0.06 255 / 0.15);
background: oklch(0.55 0.14 270 / 0.20);
color: inherit;
}
.dark ::selection {
background: oklch(0.65 0.08 255 / 0.25);
background: oklch(0.70 0.14 270 / 0.30);
}
/* 焦点样式 */

View File

@@ -428,7 +428,6 @@ onMounted(() => {
position: relative;
padding: 48px 0 32px;
background: linear-gradient(90deg, rgba(56, 189, 248, 0.12) 0%, rgba(99, 102, 241, 0.14) 100%);
border-bottom: 1px solid var(--color-gray-200);
z-index: 10;
}

View File

@@ -260,9 +260,9 @@ defineOptions({ name: 'ContentStyleBenchmark' })
/>
<!-- 空态 -->
<Card v-if="!data.length" class="border-0 shadow-sm bg-white/80 backdrop-blur-sm">
<Card v-if="!data.length" class="border-0 shadow-sm bg-card/80" style="backdrop-filter: blur(8px)">
<CardContent class="flex flex-col items-center justify-center py-16 min-h-[420px]">
<svg class="text-gray-300 mb-4" width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg class="text-muted-foreground/40 mb-4" width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="30" width="80" height="60" rx="4" stroke="currentColor" stroke-width="2" fill="none" opacity="0.3"/>
<circle cx="40" cy="50" r="8" fill="currentColor" opacity="0.4"/>
<rect x="54" y="47" width="40" height="6" rx="3" fill="currentColor" opacity="0.4"/>
@@ -271,7 +271,7 @@ defineOptions({ name: 'ContentStyleBenchmark' })
<line x1="32" y1="82" x2="88" y2="82" stroke="currentColor" stroke-width="2" opacity="0.3"/>
<line x1="32" y1="89" x2="72" y2="89" stroke="currentColor" stroke-width="2" opacity="0.3"/>
</svg>
<p class="text-gray-500">暂无数据请点击开始分析</p>
<p class="text-muted-foreground">暂无数据请点击开始分析</p>
</CardContent>
</Card>

View File

@@ -43,13 +43,13 @@ function handleReset() {
</script>
<template>
<Card class="border-0 shadow-sm bg-white/80 backdrop-blur-sm p-0">
<Card class="border-0 shadow-sm bg-card/80" style="backdrop-filter: blur(8px)">
<CardContent class="p-5 space-y-5">
<!-- 平台选择 -->
<div class="space-y-2">
<Label class="text-sm font-medium text-gray-700">平台</Label>
<Label class="text-sm font-medium text-foreground">平台</Label>
<RadioGroup v-model="form.platform" class="flex gap-2">
<div class="inline-flex items-center justify-center px-4 h-9 text-sm font-medium text-white bg-primary-500 border border-primary-500 rounded-md cursor-pointer">
<div class="inline-flex items-center justify-center px-4 h-9 text-sm font-medium text-primary-foreground bg-primary border border-primary rounded-md cursor-pointer">
<RadioGroupItem value="抖音" id="douyin" class="hidden" />
<label for="douyin" class="cursor-pointer">抖音</label>
</div>
@@ -58,34 +58,34 @@ function handleReset() {
<!-- 链接输入 -->
<div class="space-y-2">
<Label class="text-sm font-medium text-gray-700">主页/视频链接</Label>
<Label class="text-sm font-medium text-foreground">主页/视频链接</Label>
<Input
v-model="form.url"
placeholder="粘贴抖音主页或视频链接"
class="h-11 bg-gray-50 border-gray-300"
class="h-11 bg-muted border-input"
/>
</div>
<!-- 分析数量 -->
<div class="space-y-2">
<Label class="text-sm font-medium text-gray-700">分析数量</Label>
<Label class="text-sm font-medium text-foreground">分析数量</Label>
<div class="flex items-center gap-3">
<span class="min-w-[32px] text-sm font-semibold text-primary-500 text-center">{{ form.count }}</span>
<span class="min-w-[32px] text-sm font-semibold text-primary text-center">{{ form.count }}</span>
<input
type="range"
v-model.number="form.count"
min="1"
max="40"
class="flex-1 h-1 bg-gray-200 rounded-full appearance-none cursor-pointer
class="flex-1 h-1 bg-muted rounded-full appearance-none cursor-pointer
[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4
[&::-webkit-slider-thumb]:bg-primary-500 [&::-webkit-slider-thumb]:rounded-full
[&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:rounded-full
[&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:transition-transform
[&::-webkit-slider-thumb]:hover:scale-110 [&::-webkit-slider-thumb]:hover:shadow-[0_0_0_4px_rgba(59,130,246,0.15)]
[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:bg-primary-500
[&::-webkit-slider-thumb]:hover:scale-110
[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:bg-primary
[&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-none [&::-moz-range-thumb]:cursor-pointer"
/>
</div>
<p class="text-sm text-gray-500 mt-1">建议 20-30 个内容</p>
<p class="text-sm text-muted-foreground mt-1">建议 20-30 个内容</p>
</div>
<!-- 操作按钮 -->
@@ -99,7 +99,7 @@ function handleReset() {
/>
<Button variant="outline" @click="handleReset">重置</Button>
</div>
<p class="text-xs text-gray-500 text-center">每次分析将消耗积分消耗量与分析数量相关</p>
<p class="text-xs text-muted-foreground text-center">每次分析将消耗积分消耗量与分析数量相关</p>
</CardContent>
</Card>
</template>

View File

@@ -1,5 +1,5 @@
<script setup>
import { reactive, computed, ref } from 'vue'
import { computed, ref } from 'vue'
import { formatTime } from '../utils/benchmarkUtils'
import GradientButton from '@/components/GradientButton.vue'
import { Button } from '@/components/ui/button'
@@ -15,6 +15,8 @@ import {
} from '@/components/ui/table'
import { Spinner } from '@/components/ui/spinner'
import { Badge } from '@/components/ui/badge'
const props = defineProps({
data: { type: Array, required: true },
selectedRowKeys: { type: Array, required: true },
@@ -46,7 +48,7 @@ const isAllSelected = computed(() => {
return props.data.length > 0 && props.selectedRowKeys.length === props.data.length
})
// 半选状态部分选中
// 半选状态(部分选中)
const isIndeterminate = computed(() => {
const selectedLen = props.selectedRowKeys.length
return selectedLen > 0 && selectedLen < props.data.length
@@ -105,10 +107,10 @@ function formatNumber(value) {
</script>
<template>
<Card v-if="data.length > 0" class="border-0 shadow-sm bg-white/80 backdrop-blur-sm">
<Card v-if="data.length > 0" class="border-0 shadow-sm bg-card/80" style="backdrop-filter: blur(8px)">
<CardContent class="p-5">
<div class="flex justify-between items-center mb-4">
<div class="text-xs text-gray-500 mb-3">分析结果</div>
<div class="text-xs text-muted-foreground mb-3">分析结果</div>
<div class="flex items-center gap-3">
<GradientButton
:text="`导出Excel (${selectedRowKeys.length}/20)`"
@@ -131,7 +133,8 @@ function formatNumber(value) {
<div class="overflow-x-auto max-h-[calc(100vh-400px)] overflow-y-auto
[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5
[&::-webkit-scrollbar-track]:bg-transparent
[&::-webkit-scrollbar-thumb]:bg-gray-300 [&::-webkit-scrollbar-thumb]:rounded">
[&::-webkit-scrollbar-thumb]:bg-muted-foreground/30 [&::-webkit-scrollbar-thumb]:rounded"
>
<Table>
<TableHeader>
<TableRow>
@@ -179,7 +182,7 @@ function formatNumber(value) {
v-else
v-for="record in sortedData"
:key="record.id"
class="hover:bg-gray-50 transition-colors"
class="hover:bg-muted/50 transition-colors"
>
<TableCell class="w-[60px]">
<Checkbox
@@ -192,17 +195,15 @@ function formatNumber(value) {
<div class="relative inline-block">
<a v-if="record.cover && record.share_url" :href="record.share_url" target="_blank" class="block cursor-pointer group">
<img :src="record.cover" alt="cover" loading="lazy"
class="w-20 h-11 object-cover rounded border border-gray-200
class="w-20 h-11 object-cover rounded border border-border
transition-all duration-200 group-hover:scale-[1.02] group-hover:shadow-md" />
<span v-if="record.is_top"
class="absolute top-1 left-1 px-1.5 py-0.5 text-[10px] text-white bg-red-500 rounded">置顶</span>
<Badge v-if="record.is_top" variant="destructive" class="absolute top-1 left-1 text-[10px] px-1.5 py-0.5">置顶</Badge>
</a>
<template v-else-if="record.cover">
<img :src="record.cover" alt="cover" loading="lazy" class="w-20 h-11 object-cover rounded border border-gray-200" />
<span v-if="record.is_top"
class="absolute top-1 left-1 px-1.5 py-0.5 text-[10px] text-white bg-red-500 rounded">置顶</span>
<img :src="record.cover" alt="cover" loading="lazy" class="w-20 h-11 object-cover rounded border border-border" />
<Badge v-if="record.is_top" variant="destructive" class="absolute top-1 left-1 text-[10px] px-1.5 py-0.5">置顶</Badge>
</template>
<span v-else class="text-xs text-gray-400">无封面</span>
<span v-else class="text-xs text-muted-foreground">无封面</span>
</div>
</TableCell>
<TableCell>
@@ -220,7 +221,7 @@ function formatNumber(value) {
</div>
<!-- 加载更多 -->
<div v-if="hasMore" class="text-center pt-4 border-t border-gray-100">
<div v-if="hasMore" class="text-center pt-4 border-t border-border">
<Button
:disabled="loadingMore"
size="lg"
@@ -231,9 +232,9 @@ function formatNumber(value) {
<Spinner v-if="loadingMore" class="mr-2 h-4 w-4" />
{{ loadingMore ? '加载中...' : '加载更多内容' }}
</Button>
<div class="text-xs text-gray-500 mt-2">已加载 {{ data.length }} </div>
<div class="text-xs text-muted-foreground mt-2">已加载 {{ data.length }} </div>
</div>
<div v-else-if="data.length > 0" class="text-center py-3 text-xs text-gray-500 border-t border-gray-100">
<div v-else-if="data.length > 0" class="text-center py-3 text-xs text-muted-foreground border-t border-border">
已加载全部内容
</div>
</CardContent>

View File

@@ -356,37 +356,12 @@ onMounted(async () => {
<style scoped lang="less">
// ========================================
// Notion + VoiceSelector 蓝紫主题
// shadcn 主题 + Tailwind 风格
// ========================================
// Color Palette - 与 VoiceSelector 协调
@bg-page: #fafbfc;
@bg-block: #ffffff;
@text-primary: #1e293b;
@text-secondary: #64748b;
@text-tertiary: #94a3b8;
@border-light: rgba(59, 130, 246, 0.1);
@border-medium: rgba(59, 130, 246, 0.2);
// 主色 - 使用设计系统变量
@accent-blue: #3b82f6;
@accent-purple: #8b5cf6;
@accent-gradient: var(--primary);
@accent-green: #10b981;
@accent-red: #ef4444;
@accent-orange: #f59e0b;
// Shadows - 柔和蓝调
@shadow-sm: 0 1px 2px rgba(59, 130, 246, 0.04);
@shadow-md: 0 4px 12px rgba(59, 130, 246, 0.08);
@shadow-lg: 0 8px 24px rgba(59, 130, 246, 0.12);
// Typography
@font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, 'Apple Color Emoji', Arial, sans-serif;
.notion-page {
min-height: 100vh;
background: @bg-page;
background: var(--background);
padding: 48px 64px;
margin: 0 auto;
@@ -395,34 +370,6 @@ onMounted(async () => {
}
}
// Page Header
.page-header {
margin-bottom: 40px;
.header-icon {
font-size: 56px;
margin-bottom: 16px;
line-height: 1;
}
.header-title {
font-family: @font-sans;
font-size: 40px;
font-weight: 700;
color: @text-primary;
margin: 0 0 8px 0;
letter-spacing: -0.5px;
line-height: 1.2;
}
.header-desc {
font-size: 16px;
color: @text-secondary;
margin: 0;
line-height: 1.5;
}
}
// Main Content Layout
.main-content {
display: grid;
@@ -437,18 +384,17 @@ onMounted(async () => {
// Content Block
.content-block {
background: @bg-block;
border-radius: 12px;
background: var(--card);
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: @shadow-md;
border: 1px solid @border-light;
box-shadow: var(--shadow-md);
border: 1px solid var(--border);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
&:hover {
box-shadow: @shadow-lg;
border-color: rgba(59, 130, 246, 0.2);
box-shadow: var(--shadow-lg);
}
}
@@ -459,7 +405,7 @@ onMounted(async () => {
gap: 10px;
margin-bottom: 20px;
padding-bottom: 14px;
border-bottom: 1px solid rgba(59, 130, 246, 0.08);
border-bottom: 1px solid var(--border);
.block-emoji {
font-size: 20px;
@@ -469,7 +415,7 @@ onMounted(async () => {
.block-title {
font-size: 15px;
font-weight: 600;
color: @text-primary;
color: var(--foreground);
margin: 0;
}
}
@@ -479,9 +425,9 @@ onMounted(async () => {
display: flex;
gap: 8px;
margin-bottom: 20px;
background: @bg-page;
background: var(--muted);
padding: 4px;
border-radius: 6px;
border-radius: var(--radius);
}
.toggle-btn {
@@ -493,42 +439,41 @@ onMounted(async () => {
padding: 10px 16px;
border: none;
background: transparent;
border-radius: 8px;
border-radius: calc(var(--radius) - 2px);
font-size: 13px;
font-weight: 500;
color: @text-secondary;
color: var(--muted-foreground);
cursor: pointer;
transition: all 0.2s ease;
&:hover {
color: @text-primary;
background: rgba(59, 130, 246, 0.05);
color: var(--foreground);
background: var(--accent);
}
&.active {
background: @bg-block;
color: @accent-blue;
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.12);
background: var(--background);
color: var(--primary);
box-shadow: var(--shadow-sm);
}
}
// Upload Area
.upload-area {
min-height: 200px;
border: 1.5px dashed rgba(59, 130, 246, 0.2);
border-radius: 10px;
background: rgba(59, 130, 246, 0.02);
border: 1.5px dashed var(--border);
border-radius: var(--radius);
background: var(--muted);
transition: all 0.25s ease;
&.dragover {
border-color: @accent-blue;
background: rgba(59, 130, 246, 0.06);
border-color: var(--primary);
background: var(--accent);
}
&.has-video {
border-style: solid;
border-color: rgba(59, 130, 246, 0.1);
background: @bg-block;
background: var(--card);
}
}
@@ -550,7 +495,7 @@ onMounted(async () => {
width: 56px;
height: 56px;
border-radius: 50%;
background: rgba(59, 130, 246, 0.08);
background: var(--accent);
display: flex;
align-items: center;
justify-content: center;
@@ -559,25 +504,28 @@ onMounted(async () => {
.upload-icon {
font-size: 24px;
color: @accent-blue;
color: var(--primary);
}
}
.upload-empty:hover .upload-icon-wrapper {
background: rgba(59, 130, 246, 0.12);
transform: scale(1.05);
background: var(--primary);
.upload-icon {
color: var(--primary-foreground);
}
}
.upload-text {
font-size: 15px;
font-weight: 500;
color: @text-primary;
color: var(--foreground);
margin-bottom: 6px;
}
.upload-hint {
font-size: 13px;
color: @text-tertiary;
color: var(--muted-foreground);
}
.upload-preview {
@@ -587,7 +535,7 @@ onMounted(async () => {
.preview-video {
width: 100%;
max-height: 260px;
border-radius: 4px;
border-radius: calc(var(--radius) - 2px);
background: #191919;
}
@@ -599,31 +547,31 @@ onMounted(async () => {
padding: 8px 14px;
font-size: 13px;
font-weight: 500;
color: @text-secondary;
color: var(--muted-foreground);
background: transparent;
border: 1px solid rgba(59, 130, 246, 0.15);
border-radius: 8px;
border: 1px solid var(--border);
border-radius: var(--radius);
cursor: pointer;
transition: all 0.2s ease;
&:hover {
color: @accent-blue;
border-color: @accent-blue;
background: rgba(59, 130, 246, 0.05);
color: var(--primary);
border-color: var(--primary);
background: var(--accent);
}
}
// Library Video Preview
.library-preview {
padding: 16px;
background: rgba(59, 130, 246, 0.03);
border-radius: 10px;
border: 1px solid rgba(59, 130, 246, 0.1);
background: var(--muted);
border-radius: var(--radius);
border: 1px solid var(--border);
.preview-video {
width: 100%;
max-height: 260px;
border-radius: 4px;
border-radius: calc(var(--radius) - 2px);
background: #191919;
}
@@ -639,7 +587,7 @@ onMounted(async () => {
.video-thumbnail {
width: 120px;
height: 68px;
border-radius: 4px;
border-radius: calc(var(--radius) - 2px);
overflow: hidden;
background: #191919;
flex-shrink: 0;
@@ -659,7 +607,7 @@ onMounted(async () => {
.video-name {
font-size: 14px;
font-weight: 500;
color: @text-primary;
color: var(--foreground);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -669,22 +617,24 @@ onMounted(async () => {
.video-info {
font-size: 12px;
color: @text-tertiary;
color: var(--muted-foreground);
}
// Process Status
.process-status {
margin-top: 14px;
padding: 12px 16px;
background: rgba(59, 130, 246, 0.04);
border-radius: 8px;
background: var(--muted);
border-radius: var(--radius);
&.recognized {
background: rgba(16, 185, 129, 0.08);
background: oklch(0.95 0.05 145);
color: oklch(0.35 0.08 145);
}
&.error {
background: rgba(239, 68, 68, 0.08);
background: oklch(0.95 0.05 25);
color: oklch(0.40 0.12 25);
}
}
@@ -693,22 +643,22 @@ onMounted(async () => {
align-items: center;
gap: 10px;
font-size: 13px;
color: @text-secondary;
color: var(--muted-foreground);
&.success {
color: @accent-green;
color: oklch(0.35 0.08 145);
}
&.error {
color: @accent-red;
color: oklch(0.40 0.12 25);
}
}
.custom-spinner {
width: 14px;
height: 14px;
border: 2px solid rgba(59, 130, 246, 0.2);
border-top-color: @accent-blue;
border: 2px solid var(--border);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@@ -724,7 +674,7 @@ onMounted(async () => {
padding: 0;
font-size: 13px;
font-weight: 500;
color: @accent-blue;
color: var(--primary);
background: none;
border: none;
cursor: pointer;
@@ -743,7 +693,7 @@ onMounted(async () => {
display: block;
font-size: 12px;
font-weight: 600;
color: @text-secondary;
color: var(--muted-foreground);
text-transform: uppercase;
letter-spacing: 0.5px;
}
@@ -758,29 +708,24 @@ onMounted(async () => {
.notion-textarea {
width: 100%;
:deep(.ant-input) {
:deep(textarea) {
border: none;
border-radius: 8px;
border-radius: var(--radius);
font-size: 14px;
line-height: 1.6;
padding: 14px 16px;
background: rgba(59, 130, 246, 0.03);
color: @text-primary;
background: var(--muted);
color: var(--foreground);
&::placeholder {
color: @text-tertiary;
color: var(--muted-foreground);
}
&:focus {
background: @bg-block;
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.2);
background: var(--background);
box-shadow: 0 0 0 1px var(--ring);
}
}
:deep(.ant-input-data-count) {
color: @text-tertiary;
font-size: 12px;
}
}
.input-footer {
@@ -788,7 +733,7 @@ onMounted(async () => {
align-items: center;
gap: 4px;
font-size: 12px;
color: @text-tertiary;
color: var(--muted-foreground);
margin-top: 8px;
}
@@ -798,17 +743,17 @@ onMounted(async () => {
gap: 4px;
padding: 4px 10px;
background: transparent;
border: 1px solid rgba(59, 130, 246, 0.2);
border-radius: 6px;
border: 1px solid var(--border);
border-radius: calc(var(--radius) - 2px);
font-size: 12px;
font-weight: 500;
color: @accent-blue;
color: var(--primary);
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: rgba(59, 130, 246, 0.06);
border-color: rgba(59, 130, 246, 0.3);
background: var(--accent);
border-color: var(--primary);
}
}
@@ -821,39 +766,17 @@ onMounted(async () => {
.rate-slider {
flex: 1;
:deep(.ant-slider-rail) {
background: rgba(59, 130, 246, 0.1);
}
:deep(.ant-slider-track) {
background: @accent-gradient;
}
:deep(.ant-slider-handle) {
border-color: @accent-blue;
&:hover, &:focus {
border-color: @accent-blue;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.15);
}
}
:deep(.ant-slider-mark-text) {
font-size: 11px;
color: @text-tertiary;
}
}
.rate-value {
font-size: 14px;
font-weight: 600;
color: @text-primary;
color: var(--foreground);
min-width: 40px;
text-align: right;
}
// 生成按钮 - 左右布局,右下角
// 生成按钮
.generate-action {
margin-top: auto;
padding-top: 16px;
@@ -867,23 +790,23 @@ onMounted(async () => {
justify-content: flex-end;
gap: 10px;
padding: 10px 14px;
border-radius: 8px;
border-radius: var(--radius);
font-size: 13px;
font-weight: 500;
&.success {
background: rgba(16, 185, 129, 0.08);
color: @accent-green;
background: oklch(0.95 0.05 145);
color: oklch(0.35 0.08 145);
}
&.error {
background: rgba(239, 68, 68, 0.08);
color: @accent-red;
background: oklch(0.95 0.05 25);
color: oklch(0.40 0.12 25);
}
.inline-btn {
padding: 4px 12px;
border-radius: 6px;
border-radius: calc(var(--radius) - 2px);
border: 1px solid currentColor;
background: transparent;
color: inherit;
@@ -893,7 +816,7 @@ onMounted(async () => {
transition: all 0.2s ease;
&:hover {
background: rgba(16, 185, 129, 0.1);
opacity: 0.8;
}
}
}
@@ -905,17 +828,17 @@ onMounted(async () => {
justify-content: space-between;
padding: 10px 14px;
border: none;
border-radius: 8px;
background: @accent-gradient;
color: white;
border-radius: var(--radius);
background: var(--primary);
color: var(--primary-foreground);
cursor: pointer;
overflow: hidden;
transition: all 0.25s ease;
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.25);
box-shadow: var(--shadow-md);
&:hover:not(.disabled) {
transform: translateY(-1px);
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.35);
box-shadow: var(--shadow-lg);
}
&:active:not(.disabled) {
@@ -924,8 +847,9 @@ onMounted(async () => {
&.disabled {
background: var(--muted);
color: var(--muted-foreground);
cursor: not-allowed;
box-shadow: 0 2px 8px rgba(148, 163, 184, 0.25);
box-shadow: none;
}
.btn-glow {
@@ -992,29 +916,4 @@ onMounted(async () => {
left: 100%;
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.link-btn {
padding: 0;
font-size: 13px;
font-weight: 500;
color: inherit;
background: none;
border: none;
cursor: pointer;
text-decoration: underline;
text-underline-offset: 2px;
&:hover {
opacity: 0.8;
}
}
</style>