feat: 优化
This commit is contained in:
211
.claude/skills/antd-to-shadcn/SKILL.md
Normal file
211
.claude/skills/antd-to-shadcn/SKILL.md
Normal file
@@ -0,0 +1,211 @@
|
||||
---
|
||||
name: antd-to-shadcn
|
||||
description: |
|
||||
Vue 3 项目从 Ant Design Vue 迁移到 shadcn-vue 的专业技能。
|
||||
|
||||
当用户请求以下操作时使用此技能:
|
||||
- 将 Ant Design 组件替换为 shadcn 组件
|
||||
- 迁移表单、表格、弹窗等复杂组件
|
||||
- 保持业务功能不变的前提下现代化 UI
|
||||
- 颜色解耦和主题配置
|
||||
- 组件间距和布局优化
|
||||
|
||||
触发词:迁移、shadcn、antd 替换、组件升级、UI 现代化
|
||||
---
|
||||
|
||||
# Ant Design Vue → shadcn-vue 迁移技能
|
||||
|
||||
将 Vue 3 + Ant Design Vue 项目迁移到 shadcn-vue + TailwindCSS。
|
||||
|
||||
## 核心原则
|
||||
|
||||
1. **业务功能不变** - 只改 UI 层,不改业务逻辑
|
||||
2. **现代化 UI** - 采用 shadcn 设计语言,简洁现代
|
||||
3. **颜色解耦** - 使用 CSS 变量,支持主题切换
|
||||
4. **渐进式迁移** - 逐个组件替换,保持可运行
|
||||
|
||||
## 设计品质要求
|
||||
|
||||
### 抵制过时设计
|
||||
|
||||
**拒绝老气 UI**:
|
||||
- ❌ 粗重边框、多重边框嵌套
|
||||
- ❌ 灰暗沉闷的配色
|
||||
- ❌ 过度装饰的渐变和阴影
|
||||
- ❌ 拥挤无呼吸感的布局
|
||||
- ❌ 间距混乱、缺乏层级
|
||||
|
||||
**拥抱现代设计**:
|
||||
- ✅ 轻量边框或无边框设计
|
||||
- ✅ 明亮有活力的色彩
|
||||
- ✅ 克制的阴影(shadow-sm 为主)
|
||||
- ✅ 充足留白,呼吸感
|
||||
- ✅ 清晰的视觉层级(8px 间距递进)
|
||||
|
||||
### 颜色设计原则
|
||||
|
||||
**年轻活力感**:
|
||||
- 主色饱和度适中(oklch chroma 0.14-0.18)
|
||||
- 避免过于灰暗的中间色
|
||||
- 使用微妙渐变增加质感
|
||||
- 深色模式保持足够对比度
|
||||
|
||||
**发现优秀配色时**:
|
||||
1. 提取关键颜色值
|
||||
2. 添加到 `style.css` 设计令牌
|
||||
3. 使用语义化命名(如 `--color-accent-blue`)
|
||||
4. 在组件中通过变量引用
|
||||
|
||||
### 间距层级规范
|
||||
|
||||
```
|
||||
紧密:gap-1 (4px) - 图标与文字
|
||||
标准:gap-2 (8px) - 同组元素
|
||||
舒适:gap-3 (12px) - 表单项之间
|
||||
宽松:gap-4 (16px) - 卡片内容
|
||||
分区:gap-6 (24px) - 不同区块
|
||||
```
|
||||
|
||||
## 项目上下文
|
||||
|
||||
```
|
||||
前端目录: frontend/app/web-gold/
|
||||
组件目录: src/components/ui/ # shadcn 组件
|
||||
主题文件: src/theme.css # shadcn 主题变量
|
||||
样式文件: src/style.css # 设计令牌
|
||||
```
|
||||
|
||||
## 迁移工作流
|
||||
|
||||
### Step 1: 识别 Ant Design 组件
|
||||
|
||||
扫描文件中的 Ant Design 导入:
|
||||
```vue
|
||||
// 需要替换的模式
|
||||
import { Button, Input, Form, Table, Modal, ... } from 'ant-design-vue'
|
||||
import { IconName } from '@ant-design/icons-vue'
|
||||
import { message, notification } from 'ant-design-vue'
|
||||
```
|
||||
|
||||
### Step 2: 组件映射
|
||||
|
||||
参见 [COMPONENT_MAP.md](references/COMPONENT_MAP.md) 获取完整映射表。
|
||||
|
||||
快速参考:
|
||||
| Ant Design | shadcn-vue |
|
||||
|------------|------------|
|
||||
| a-button | Button |
|
||||
| a-input | Input |
|
||||
| a-form | Form |
|
||||
| a-table | Table |
|
||||
| a-modal | Dialog |
|
||||
| a-select | Select |
|
||||
| a-message | Sonner (toast) |
|
||||
| a-icon | Iconify lucide |
|
||||
|
||||
### Step 3: 迁移模式
|
||||
|
||||
参见 [MIGRATION_PATTERNS.md](references/MIGRATION_PATTERNS.md) 获取详细代码示例。
|
||||
|
||||
### Step 4: 样式迁移
|
||||
|
||||
参见 [STYLES.md](references/STYLES.md) 获取样式迁移指南。
|
||||
|
||||
## 执行迁移
|
||||
|
||||
### 1. 表单组件迁移
|
||||
|
||||
```vue
|
||||
<!-- Before: Ant Design -->
|
||||
<a-form :model="form" :rules="rules" ref="formRef">
|
||||
<a-form-item name="field" label="标签">
|
||||
<a-input v-model:value="form.field" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- After: shadcn-vue -->
|
||||
<Form v-model="form" :schema="schema" @submit="onSubmit">
|
||||
<FormField name="field" v-slot="{ componentField }">
|
||||
<FormItem>
|
||||
<FormLabel>标签</FormLabel>
|
||||
<FormControl>
|
||||
<Input v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</Form>
|
||||
```
|
||||
|
||||
### 2. 表格组件迁移
|
||||
|
||||
```vue
|
||||
<!-- Before: Ant Design -->
|
||||
<a-table :columns="columns" :data-source="data" />
|
||||
|
||||
<!-- After: shadcn-vue -->
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead v-for="col in columns" :key="col.key">{{ col.title }}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-for="row in data" :key="row.id">
|
||||
<TableCell v-for="col in columns" :key="col.key">{{ row[col.dataIndex] }}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
```
|
||||
|
||||
### 3. 消息提示迁移
|
||||
|
||||
```javascript
|
||||
// Before
|
||||
import { message } from 'ant-design-vue'
|
||||
message.success('操作成功')
|
||||
|
||||
// After
|
||||
import { toast } from 'vue-sonner'
|
||||
toast.success('操作成功')
|
||||
```
|
||||
|
||||
### 4. 图标迁移
|
||||
|
||||
```vue
|
||||
<!-- Before -->
|
||||
import { UserOutlined } from '@ant-design/icons-vue'
|
||||
<UserOutlined />
|
||||
|
||||
<!-- After -->
|
||||
import { Icon } from '@iconify/vue'
|
||||
<Icon icon="lucide:user" />
|
||||
```
|
||||
|
||||
## 间距规范
|
||||
|
||||
使用 Tailwind 间距类替代固定值:
|
||||
- `p-4` = 16px
|
||||
- `gap-3` = 12px
|
||||
- `space-y-4` = 子元素间 16px 间距
|
||||
|
||||
## 颜色使用
|
||||
|
||||
使用语义化 CSS 变量:
|
||||
```css
|
||||
color: var(--foreground) /* 主文字 */
|
||||
color: var(--muted-foreground) /* 次要文字 */
|
||||
background: var(--background) /* 背景 */
|
||||
background: var(--primary) /* 主色 */
|
||||
border-color: var(--border) /* 边框 */
|
||||
```
|
||||
|
||||
## 检查清单
|
||||
|
||||
迁移完成后验证:
|
||||
- [ ] 所有 a- 前缀组件已替换
|
||||
- [ ] ant-design-vue 导入已移除
|
||||
- [ ] 图标已迁移到 Iconify
|
||||
- [ ] message/notification 已迁移到 Sonner
|
||||
- [ ] 样式使用 Tailwind 类或 CSS 变量
|
||||
- [ ] 业务功能测试通过
|
||||
162
.claude/skills/antd-to-shadcn/references/COMPONENT_MAP.md
Normal file
162
.claude/skills/antd-to-shadcn/references/COMPONENT_MAP.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 组件映射表
|
||||
|
||||
完整的 Ant Design Vue → shadcn-vue 组件映射。
|
||||
|
||||
## 基础组件
|
||||
|
||||
| Ant Design | shadcn-vue | 说明 |
|
||||
|------------|------------|------|
|
||||
| `a-button` | `Button` | 按钮 |
|
||||
| `a-input` | `Input` | 输入框 |
|
||||
| `a-input-number` | `NumberField` | 数字输入 |
|
||||
| `a-input-password` | `Input type="password"` | 密码输入 |
|
||||
| `a-textarea` | `Textarea` | 多行文本 |
|
||||
| `a-select` | `Select` | 下拉选择 |
|
||||
| `a-radio` | `RadioGroup` + `RadioGroupItem` | 单选 |
|
||||
| `a-checkbox` | `Checkbox` | 复选框 |
|
||||
| `a-switch` | `Switch` | 开关 |
|
||||
| `a-slider` | `Slider` | 滑动条 |
|
||||
| `a-rate` | 自定义 | 评分(需自定义) |
|
||||
| `a-upload` | 自定义 | 上传(需自定义) |
|
||||
|
||||
## 表单组件
|
||||
|
||||
| Ant Design | shadcn-vue | 说明 |
|
||||
|------------|------------|------|
|
||||
| `a-form` | `Form` | 表单容器 |
|
||||
| `a-form-item` | `FormItem` | 表单项 |
|
||||
| `a-form-provider` | 自定义 | 表单上下文 |
|
||||
| `a-range-picker` | `RangeCalendar` + `Popover` | 日期范围 |
|
||||
| `a-date-picker` | `Calendar` + `Popover` | 日期选择 |
|
||||
| `a-time-picker` | 自定义 | 时间选择 |
|
||||
|
||||
## 数据展示
|
||||
|
||||
| Ant Design | shadcn-vue | 说明 |
|
||||
|------------|------------|------|
|
||||
| `a-table` | `Table` | 表格 |
|
||||
| `a-list` | 自定义 | 列表 |
|
||||
| `a-card` | `Card` | 卡片 |
|
||||
| `a-descriptions` | 自定义 | 描述列表 |
|
||||
| `a-statistic` | 自定义 | 统计数值 |
|
||||
| `a-tree` | 自定义 | 树形控件 |
|
||||
| `a-avatar` | `Avatar` | 头像 |
|
||||
| `a-badge` | `Badge` | 徽标 |
|
||||
| `a-tag` | `Badge` variant | 标签 |
|
||||
| `a-timeline` | 自定义 | 时间轴 |
|
||||
| `a-image` | `Img` 或自定义 | 图片 |
|
||||
| `a-empty` | `Empty` | 空状态 |
|
||||
|
||||
## 导航组件
|
||||
|
||||
| Ant Design | shadcn-vue | 说明 |
|
||||
|------------|------------|------|
|
||||
| `a-menu` | `NavigationMenu` / `SidebarMenu` | 菜单 |
|
||||
| `a-dropdown` | `DropdownMenu` | 下拉菜单 |
|
||||
| `a-pagination` | `Pagination` | 分页 |
|
||||
| `a-steps` | `Stepper` | 步骤条 |
|
||||
| `a-breadcrumb` | `Breadcrumb` | 面包屑 |
|
||||
| `a-tabs` | `Tabs` | 标签页 |
|
||||
|
||||
## 反馈组件
|
||||
|
||||
| Ant Design | shadcn-vue | 说明 |
|
||||
|------------|------------|------|
|
||||
| `a-modal` | `Dialog` | 对话框 |
|
||||
| `a-drawer` | `Drawer` | 抽屉 |
|
||||
| `a-message` | `toast` (vue-sonner) | 全局提示 |
|
||||
| `a-notification` | `toast` (vue-sonner) | 通知 |
|
||||
| `a-popconfirm` | `AlertDialog` | 确认对话框 |
|
||||
| `a-popover` | `Popover` | 气泡卡片 |
|
||||
| `a-tooltip` | `Tooltip` | 文字提示 |
|
||||
| `a-progress` | `Progress` | 进度条 |
|
||||
| `a-spin` | `Spinner` | 加载中 |
|
||||
| `a-skeleton` | `Skeleton` | 骨架屏 |
|
||||
| `a-alert` | `Alert` | 警告提示 |
|
||||
|
||||
## 布局组件
|
||||
|
||||
| Ant Design | shadcn-vue | 说明 |
|
||||
|------------|------------|------|
|
||||
| `a-layout` | 自定义 | 布局 |
|
||||
| `a-layout-sider` | `Sidebar` | 侧边栏 |
|
||||
| `a-layout-header` | 自定义 | 头部 |
|
||||
| `a-layout-content` | 自定义 | 内容 |
|
||||
| `a-layout-footer` | 自定义 | 底部 |
|
||||
| `a-row` / `a-col` | Tailwind `grid` / `flex` | 栅格 |
|
||||
| `a-space` | `div` + Tailwind `gap-*` | 间距 |
|
||||
| `a-divider` | `Separator` | 分割线 |
|
||||
|
||||
## 图标映射
|
||||
|
||||
| Ant Design Icons | Iconify (lucide) |
|
||||
|------------------|------------------|
|
||||
| `UserOutlined` | `lucide:user` |
|
||||
| `SettingOutlined` | `lucide:settings` |
|
||||
| `SearchOutlined` | `lucide:search` |
|
||||
| `PlusOutlined` | `lucide:plus` |
|
||||
| `DeleteOutlined` | `lucide:trash-2` |
|
||||
| `EditOutlined` | `lucide:pencil` |
|
||||
| `CloseOutlined` | `lucide:x` |
|
||||
| `CheckOutlined` | `lucide:check` |
|
||||
| `EyeOutlined` | `lucide:eye` |
|
||||
| `EyeInvisibleOutlined` | `lucide:eye-off` |
|
||||
| `LoadingOutlined` | `lucide:loader-2` |
|
||||
| `ExclamationCircleOutlined` | `lucide:alert-circle` |
|
||||
| `InfoCircleOutlined` | `lucide:info` |
|
||||
| `QuestionCircleOutlined` | `lucide:help-circle` |
|
||||
| `PhoneOutlined` | `lucide:phone` |
|
||||
| `MailOutlined` | `lucide:mail` |
|
||||
| `SafetyOutlined` | `lucide:shield` |
|
||||
| `UploadOutlined` | `lucide:upload` |
|
||||
| `DownloadOutlined` | `lucide:download` |
|
||||
| `CopyOutlined` | `lucide:copy` |
|
||||
| `ReloadOutlined` | `lucide:refresh-cw` |
|
||||
| `FilterOutlined` | `lucide:filter` |
|
||||
| `MoreOutlined` | `lucide:more-horizontal` |
|
||||
| `MenuOutlined` | `lucide:menu` |
|
||||
| `HomeOutlined` | `lucide:home` |
|
||||
| `LeftOutlined` | `lucide:chevron-left` |
|
||||
| `RightOutlined` | `lucide:chevron-right` |
|
||||
| `UpOutlined` | `lucide:chevron-up` |
|
||||
| `DownOutlined` | `lucide:chevron-down` |
|
||||
|
||||
## 图标使用方式
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { Icon } from '@iconify/vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 使用 Icon 组件 -->
|
||||
<Icon icon="lucide:user" class="w-4 h-4" />
|
||||
|
||||
<!-- 带颜色 -->
|
||||
<Icon icon="lucide:settings" class="w-5 h-5 text-primary" />
|
||||
|
||||
<!-- 在按钮中 -->
|
||||
<Button>
|
||||
<Icon icon="lucide:plus" class="w-4 h-4 mr-2" />
|
||||
添加
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 表单验证迁移
|
||||
|
||||
| Ant Design | shadcn-vue (VeeForm) |
|
||||
|------------|----------------------|
|
||||
| `rules` prop | `zod` schema + `toTypedSchema` |
|
||||
| `validateFields()` | `form.validate()` |
|
||||
| `validateTrigger` | schema 配置 |
|
||||
| `hasFeedback` | `FormMessage` 组件 |
|
||||
|
||||
## 事件映射
|
||||
|
||||
| Ant Design | shadcn-vue |
|
||||
|------------|------------|
|
||||
| `@change` | `@update:modelValue` |
|
||||
| `@pressEnter` | `@keydown.enter` |
|
||||
| `@search` | 自定义 + `@keydown.enter` |
|
||||
| `@select` | `@update:modelValue` (Select) |
|
||||
662
.claude/skills/antd-to-shadcn/references/MIGRATION_PATTERNS.md
Normal file
662
.claude/skills/antd-to-shadcn/references/MIGRATION_PATTERNS.md
Normal file
@@ -0,0 +1,662 @@
|
||||
# 迁移模式
|
||||
|
||||
详细的代码迁移示例,从 Ant Design Vue 到 shadcn-vue。
|
||||
|
||||
## 目录
|
||||
|
||||
1. [表单迁移](#表单迁移)
|
||||
2. [表格迁移](#表格迁移)
|
||||
3. [弹窗迁移](#弹窗迁移)
|
||||
4. [消息提示迁移](#消息提示迁移)
|
||||
5. [下拉选择迁移](#下拉选择迁移)
|
||||
6. [分页迁移](#分页迁移)
|
||||
7. [标签页迁移](#标签页迁移)
|
||||
8. [抽屉迁移](#抽屉迁移)
|
||||
|
||||
---
|
||||
|
||||
## 表单迁移
|
||||
|
||||
### 简单表单
|
||||
|
||||
```vue
|
||||
<!-- ========== Before: Ant Design ========== -->
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
const formRef = ref()
|
||||
const form = reactive({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
const rules = {
|
||||
username: [{ required: true, message: '请输入用户名' }],
|
||||
password: [{ required: true, message: '请输入密码' }]
|
||||
}
|
||||
async function handleSubmit() {
|
||||
await formRef.value.validateFields()
|
||||
// 提交逻辑
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
|
||||
<a-form-item name="username" label="用户名">
|
||||
<a-input v-model:value="form.username" />
|
||||
</a-form-item>
|
||||
<a-form-item name="password" label="密码">
|
||||
<a-input-password v-model:value="form.password" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" @click="handleSubmit">登录</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import * as z from 'zod'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage
|
||||
} from '@/components/ui/form'
|
||||
|
||||
const formSchema = toTypedSchema(z.object({
|
||||
username: z.string().min(1, '请输入用户名'),
|
||||
password: z.string().min(1, '请输入密码')
|
||||
}))
|
||||
|
||||
const { handleSubmit } = useForm({
|
||||
validationSchema: formSchema,
|
||||
initialValues: { username: '', password: '' }
|
||||
})
|
||||
|
||||
const onSubmit = handleSubmit((values) => {
|
||||
// 提交逻辑,values 已验证
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form @submit="onSubmit" class="space-y-4">
|
||||
<FormField v-slot="{ componentField }" name="username">
|
||||
<FormItem>
|
||||
<FormLabel>用户名</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="请输入用户名" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="password">
|
||||
<FormItem>
|
||||
<FormLabel>密码</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="请输入密码" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<Button type="submit">登录</Button>
|
||||
</Form>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 表单 + 验证码(带按钮状态)
|
||||
|
||||
```vue
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import * as z from 'zod'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
|
||||
|
||||
const countdown = ref(0)
|
||||
const sendingCode = ref(false)
|
||||
|
||||
const formSchema = toTypedSchema(z.object({
|
||||
mobile: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号'),
|
||||
code: z.string().length(4, '验证码为4位数字')
|
||||
}))
|
||||
|
||||
const { handleSubmit, validateField } = useForm({
|
||||
validationSchema: formSchema
|
||||
})
|
||||
|
||||
async function sendCode() {
|
||||
const { valid } = await validateField('mobile')
|
||||
if (!valid) return
|
||||
|
||||
sendingCode.value = true
|
||||
// 发送验证码 API
|
||||
countdown.value = 60
|
||||
const timer = setInterval(() => {
|
||||
countdown.value--
|
||||
if (countdown.value <= 0) clearInterval(timer)
|
||||
}, 1000)
|
||||
sendingCode.value = false
|
||||
}
|
||||
|
||||
const onSubmit = handleSubmit((values) => {
|
||||
// 登录逻辑
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form @submit="onSubmit" class="space-y-4">
|
||||
<FormField v-slot="{ componentField }" name="mobile">
|
||||
<FormItem>
|
||||
<FormLabel>手机号</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="请输入手机号" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<FormField v-slot="{ componentField }" name="code">
|
||||
<FormItem>
|
||||
<FormLabel>验证码</FormLabel>
|
||||
<div class="flex gap-2">
|
||||
<FormControl>
|
||||
<Input placeholder="请输入验证码" v-bind="componentField" class="flex-1" />
|
||||
</FormControl>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
:disabled="countdown > 0 || sendingCode"
|
||||
@click="sendCode"
|
||||
>
|
||||
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
|
||||
</Button>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<Button type="submit" class="w-full">登录</Button>
|
||||
</Form>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 表格迁移
|
||||
|
||||
```vue
|
||||
<!-- ========== Before: Ant Design ========== -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="data"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-button type="link" @click="edit(record)">编辑</a-button>
|
||||
<a-popconfirm title="确定删除?" @confirm="del(record)">
|
||||
<a-button type="link" danger>删除</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger
|
||||
} from '@/components/ui/alert-dialog'
|
||||
|
||||
const columns = [
|
||||
{ key: 'name', title: '名称' },
|
||||
{ key: 'status', title: '状态' },
|
||||
{ key: 'action', title: '操作' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead v-for="col in columns" :key="col.key">
|
||||
{{ col.title }}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<!-- 加载状态 -->
|
||||
<TableRow v-if="loading">
|
||||
<TableCell :colspan="columns.length" class="h-24 text-center">
|
||||
<Spinner class="mx-auto" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<TableRow v-else-if="data.length === 0">
|
||||
<TableCell :colspan="columns.length" class="h-24 text-center text-muted-foreground">
|
||||
暂无数据
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<!-- 数据行 -->
|
||||
<TableRow v-else v-for="record in data" :key="record.id">
|
||||
<TableCell>{{ record.name }}</TableCell>
|
||||
<TableCell>{{ record.status }}</TableCell>
|
||||
<TableCell>
|
||||
<div class="flex gap-2">
|
||||
<Button variant="link" size="sm" @click="edit(record)">编辑</Button>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger as-child>
|
||||
<Button variant="link" size="sm" class="text-destructive">删除</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogTitle>确定删除?</AlertDialogTitle>
|
||||
<AlertDialogDescription>此操作不可撤销</AlertDialogDescription>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction @click="del(record)">确定</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<!-- 分页(独立组件) -->
|
||||
<div class="mt-4 flex justify-end">
|
||||
<Pagination
|
||||
:current="pagination.current"
|
||||
:total="pagination.total"
|
||||
:page-size="pagination.pageSize"
|
||||
@change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 弹窗迁移
|
||||
|
||||
```vue
|
||||
<!-- ========== Before: Ant Design ========== -->
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
title="编辑"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form>...</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const visible = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="visible">
|
||||
<DialogContent class="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>编辑</DialogTitle>
|
||||
<DialogDescription>修改以下信息</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<div class="py-4">
|
||||
<Form>...</Form>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="visible = false">取消</Button>
|
||||
<Button @click="handleOk">确定</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 消息提示迁移
|
||||
|
||||
```javascript
|
||||
// ========== Before: Ant Design ==========
|
||||
import { message, notification } from 'ant-design-vue'
|
||||
|
||||
message.success('操作成功')
|
||||
message.error('操作失败')
|
||||
message.warning('警告信息')
|
||||
message.loading('加载中...')
|
||||
|
||||
notification.success({
|
||||
message: '成功',
|
||||
description: '操作已完成'
|
||||
})
|
||||
|
||||
// ========== After: shadcn-vue (vue-sonner) ==========
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
toast.success('操作成功')
|
||||
toast.error('操作失败')
|
||||
toast.warning('警告信息')
|
||||
toast.loading('加载中...')
|
||||
|
||||
// 带描述的通知
|
||||
toast.success('操作已完成', {
|
||||
description: '数据已保存'
|
||||
})
|
||||
|
||||
// 自定义时长
|
||||
toast.success('操作成功', { duration: 3000 })
|
||||
|
||||
// Promise 状态
|
||||
toast.promise(saveData(), {
|
||||
loading: '保存中...',
|
||||
success: '保存成功',
|
||||
error: '保存失败'
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下拉选择迁移
|
||||
|
||||
```vue
|
||||
<!-- ========== Before: Ant Design ========== -->
|
||||
<a-select
|
||||
v-model:value="selected"
|
||||
:options="options"
|
||||
placeholder="请选择"
|
||||
allow-clear
|
||||
show-search
|
||||
/>
|
||||
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select'
|
||||
|
||||
const selected = ref('')
|
||||
const options = [
|
||||
{ value: '1', label: '选项一' },
|
||||
{ value: '2', label: '选项二' }
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Select v-model="selected">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
v-for="opt in options"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分页迁移
|
||||
|
||||
```vue
|
||||
<!-- ========== Before: Ant Design ========== -->
|
||||
<a-pagination
|
||||
v-model:current="page"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
show-size-changer
|
||||
@change="handleChange"
|
||||
/>
|
||||
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious
|
||||
} from '@/components/ui/pagination'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select'
|
||||
|
||||
const page = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const total = ref(100)
|
||||
|
||||
const totalPages = computed(() => Math.ceil(total.value / pageSize.value))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center justify-between">
|
||||
<Pagination v-model:page="page" :total="totalPages" @update:page="handleChange">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious />
|
||||
</PaginationItem>
|
||||
<PaginationItem v-for="p in totalPages" :key="p">
|
||||
<PaginationLink :isActive="page === p" @click="page = p">
|
||||
{{ p }}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
<PaginationItem>
|
||||
<PaginationNext />
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
|
||||
<Select v-model="pageSize" @update:modelValue="handlePageSizeChange">
|
||||
<SelectTrigger class="w-[100px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="10">10条/页</SelectItem>
|
||||
<SelectItem value="20">20条/页</SelectItem>
|
||||
<SelectItem value="50">50条/页</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 标签页迁移
|
||||
|
||||
```vue
|
||||
<!-- ========== Before: Ant Design ========== -->
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="1" tab="标签一">内容一</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="标签二">内容二</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
|
||||
const activeKey = ref('1')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tabs v-model="activeKey">
|
||||
<TabsList>
|
||||
<TabsTrigger value="1">标签一</TabsTrigger>
|
||||
<TabsTrigger value="2">标签二</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="1">内容一</TabsContent>
|
||||
<TabsContent value="2">内容二</TabsContent>
|
||||
</Tabs>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 抽屉迁移
|
||||
|
||||
```vue
|
||||
<!-- ========== Before: Ant Design ========== -->
|
||||
<a-drawer
|
||||
v-model:open="visible"
|
||||
title="详情"
|
||||
placement="right"
|
||||
:width="400"
|
||||
>
|
||||
内容
|
||||
</a-drawer>
|
||||
|
||||
<!-- ========== After: shadcn-vue ========== -->
|
||||
<script setup>
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle
|
||||
} from '@/components/ui/drawer'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const visible = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Drawer v-model:open="visible">
|
||||
<DrawerContent class="right-0 left-auto top-0 bottom-0 h-full w-[400px] mt-0 rounded-none">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>详情</DrawerTitle>
|
||||
<DrawerDescription>查看详细信息</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<div class="p-4 flex-1 overflow-auto">
|
||||
内容
|
||||
</div>
|
||||
<DrawerFooter>
|
||||
<DrawerClose as-child>
|
||||
<Button variant="outline">关闭</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何处理 a-form 的 layout 属性?
|
||||
|
||||
```vue
|
||||
<!-- layout="vertical" -->
|
||||
<Form class="space-y-4">
|
||||
|
||||
<!-- layout="horizontal" -->
|
||||
<Form class="flex gap-4">
|
||||
<FormField class="flex items-center gap-2">
|
||||
<FormLabel class="w-24 shrink-0">
|
||||
<FormControl class="flex-1">
|
||||
</FormField>
|
||||
</Form>
|
||||
|
||||
<!-- layout="inline" -->
|
||||
<Form class="flex flex-wrap gap-4">
|
||||
```
|
||||
|
||||
### Q: 如何处理自定义验证?
|
||||
|
||||
```javascript
|
||||
// Ant Design
|
||||
const rules = {
|
||||
field: [{
|
||||
validator: async (rule, value) => {
|
||||
if (!value) throw new Error('必填')
|
||||
if (value.length < 3) throw new Error('太短')
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
// shadcn-vue (zod)
|
||||
const formSchema = toTypedSchema(z.object({
|
||||
field: z.string()
|
||||
.min(1, '必填')
|
||||
.refine(val => val.length >= 3, '太短')
|
||||
.refine(async (val) => {
|
||||
// 异步验证
|
||||
const exists = await checkExists(val)
|
||||
return !exists
|
||||
}, '已存在')
|
||||
}))
|
||||
```
|
||||
|
||||
### Q: 如何处理表单初始值?
|
||||
|
||||
```javascript
|
||||
// Ant Design
|
||||
const form = reactive({ name: '初始值' })
|
||||
|
||||
// shadcn-vue
|
||||
const { resetForm } = useForm({
|
||||
validationSchema: formSchema,
|
||||
initialValues: { name: '初始值' }
|
||||
})
|
||||
|
||||
// 重置到初始值
|
||||
resetForm()
|
||||
```
|
||||
484
.claude/skills/antd-to-shadcn/references/STYLES.md
Normal file
484
.claude/skills/antd-to-shadcn/references/STYLES.md
Normal file
@@ -0,0 +1,484 @@
|
||||
# 样式迁移指南
|
||||
|
||||
从 Ant Design 样式系统迁移到 TailwindCSS + CSS 变量。
|
||||
|
||||
## 目录
|
||||
|
||||
1. [现代设计原则](#现代设计原则)
|
||||
2. [颜色系统](#颜色系统)
|
||||
3. [间距系统](#间距系统)
|
||||
4. [字体系统](#字体系统)
|
||||
5. [阴影系统](#阴影系统)
|
||||
6. [圆角系统](#圆角系统)
|
||||
7. [响应式布局](#响应式布局)
|
||||
8. [状态样式](#状态样式)
|
||||
|
||||
---
|
||||
|
||||
## 现代设计原则
|
||||
|
||||
### 抵制过时设计模式
|
||||
|
||||
**❌ 拒绝老气 UI**:
|
||||
- 粗重边框、多重边框嵌套(边框重叠)
|
||||
- 灰暗沉闷的配色(缺乏活力)
|
||||
- 过度装饰的渐变和重阴影
|
||||
- 拥挤无呼吸感的布局
|
||||
- 间距混乱、缺乏层级感
|
||||
- 老式的表单样式(密集排列)
|
||||
|
||||
**✅ 拥抱现代设计**:
|
||||
- 轻量边框或无边框设计
|
||||
- 明亮有活力的色彩
|
||||
- 克制精致的阴影(shadow-sm 为主)
|
||||
- 充足留白,呼吸感
|
||||
- 清晰的视觉层级(4px 间距递进)
|
||||
- 现代表单(宽松布局,清晰标签)
|
||||
|
||||
### 颜色活力设计
|
||||
|
||||
**年轻感配色特征**:
|
||||
```css
|
||||
/* 主色:中等饱和度,避免过深或过浅 */
|
||||
--primary: oklch(0.55 0.18 254.604); /* 活力蓝,非深蓝 */
|
||||
|
||||
/* 强调色:高饱和度,用于吸引注意 */
|
||||
--accent-bright: oklch(0.65 0.22 254.604);
|
||||
|
||||
/* 渐变:柔和过渡,非跳跃式 */
|
||||
--gradient-primary: linear-gradient(135deg,
|
||||
oklch(0.68 0.16 254.604),
|
||||
oklch(0.45 0.16 254.604)
|
||||
);
|
||||
```
|
||||
|
||||
**发现优秀配色时**:
|
||||
1. 提取关键颜色值(oklch 格式)
|
||||
2. 添加到 `style.css` 设计令牌
|
||||
3. 使用语义化命名
|
||||
4. 在 `theme.css` 中同步 shadcn 变量
|
||||
|
||||
### 间距层级感
|
||||
|
||||
```
|
||||
紧凑:gap-2 (8px) - 同组元素
|
||||
舒适:gap-3 (12px) - 表单项之间
|
||||
宽松:gap-4 (16px) - 卡片内容
|
||||
分区:gap-6 (24px) - 不同区块
|
||||
隔离:gap-8 (32px) - 模块分隔
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 颜色系统
|
||||
|
||||
### 语义化颜色变量
|
||||
|
||||
项目使用 `oklch` 色彩空间,定义在 `theme.css` 中:
|
||||
|
||||
```css
|
||||
/* theme.css 中的变量 */
|
||||
--background /* 页面背景 */
|
||||
--foreground /* 主文字 */
|
||||
--card /* 卡片背景 */
|
||||
--card-foreground /* 卡片文字 */
|
||||
--popover /* 弹出层背景 */
|
||||
--popover-foreground
|
||||
--primary /* 主色(品牌蓝) */
|
||||
--primary-foreground
|
||||
--secondary /* 次要色 */
|
||||
--secondary-foreground
|
||||
--muted /* 静音背景 */
|
||||
--muted-foreground /* 静音文字 */
|
||||
--accent /* 强调背景 */
|
||||
--accent-foreground
|
||||
--destructive /* 危险色 */
|
||||
--destructive-foreground
|
||||
--border /* 边框 */
|
||||
--input /* 输入框边框 */
|
||||
--ring /* 焦点环 */
|
||||
```
|
||||
|
||||
### 使用方式
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 使用 Tailwind 类 -->
|
||||
<div class="bg-background text-foreground">
|
||||
<p class="text-muted-foreground">次要文字</p>
|
||||
<Button class="bg-primary text-primary-foreground">主按钮</Button>
|
||||
<span class="text-destructive">错误提示</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 在 CSS 中使用变量 */
|
||||
.custom-element {
|
||||
color: var(--foreground);
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Ant Design 颜色迁移
|
||||
|
||||
| Ant Design | shadcn/Tailwind |
|
||||
|------------|-----------------|
|
||||
| `@primary-color` | `var(--primary)` 或 `text-primary bg-primary` |
|
||||
| `@success-color` | `text-green-500` 或自定义 |
|
||||
| `@warning-color` | `text-yellow-500` 或自定义 |
|
||||
| `@error-color` | `var(--destructive)` 或 `text-destructive` |
|
||||
| `@text-color` | `var(--foreground)` 或 `text-foreground` |
|
||||
| `@text-color-secondary` | `var(--muted-foreground)` |
|
||||
| `@border-color-base` | `var(--border)` 或 `border-border` |
|
||||
| `@disabled-color` | `text-muted-foreground` |
|
||||
|
||||
---
|
||||
|
||||
## 间距系统
|
||||
|
||||
Tailwind 使用 4px 基准:
|
||||
|
||||
| Tailwind | 像素值 |
|
||||
|----------|--------|
|
||||
| `p-1` / `m-1` | 4px |
|
||||
| `p-2` / `m-2` | 8px |
|
||||
| `p-3` / `m-3` | 12px |
|
||||
| `p-4` / `m-4` | 16px |
|
||||
| `p-5` / `m-5` | 20px |
|
||||
| `p-6` / `m-6` | 24px |
|
||||
| `p-8` / `m-8` | 32px |
|
||||
| `p-10` / `m-10` | 40px |
|
||||
| `p-12` / `m-12` | 48px |
|
||||
|
||||
### 常见场景
|
||||
|
||||
```vue
|
||||
<!-- 表单间距 -->
|
||||
<Form class="space-y-4">
|
||||
<FormField class="space-y-2">...</FormField>
|
||||
</Form>
|
||||
|
||||
<!-- 按钮组 -->
|
||||
<div class="flex gap-2">
|
||||
<Button>取消</Button>
|
||||
<Button>确定</Button>
|
||||
</div>
|
||||
|
||||
<!-- 卡片内边距 -->
|
||||
<Card class="p-6">
|
||||
<CardHeader class="pb-4">...</CardHeader>
|
||||
<CardContent>...</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- 页面边距 -->
|
||||
<div class="p-6 lg:p-8">
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
### Ant Design 间距迁移
|
||||
|
||||
| Ant Design | Tailwind |
|
||||
|------------|----------|
|
||||
| `margin: 16px` | `m-4` |
|
||||
| `padding: 24px` | `p-6` |
|
||||
| `gap: 8px` | `gap-2` |
|
||||
| `space-between` | `justify-between` |
|
||||
| `align-center` | `items-center` |
|
||||
|
||||
---
|
||||
|
||||
## 字体系统
|
||||
|
||||
### 字体大小
|
||||
|
||||
| Tailwind | 像素值 | 用途 |
|
||||
|----------|--------|------|
|
||||
| `text-xs` | 12px | 辅助文字 |
|
||||
| `text-sm` | 14px | 正文、标签 |
|
||||
| `text-base` | 16px | 正文 |
|
||||
| `text-lg` | 18px | 小标题 |
|
||||
| `text-xl` | 20px | 标题 |
|
||||
| `text-2xl` | 24px | 大标题 |
|
||||
| `text-3xl` | 30px | 页面标题 |
|
||||
|
||||
### 字重
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<span class="font-normal">常规</span>
|
||||
<span class="font-medium">中等</span>
|
||||
<span class="font-semibold">半粗</span>
|
||||
<span class="font-bold">粗体</span>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 行高
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<p class="leading-tight">紧凑行高</p>
|
||||
<p class="leading-normal">正常行高</p>
|
||||
<p class="leading-relaxed">宽松行高</p>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 阴影系统
|
||||
|
||||
定义在 `theme.css`:
|
||||
|
||||
```css
|
||||
--shadow-sm /* 微阴影 */
|
||||
--shadow /* 基础阴影 */
|
||||
--shadow-md /* 中等阴影 */
|
||||
--shadow-lg /* 大阴影 */
|
||||
--shadow-xl /* 超大阴影 */
|
||||
--shadow-2xl /* 巨大阴影 */
|
||||
```
|
||||
|
||||
### 使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Card class="shadow-sm">卡片</Card>
|
||||
<Dialog class="shadow-lg">弹窗</Dialog>
|
||||
<DropdownMenu class="shadow-md">下拉菜单</DropdownMenu>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Ant Design 阴影迁移
|
||||
|
||||
| Ant Design | Tailwind |
|
||||
|------------|----------|
|
||||
| `box-shadow: 0 2px 8px rgba(0,0,0,0.15)` | `shadow-md` |
|
||||
| Modal 阴影 | `shadow-xl` |
|
||||
| Popover 阴影 | `shadow-lg` |
|
||||
|
||||
---
|
||||
|
||||
## 圆角系统
|
||||
|
||||
定义在 `theme.css`:
|
||||
|
||||
```css
|
||||
--radius-sm /* 小圆角 */
|
||||
--radius-md /* 中圆角 */
|
||||
--radius-lg /* 大圆角 */
|
||||
--radius-xl /* 超大圆角 */
|
||||
```
|
||||
|
||||
### 使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 使用 Tailwind 类 -->
|
||||
<Button class="rounded-md">按钮</Button>
|
||||
<Card class="rounded-lg">卡片</Card>
|
||||
<Input class="rounded-md" />
|
||||
|
||||
<!-- 自定义圆角 -->
|
||||
<div class="rounded-[var(--radius-lg)]">自定义</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Ant Design 圆角迁移
|
||||
|
||||
| Ant Design | Tailwind |
|
||||
|------------|----------|
|
||||
| `border-radius: 2px` | `rounded-sm` |
|
||||
| `border-radius: 4px` | `rounded` |
|
||||
| `border-radius: 8px` | `rounded-lg` |
|
||||
| `border-radius: 12px` | `rounded-xl` |
|
||||
|
||||
---
|
||||
|
||||
## 响应式布局
|
||||
|
||||
### 断点
|
||||
|
||||
| 断点 | 最小宽度 |
|
||||
|------|----------|
|
||||
| `sm:` | 640px |
|
||||
| `md:` | 768px |
|
||||
| `lg:` | 1024px |
|
||||
| `xl:` | 1280px |
|
||||
| `2xl:` | 1536px |
|
||||
|
||||
### 响应式示例
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 响应式网格 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<Card v-for="item in items">...</Card>
|
||||
</div>
|
||||
|
||||
<!-- 响应式隐藏 -->
|
||||
<div class="hidden md:block">桌面端显示</div>
|
||||
<div class="md:hidden">移动端显示</div>
|
||||
|
||||
<!-- 响应式间距 -->
|
||||
<div class="p-4 md:p-6 lg:p-8">
|
||||
...
|
||||
</div>
|
||||
|
||||
<!-- 响应式文字 -->
|
||||
<h1 class="text-2xl md:text-3xl lg:text-4xl">标题</h1>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 栅格迁移
|
||||
|
||||
```vue
|
||||
<!-- Ant Design 栅格 -->
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">...</a-col>
|
||||
<a-col :span="12">...</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- Tailwind 网格 -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>...</div>
|
||||
<div>...</div>
|
||||
</div>
|
||||
|
||||
<!-- 或使用 Flex -->
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1">...</div>
|
||||
<div class="flex-1">...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 状态样式
|
||||
|
||||
### 悬停
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Button class="hover:bg-primary/90">按钮</Button>
|
||||
<Card class="hover:shadow-md transition-shadow">卡片</Card>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 焦点
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Input class="focus:ring-2 focus:ring-primary" />
|
||||
<Button class="focus-visible:ring-2 focus-visible:ring-primary">
|
||||
按钮
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 禁用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Button class="disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
按钮
|
||||
</Button>
|
||||
<Input class="disabled:bg-muted disabled:cursor-not-allowed" />
|
||||
</template>
|
||||
```
|
||||
|
||||
### 加载中
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Button class="relative" disabled>
|
||||
<Spinner v-if="loading" class="absolute" />
|
||||
<span :class="{ 'opacity-0': loading }">提交</span>
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 过渡动画
|
||||
|
||||
### 基础过渡
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 颜色过渡 -->
|
||||
<Button class="transition-colors hover:bg-primary">按钮</Button>
|
||||
|
||||
<!-- 阴影过渡 -->
|
||||
<Card class="transition-shadow hover:shadow-lg">卡片</Card>
|
||||
|
||||
<!-- 全属性过渡 -->
|
||||
<div class="transition-all hover:scale-105">元素</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 动画时长
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="transition-colors duration-150">快速</div>
|
||||
<div class="transition-colors duration-200">正常</div>
|
||||
<div class="transition-colors duration-300">慢速</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见样式模式
|
||||
|
||||
### 卡片
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="rounded-lg border bg-card p-6 shadow-sm">
|
||||
<h3 class="text-lg font-semibold">标题</h3>
|
||||
<p class="text-muted-foreground mt-2">描述文字</p>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 表单项
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="space-y-2">
|
||||
<Label class="text-sm font-medium">标签</Label>
|
||||
<Input class="h-10" />
|
||||
<p class="text-xs text-muted-foreground">提示文字</p>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 操作栏
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="flex items-center justify-between py-4">
|
||||
<div class="text-sm text-muted-foreground">共 {{ total }} 条</div>
|
||||
<div class="flex gap-2">
|
||||
<Button variant="outline" size="sm">导出</Button>
|
||||
<Button size="sm">添加</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 空状态
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="flex flex-col items-center justify-center py-12 text-center">
|
||||
<Icon icon="lucide:inbox" class="h-12 w-12 text-muted-foreground/50" />
|
||||
<h3 class="mt-4 text-lg font-medium">暂无数据</h3>
|
||||
<p class="mt-2 text-sm text-muted-foreground">点击添加按钮创建</p>
|
||||
<Button class="mt-4">添加</Button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
@@ -1,548 +0,0 @@
|
||||
# Yudao Cloud B端设计规范提示词
|
||||
|
||||
> **定位**: B端生产力工具 | 简约高效 | 专业可信 | 创新体验
|
||||
> **技术栈**: Vue 3 + TypeScript + Ant Design Vue + TailwindCSS
|
||||
> **业务**: AI/媒体功能增强的Spring Boot快速开发平台
|
||||
|
||||
---
|
||||
|
||||
## 设计哲学
|
||||
|
||||
```
|
||||
简约而不简单,高效且具辨识
|
||||
- 每个像素都有目的
|
||||
- 每个交互都有反馈
|
||||
- 每个元素都有秩序
|
||||
```
|
||||
|
||||
**核心原则**:
|
||||
1. **认知减负** - 减少用户思考成本
|
||||
2. **操作高效** - 最短路径完成任务
|
||||
3. **视觉一致** - 建立可预测的心智模型
|
||||
4. **创新克制** - 在熟悉中创造惊喜
|
||||
|
||||
---
|
||||
|
||||
## 一、色彩系统 (Color System)
|
||||
|
||||
### 1.1 品牌主色 - 科技蓝
|
||||
|
||||
```css
|
||||
/* 主色阶 - 用于品牌识别、主要操作 */
|
||||
--color-primary-50: #EFF6FF; /* 悬浮背景 */
|
||||
--color-primary-100: #DBEAFE; /* 选中背景 */
|
||||
--color-primary-200: #BFDBFE; /* 边框高亮 */
|
||||
--color-primary-300: #93C5FD; /* 禁用状态 */
|
||||
--color-primary-400: #60A5FA; /* 悬浮状态 */
|
||||
--color-primary-500: #3B82F6; /* 标准主色 */
|
||||
--color-primary-600: #2563EB; /* 按下状态 */
|
||||
--color-primary-700: #1D4ED8; /* 强调状态 */
|
||||
```
|
||||
|
||||
**使用规则**:
|
||||
- 主按钮、关键操作使用 `primary-500`
|
||||
- 悬浮状态提升至 `primary-400`
|
||||
- 按下状态加深至 `primary-600`
|
||||
- 大面积背景仅用 `primary-50/100`
|
||||
|
||||
### 1.2 中性色 - 专业灰
|
||||
|
||||
```css
|
||||
/* 灰度系统 - 用于文字、边框、背景 */
|
||||
--color-gray-50: #F9FAFB; /* 页面背景 */
|
||||
--color-gray-100: #F3F4F6; /* 卡片背景 */
|
||||
--color-gray-200: #E5E7EB; /* 分割线 */
|
||||
--color-gray-300: #D1D5DB; /* 边框 */
|
||||
--color-gray-400: #9CA3AF; /* 占位符 */
|
||||
--color-gray-500: #6B7280; /* 辅助文字 */
|
||||
--color-gray-600: #4B5563; /* 正文 */
|
||||
--color-gray-700: #374151; /* 标题 */
|
||||
--color-gray-800: #1F2937; /* 强调标题 */
|
||||
--color-gray-900: #111827; /* 最高对比 */
|
||||
```
|
||||
|
||||
### 1.3 功能色
|
||||
|
||||
```css
|
||||
/* 成功 - 绿色系 */
|
||||
--color-success-light: #DCFCE7;
|
||||
--color-success: #22C55E;
|
||||
--color-success-dark: #16A34A;
|
||||
|
||||
/* 警告 - 琥珀系 */
|
||||
--color-warning-light: #FEF3C7;
|
||||
--color-warning: #F59E0B;
|
||||
--color-warning-dark: #D97706;
|
||||
|
||||
/* 错误 - 红色系 */
|
||||
--color-error-light: #FEE2E2;
|
||||
--color-error: #EF4444;
|
||||
--color-error-dark: #DC2626;
|
||||
|
||||
/* 信息 - 蓝色系 */
|
||||
--color-info-light: #DBEAFE;
|
||||
--color-info: #3B82F6;
|
||||
--color-info-dark: #2563EB;
|
||||
```
|
||||
|
||||
### 1.4 语义映射
|
||||
|
||||
| 场景 | 颜色 | 示例 |
|
||||
|------|------|------|
|
||||
| 主要操作 | primary-500 | 提交、保存、确认 |
|
||||
| 次要操作 | gray-600 + border | 取消、返回 |
|
||||
| 危险操作 | error-500 | 删除、清空 |
|
||||
| 成功反馈 | success-500 | 完成提示 |
|
||||
| 数据展示 | gray-700 | 表格正文 |
|
||||
| 辅助说明 | gray-500 | 表头、注释 |
|
||||
|
||||
---
|
||||
|
||||
## 二、字体排版 (Typography)
|
||||
|
||||
### 2.1 字体家族
|
||||
|
||||
```css
|
||||
/* 主字体 - 中文优先思源黑体,西文 Inter */
|
||||
--font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
|
||||
"Microsoft YaHei", "Noto Sans SC", sans-serif;
|
||||
|
||||
/* 等宽字体 - 代码、数字 */
|
||||
--font-family-mono: "JetBrains Mono", "Fira Code", Consolas, monospace;
|
||||
```
|
||||
|
||||
### 2.2 字号阶梯
|
||||
|
||||
```css
|
||||
/* 字号系统 - 基于 14px 基准 */
|
||||
--font-size-xs: 12px; /* 辅助标签、时间戳 */
|
||||
--font-size-sm: 13px; /* 表头、次要文字 */
|
||||
--font-size-base: 14px; /* 正文、输入框 */
|
||||
--font-size-md: 16px; /* 小标题、强调 */
|
||||
--font-size-lg: 18px; /* 卡片标题 */
|
||||
--font-size-xl: 20px; /* 区块标题 */
|
||||
--font-size-2xl: 24px; /* 页面标题 */
|
||||
--font-size-3xl: 30px; /* 大标题 */
|
||||
```
|
||||
|
||||
### 2.3 行高规范
|
||||
|
||||
```css
|
||||
--line-height-tight: 1.25; /* 标题 */
|
||||
--line-height-base: 1.5; /* 正文 */
|
||||
--line-height-relaxed: 1.75; /* 长文本 */
|
||||
```
|
||||
|
||||
### 2.4 字重规范
|
||||
|
||||
```css
|
||||
--font-weight-normal: 400; /* 正文 */
|
||||
--font-weight-medium: 500; /* 强调 */
|
||||
--font-weight-semibold: 600; /* 小标题 */
|
||||
--font-weight-bold: 700; /* 大标题 */
|
||||
```
|
||||
|
||||
### 2.5 使用规则
|
||||
|
||||
| 元素 | 字号 | 字重 | 行高 |
|
||||
|------|------|------|------|
|
||||
| 页面标题 | 24px | 600 | 1.25 |
|
||||
| 卡片标题 | 16px | 500 | 1.5 |
|
||||
| 正文内容 | 14px | 400 | 1.5 |
|
||||
| 辅助说明 | 12px | 400 | 1.5 |
|
||||
| 表格数据 | 14px | 400 | 1.5 |
|
||||
| 按钮文字 | 14px | 500 | 1 |
|
||||
|
||||
---
|
||||
|
||||
## 三、间距系统 (Spacing)
|
||||
|
||||
### 3.1 基础单位
|
||||
|
||||
```css
|
||||
/* 基于 4px 网格系统 */
|
||||
--space-0: 0;
|
||||
--space-1: 4px; /* 紧凑间距 */
|
||||
--space-2: 8px; /* 元素内间距 */
|
||||
--space-3: 12px; /* 小组件间距 */
|
||||
--space-4: 16px; /* 标准间距 */
|
||||
--space-5: 20px; /* 卡片内边距 */
|
||||
--space-6: 24px; /* 区块间距 */
|
||||
--space-8: 32px; /* 大区块间距 */
|
||||
--space-10: 40px; /* 章节间距 */
|
||||
--space-12: 48px; /* 页面边距 */
|
||||
--space-16: 64px; /* 主要分隔 */
|
||||
```
|
||||
|
||||
### 3.2 语义映射
|
||||
|
||||
| 场景 | 间距值 | 示例 |
|
||||
|------|--------|------|
|
||||
| 图标与文字 | 4-8px | `gap: 8px` |
|
||||
| 按钮内边距 | 8-16px | `padding: 8px 16px` |
|
||||
| 表单字段间距 | 16-24px | `margin-bottom: 16px` |
|
||||
| 卡片内边距 | 16-24px | `padding: 20px` |
|
||||
| 卡片之间 | 16-24px | `gap: 16px` |
|
||||
| 区块之间 | 24-32px | `margin-top: 24px` |
|
||||
| 页面边距 | 24px | `padding: 24px` |
|
||||
|
||||
---
|
||||
|
||||
## 四、圆角规范 (Border Radius)
|
||||
|
||||
### 4.1 圆角系统
|
||||
|
||||
```css
|
||||
--radius-none: 0;
|
||||
--radius-sm: 4px; /* 小按钮、标签 */
|
||||
--radius-base: 6px; /* 输入框、小卡片 */
|
||||
--radius-md: 8px; /* 按钮、下拉框 */
|
||||
--radius-lg: 12px; /* 卡片、弹窗 */
|
||||
--radius-xl: 16px; /* 大卡片 */
|
||||
--radius-2xl: 24px; /* 特殊容器 */
|
||||
--radius-full: 9999px; /* 圆形、药丸 */
|
||||
```
|
||||
|
||||
### 4.2 使用规则
|
||||
|
||||
| 元素 | 圆角 | 说明 |
|
||||
|------|------|------|
|
||||
| 按钮 | 6px | 标准圆角 |
|
||||
| 输入框 | 6px | 与按钮一致 |
|
||||
| 小标签 | 4px | 轻量感 |
|
||||
| 卡片 | 12px | 柔和边界 |
|
||||
| 弹窗 | 12px | 与卡片一致 |
|
||||
| 头像 | 9999px | 圆形 |
|
||||
| 抽屉 | 0 | 左侧直角 |
|
||||
|
||||
---
|
||||
|
||||
## 五、阴影系统 (Shadows)
|
||||
|
||||
### 5.1 阴影层级
|
||||
|
||||
```css
|
||||
/* 层级递进 - 从平面到立体 */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
--shadow-base: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
||||
```
|
||||
|
||||
### 5.2 使用场景
|
||||
|
||||
| 层级 | 阴影 | 示例 |
|
||||
|------|------|------|
|
||||
| 层级0 | none | 页面背景 |
|
||||
| 层级1 | sm | 输入框聚焦 |
|
||||
| 层级2 | base | 卡片默认 |
|
||||
| 层级3 | md | 卡片悬浮 |
|
||||
| 层级4 | lg | 下拉菜单 |
|
||||
| 层级5 | xl | 弹窗、抽屉 |
|
||||
|
||||
---
|
||||
|
||||
## 六、图标系统 (Icons)
|
||||
|
||||
### 6.1 图标规范
|
||||
|
||||
```css
|
||||
/* 图标尺寸 */
|
||||
--icon-xs: 12px; /* 内联图标 */
|
||||
--icon-sm: 16px; /* 列表图标 */
|
||||
--icon-md: 20px; /* 标准图标 */
|
||||
--icon-lg: 24px; /* 导航图标 */
|
||||
--icon-xl: 32px; /* 功能图标 */
|
||||
|
||||
/* 图标描边 */
|
||||
--icon-stroke: 1.5px;
|
||||
```
|
||||
|
||||
### 6.2 使用规则
|
||||
|
||||
- **图标库**: 统一使用 Lucide Icons 或 Ant Design Icons
|
||||
- **风格**: 线性图标,圆角端点
|
||||
- **颜色**: 继承文字颜色或使用 gray-500
|
||||
- **对齐**: 与文字垂直居中,间距 4-8px
|
||||
|
||||
---
|
||||
|
||||
## 七、组件规范 (Components)
|
||||
|
||||
### 7.1 按钮
|
||||
|
||||
```css
|
||||
/* 尺寸规范 */
|
||||
--btn-height-sm: 28px;
|
||||
--btn-height-base: 32px;
|
||||
--btn-height-lg: 40px;
|
||||
|
||||
/* 内边距 */
|
||||
--btn-padding-sm: 0 12px;
|
||||
--btn-padding-base: 0 16px;
|
||||
--btn-padding-lg: 0 24px;
|
||||
```
|
||||
|
||||
**按钮类型**:
|
||||
| 类型 | 样式 | 使用场景 |
|
||||
|------|------|----------|
|
||||
| 主要 | primary-500 填充 | 提交、确认、下一步 |
|
||||
| 次要 | gray-600 边框 | 取消、返回、重置 |
|
||||
| 文字 | 无边框透明 | 次要操作、链接跳转 |
|
||||
| 危险 | error-500 填充 | 删除、清空、禁用 |
|
||||
|
||||
### 7.2 输入框
|
||||
|
||||
```css
|
||||
--input-height: 32px;
|
||||
--input-padding: 0 12px;
|
||||
--input-border: 1px solid var(--color-gray-300);
|
||||
--input-radius: 6px;
|
||||
--input-focus-ring: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
```
|
||||
|
||||
### 7.3 卡片
|
||||
|
||||
```css
|
||||
--card-bg: var(--color-gray-50);
|
||||
--card-border: 1px solid var(--color-gray-200);
|
||||
--card-radius: 12px;
|
||||
--card-padding: 20px;
|
||||
--card-shadow: var(--shadow-base);
|
||||
--card-hover-shadow: var(--shadow-md);
|
||||
```
|
||||
|
||||
### 7.4 表格
|
||||
|
||||
```css
|
||||
--table-header-bg: var(--color-gray-50);
|
||||
--table-row-hover: var(--color-gray-50);
|
||||
--table-border: 1px solid var(--color-gray-200);
|
||||
--table-cell-padding: 12px 16px;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、布局系统 (Layout)
|
||||
|
||||
### 8.1 栅格系统
|
||||
|
||||
```css
|
||||
/* 12列栅格 */
|
||||
--grid-columns: 12;
|
||||
--grid-gutter: 24px;
|
||||
```
|
||||
|
||||
### 8.2 页面布局
|
||||
|
||||
```css
|
||||
/* 侧边栏 */
|
||||
--sidebar-width: 240px;
|
||||
--sidebar-collapsed: 64px;
|
||||
|
||||
/* 顶部导航 */
|
||||
--header-height: 56px;
|
||||
|
||||
/* 内容区域 */
|
||||
--content-max-width: 1200px;
|
||||
--content-padding: 24px;
|
||||
```
|
||||
|
||||
### 8.3 Z-Index 层级
|
||||
|
||||
```css
|
||||
--z-dropdown: 1000;
|
||||
--z-sticky: 1020;
|
||||
--z-fixed: 1030;
|
||||
--z-modal-backdrop: 1040;
|
||||
--z-modal: 1050;
|
||||
--z-popover: 1060;
|
||||
--z-tooltip: 1070;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、交互反馈 (Interaction)
|
||||
|
||||
### 9.1 状态颜色
|
||||
|
||||
| 状态 | 背景 | 边框 | 文字 |
|
||||
|------|------|------|------|
|
||||
| 默认 | white | gray-300 | gray-700 |
|
||||
| 悬浮 | gray-50 | gray-300 | gray-700 |
|
||||
| 聚焦 | white | primary-500 | gray-700 |
|
||||
| 激活 | primary-50 | primary-500 | primary-600 |
|
||||
| 禁用 | gray-100 | gray-200 | gray-400 |
|
||||
| 错误 | error-50 | error-500 | error-600 |
|
||||
| 成功 | success-50 | success-500 | success-600 |
|
||||
|
||||
### 9.2 反馈方式
|
||||
|
||||
| 场景 | 反馈 |
|
||||
|------|------|
|
||||
| 按钮点击 | 缩放 0.98 + 背景加深 |
|
||||
| 输入聚焦 | 边框变色 + 外发光 |
|
||||
| 加载中 | 旋转图标 + 禁用交互 |
|
||||
| 操作成功 | Toast 提示 + 图标动画 |
|
||||
| 操作失败 | Toast 错误 + 抖动动画 |
|
||||
|
||||
---
|
||||
|
||||
## 十、动效规范 (Motion)
|
||||
|
||||
### 10.1 时长
|
||||
|
||||
```css
|
||||
--duration-instant: 100ms; /* 微交互 */
|
||||
--duration-fast: 150ms; /* 悬浮 */
|
||||
--duration-base: 200ms; /* 状态切换 */
|
||||
--duration-slow: 300ms; /* 展开/收起 */
|
||||
--duration-slower: 500ms; /* 页面过渡 */
|
||||
```
|
||||
|
||||
### 10.2 缓动函数
|
||||
|
||||
```css
|
||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
```
|
||||
|
||||
### 10.3 动效原则
|
||||
|
||||
1. **目的性** - 每个动效都要服务于用户理解
|
||||
2. **克制** - B端产品动效要低调、高效
|
||||
3. **一致性** - 相同交互使用相同动效
|
||||
4. **可关闭** - 尊重 prefers-reduced-motion
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、响应式设计 (Responsive)
|
||||
|
||||
### 11.1 断点系统
|
||||
|
||||
```css
|
||||
/* 移动优先 */
|
||||
--breakpoint-sm: 640px; /* 小屏手机 */
|
||||
--breakpoint-md: 768px; /* 平板竖屏 */
|
||||
--breakpoint-lg: 1024px; /* 平板横屏/小桌面 */
|
||||
--breakpoint-xl: 1280px; /* 标准桌面 */
|
||||
--breakpoint-2xl: 1536px; /* 大屏桌面 */
|
||||
```
|
||||
|
||||
### 11.2 响应策略
|
||||
|
||||
| 断点 | 布局 | 导航 | 侧边栏 |
|
||||
|------|------|------|--------|
|
||||
| < 768px | 单列 | 底部 | 抽屉 |
|
||||
| 768-1024 | 双列 | 顶部 | 可折叠 |
|
||||
| > 1024 | 多列 | 顶部 | 固定展开 |
|
||||
|
||||
---
|
||||
|
||||
## 十二、深色模式 (Dark Mode)
|
||||
|
||||
### 12.1 深色色板
|
||||
|
||||
```css
|
||||
[data-theme="dark"] {
|
||||
/* 背景层级 */
|
||||
--color-bg-base: #0F172A; /* 页面背景 */
|
||||
--color-bg-elevated: #1E293B; /* 卡片背景 */
|
||||
--color-bg-overlay: #334155; /* 弹窗背景 */
|
||||
|
||||
/* 文字 */
|
||||
--color-text-primary: #F1F5F9;
|
||||
--color-text-secondary: #94A3B8;
|
||||
--color-text-muted: #64748B;
|
||||
|
||||
/* 边框 */
|
||||
--color-border: #334155;
|
||||
--color-border-light: #1E293B;
|
||||
|
||||
/* 主色调整 - 深色下提亮 */
|
||||
--color-primary-500: #60A5FA;
|
||||
}
|
||||
```
|
||||
|
||||
### 12.2 切换策略
|
||||
|
||||
- 跟随系统: `prefers-color-scheme`
|
||||
- 手动切换: localStorage 持久化
|
||||
- 无闪烁: CSS 变量 + preload 设置
|
||||
|
||||
---
|
||||
|
||||
## 实施指南
|
||||
|
||||
### 优先级排序
|
||||
|
||||
1. **P0 - 立即统一**
|
||||
- 品牌主色 (三套主色 → 统一蓝色系)
|
||||
- 字体大小 (移除硬编码)
|
||||
- 间距系统 (使用 CSS 变量)
|
||||
|
||||
2. **P1 - 短期完善**
|
||||
- TailwindCSS 配置整合
|
||||
- Ant Design 主题配置
|
||||
- 组件样式清理
|
||||
|
||||
3. **P2 - 中期优化**
|
||||
- 动效系统
|
||||
- 深色模式
|
||||
- 响应式适配
|
||||
|
||||
### 代码迁移示例
|
||||
|
||||
**Before (问题代码)**:
|
||||
```css
|
||||
/* HistoryPanel.vue */
|
||||
@primary: #6366f1;
|
||||
.button {
|
||||
background: @primary;
|
||||
padding: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
```
|
||||
|
||||
**After (规范代码)**:
|
||||
```css
|
||||
/* 使用全局变量 */
|
||||
.button {
|
||||
background: var(--color-primary-500);
|
||||
padding: var(--space-4);
|
||||
font-size: var(--font-size-base);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 设计检查清单
|
||||
|
||||
- [ ] 颜色使用 CSS 变量,无硬编码
|
||||
- [ ] 字体大小在定义的阶梯内
|
||||
- [ ] 间距使用 4px 倍数
|
||||
- [ ] 圆角使用预定义值
|
||||
- [ ] 阴影层级正确
|
||||
- [ ] 图标风格统一
|
||||
- [ ] 交互状态完整
|
||||
- [ ] 动效时长合理
|
||||
- [ ] 响应式断点正确
|
||||
- [ ] 深色模式适配
|
||||
|
||||
---
|
||||
|
||||
> **版本**: v1.0
|
||||
> **更新日期**: 2026-02-25
|
||||
> **适用项目**: Yudao Cloud Frontend
|
||||
@@ -109,24 +109,26 @@ function isActive(item) {
|
||||
</Sidebar>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="less">
|
||||
:deep(.is-active) {
|
||||
background: linear-gradient(135deg, oklch(0.45 0.16 254.604 / 0.1) 0%, oklch(0.45 0.16 254.604 / 0.05) 100%);
|
||||
color: oklch(0.45 0.16 254.604);
|
||||
background-color: oklch(from var(--sidebar-primary) l c h / 0.1);
|
||||
color: var(--sidebar-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.is-active:hover) {
|
||||
background: linear-gradient(135deg, oklch(0.45 0.16 254.604 / 0.15) 0%, oklch(0.45 0.16 254.604 / 0.08) 100%);
|
||||
&:hover {
|
||||
background-color: oklch(from var(--sidebar-primary) l c h / 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.sidebar-group-label) {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.025em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
:deep(.nav-item) {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,142 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
/* shadcn-vue 主题映射 */
|
||||
@theme inline {
|
||||
:root {
|
||||
/* ========================================
|
||||
基础颜色 - 与 style.css 保持一致
|
||||
======================================== */
|
||||
--background: oklch(0.985 0.001 264.695);
|
||||
--foreground: oklch(0.195 0.038 264.695);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.195 0.038 264.695);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.195 0.038 264.695);
|
||||
|
||||
/* ========================================
|
||||
主色 - 科技蓝品牌色
|
||||
======================================== */
|
||||
--primary: oklch(0.45 0.16 254.604);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
|
||||
/* ========================================
|
||||
次要色 - 偏蓝冷灰
|
||||
======================================== */
|
||||
--secondary: oklch(0.97 0.001 264.695);
|
||||
--secondary-foreground: oklch(0.205 0.015 264.695);
|
||||
|
||||
/* ========================================
|
||||
静音色 - 柔和灰
|
||||
======================================== */
|
||||
--muted: oklch(0.97 0.001 264.695);
|
||||
--muted-foreground: oklch(0.45 0.02 264.695);
|
||||
|
||||
/* ========================================
|
||||
强调色
|
||||
======================================== */
|
||||
--accent: oklch(0.97 0.001 264.695);
|
||||
--accent-foreground: oklch(0.205 0.015 264.695);
|
||||
|
||||
/* ========================================
|
||||
功能色
|
||||
======================================== */
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--destructive-foreground: oklch(0.985 0 0);
|
||||
|
||||
/* ========================================
|
||||
边框与输入
|
||||
======================================== */
|
||||
--border: oklch(0.922 0.004 264.695);
|
||||
--input: oklch(0.922 0.004 264.695);
|
||||
--ring: oklch(0.55 0.18 254.604);
|
||||
|
||||
/* ========================================
|
||||
图表色 - 蓝色系渐变
|
||||
======================================== */
|
||||
--chart-1: oklch(0.68 0.16 254.604);
|
||||
--chart-2: oklch(0.55 0.18 254.604);
|
||||
--chart-3: oklch(0.45 0.16 254.604);
|
||||
--chart-4: oklch(0.37 0.13 254.604);
|
||||
--chart-5: oklch(0.30 0.10 254.604);
|
||||
|
||||
/* ========================================
|
||||
侧边栏主题 - 品牌蓝高亮
|
||||
======================================== */
|
||||
--sidebar: oklch(0.985 0.001 264.695);
|
||||
--sidebar-foreground: oklch(0.195 0.038 264.695);
|
||||
--sidebar-primary: oklch(0.45 0.16 254.604);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0.001 264.695);
|
||||
--sidebar-accent-foreground: oklch(0.205 0.015 264.695);
|
||||
--sidebar-border: oklch(0.922 0.004 264.695);
|
||||
--sidebar-ring: oklch(0.55 0.18 254.604);
|
||||
|
||||
/* ========================================
|
||||
字体系统
|
||||
======================================== */
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
||||
--font-mono: ui-monospace, monospace;
|
||||
|
||||
/* ========================================
|
||||
圆角系统
|
||||
======================================== */
|
||||
--radius: 0.625rem;
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
|
||||
/* ========================================
|
||||
阴影系统
|
||||
======================================== */
|
||||
--shadow-2xs: 0 1px 2px 0 oklch(0 0 0 / 0.05);
|
||||
--shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.05);
|
||||
--shadow-sm: 0 1px 3px 0 oklch(0 0 0 / 0.10), 0 1px 2px -1px oklch(0 0 0 / 0.10);
|
||||
--shadow: 0 1px 3px 0 oklch(0 0 0 / 0.10), 0 1px 2px -1px oklch(0 0 0 / 0.10);
|
||||
--shadow-md: 0 4px 6px -1px oklch(0 0 0 / 0.10), 0 2px 4px -2px oklch(0 0 0 / 0.10);
|
||||
--shadow-lg: 0 10px 15px -3px oklch(0 0 0 / 0.10), 0 4px 6px -4px oklch(0 0 0 / 0.10);
|
||||
--shadow-xl: 0 20px 25px -5px oklch(0 0 0 / 0.10), 0 8px 10px -6px oklch(0 0 0 / 0.10);
|
||||
--shadow-2xl: 0 25px 50px -12px oklch(0 0 0 / 0.25);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0.004 264.695);
|
||||
--foreground: oklch(0.94 0.003 264.695);
|
||||
--card: oklch(0.18 0.006 264.695);
|
||||
--card-foreground: oklch(0.94 0.003 264.695);
|
||||
--popover: oklch(0.18 0.006 264.695);
|
||||
--popover-foreground: oklch(0.94 0.003 264.695);
|
||||
--primary: oklch(0.68 0.16 254.604);
|
||||
--primary-foreground: oklch(0.145 0.004 264.695);
|
||||
--secondary: oklch(0.25 0.01 264.695);
|
||||
--secondary-foreground: oklch(0.94 0.003 264.695);
|
||||
--muted: oklch(0.25 0.01 264.695);
|
||||
--muted-foreground: oklch(0.65 0.015 264.695);
|
||||
--accent: oklch(0.30 0.015 264.695);
|
||||
--accent-foreground: oklch(0.94 0.003 264.695);
|
||||
--destructive: oklch(0.65 0.2 25);
|
||||
--destructive-foreground: oklch(0.94 0.003 264.695);
|
||||
--border: oklch(0.28 0.01 264.695);
|
||||
--input: oklch(0.28 0.01 264.695);
|
||||
--ring: oklch(0.55 0.15 254.604);
|
||||
--chart-1: oklch(0.78 0.11 254.604);
|
||||
--chart-2: oklch(0.68 0.16 254.604);
|
||||
--chart-3: oklch(0.55 0.18 254.604);
|
||||
--chart-4: oklch(0.45 0.16 254.604);
|
||||
--chart-5: oklch(0.37 0.13 254.604);
|
||||
--sidebar: oklch(0.18 0.006 264.695);
|
||||
--sidebar-foreground: oklch(0.94 0.003 264.695);
|
||||
--sidebar-primary: oklch(0.68 0.16 254.604);
|
||||
--sidebar-primary-foreground: oklch(0.145 0.004 264.695);
|
||||
--sidebar-accent: oklch(0.25 0.01 264.695);
|
||||
--sidebar-accent-foreground: oklch(0.94 0.003 264.695);
|
||||
--sidebar-border: oklch(0.28 0.01 264.695);
|
||||
--sidebar-ring: oklch(0.55 0.15 254.604);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
@@ -28,11 +156,103 @@
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--font-sans: var(--font-sans);
|
||||
--font-mono: var(--font-mono);
|
||||
--font-serif: var(--font-serif);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--shadow-2xs: var(--shadow-2xs);
|
||||
--shadow-xs: var(--shadow-xs);
|
||||
--shadow-sm: var(--shadow-sm);
|
||||
--shadow: var(--shadow);
|
||||
--shadow-md: var(--shadow-md);
|
||||
--shadow-lg: var(--shadow-lg);
|
||||
--shadow-xl: var(--shadow-xl);
|
||||
--shadow-2xl: var(--shadow-2xl);
|
||||
}
|
||||
|
||||
/* shadcn 基础层 */
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground antialiased;
|
||||
}
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
|
||||
--font-sans: var(--font-sans);
|
||||
--font-mono: var(--font-mono);
|
||||
--font-serif: var(--font-serif);
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
|
||||
--shadow-2xs: var(--shadow-2xs);
|
||||
--shadow-xs: var(--shadow-xs);
|
||||
--shadow-sm: var(--shadow-sm);
|
||||
--shadow: var(--shadow);
|
||||
--shadow-md: var(--shadow-md);
|
||||
--shadow-lg: var(--shadow-lg);
|
||||
--shadow-xl: var(--shadow-xl);
|
||||
--shadow-2xl: var(--shadow-2xl);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user