Browse Source
* chore: update deps * feat: use jsonc/x language * chore: update eslint 10.0 * fix: no-useless-assignment * feat: add CLAUDE.md * chore: ignore * feat: claude * fix: lint * chore: suppot eslint v10 * fix: lint * fix: lint * fix: type check * fix: unit test * fix: Suggested fix * fix: unit test * chore: update stylelint v17 * chore: update all major deps * fix: echarts console warn * chore: update vitest v4 * feat: add skills ignores * chore: update deps * chore: update deps * fix: cspell * chore: update deps * chore: update tailwindcss v4 * chore: remove postcss config * fix: no use catalog * chore: tailwind v4 config * fix: tailwindcss v4 sort * feat: use eslint-plugin-better-tailwindcss * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix(lint): resolve prettier and better-tailwindcss formatting conflicts * fix(tailwind): update theme references and lint sources * style(format): normalize apps docs and playground vue files * style(format): normalize core ui-kit components * style(format): normalize effects ui and layout componentspull/7622/head
committed by
GitHub
289 changed files with 5293 additions and 6338 deletions
@ -0,0 +1,148 @@ |
|||||
|
# CLAUDE.md |
||||
|
|
||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
||||
|
|
||||
|
## 技术栈 |
||||
|
|
||||
|
1. 基于 **pnpm workspaces** + **Turborepo** 的 Vue 3 + TypeScript + Vite monorepo 项目。 |
||||
|
2. 提供多个 UI 组件库版本(Ant Design Vue、Element Plus、Naive UI、TDesign),共享同一套使用tailwindcss+shadcn-vue的UI组件库核心框架。 |
||||
|
3. 要求 Node ≥ 20.19.0,pnpm ≥ 10。 |
||||
|
4. 使用 **prettier** + **eslint** + **stylelint** 进行代码检查和格式化。 |
||||
|
5. 使用 **vitest** 进行单元测试。 |
||||
|
6. 使用 **commitlint** 进行提交规范。 |
||||
|
7. 使用 **czg** 进行提交规范。 |
||||
|
8. 使用 **lefthook** 进行提交规范。 |
||||
|
9. 使用 **vsh** 进行代码检查和格式化。 |
||||
|
10. 使用 **turbo** 进行构建。 |
||||
|
11. 使用 **vite** 进行开发。 |
||||
|
12. 使用 **vue-tsc** 进行类型检查。 |
||||
|
|
||||
|
```bash |
||||
|
# 其他检查 |
||||
|
pnpm check:circular # 循环依赖扫描 |
||||
|
pnpm check:dep # depcheck 依赖检查 |
||||
|
pnpm check:cspell # 拼写检查 |
||||
|
|
||||
|
# 清理 |
||||
|
pnpm clean # 删除 dist、node_modules 等产物 |
||||
|
pnpm reinstall # clean + 重新安装 |
||||
|
|
||||
|
# 交互式规范提交 |
||||
|
pnpm commit # czg 提交向导 |
||||
|
``` |
||||
|
|
||||
|
Turbo 任务通过 `dependsOn: ["^build"]` 级联,构建某个应用时会自动先构建其所有依赖包。 |
||||
|
|
||||
|
## Monorepo 目录结构 |
||||
|
|
||||
|
```text |
||||
|
apps/ |
||||
|
backend-mock/ # 基于 Nitro 的 mock API 服务(h3 路由 + faker.js 数据) |
||||
|
web-antd/ # Ant Design Vue 应用 |
||||
|
web-ele/ # Element Plus 应用 |
||||
|
web-naive/ # Naive UI 应用 |
||||
|
web-tdesign/ # TDesign Vue 应用 |
||||
|
|
||||
|
packages/ |
||||
|
@core/ # 框架核心(不依赖具体 UI 库) |
||||
|
base/ # 共享工具、缓存、颜色处理、类型定义 |
||||
|
composables/ # 核心 Vue composable |
||||
|
preferences/ # PreferenceManager 类(响应式、持久化配置) |
||||
|
ui-kit/ # UI 组件片段:form-ui、layout-ui、menu-ui、popup-ui、shadcn-ui、tabs-ui |
||||
|
effects/ # 高层模块,可依赖 @core 和 UI 库 |
||||
|
access/ # 路由/菜单生成与权限指令 |
||||
|
common-ui/ # 通用 UI 组件(ApiComponent、IconPicker、VCropper、Tippy 等) |
||||
|
hooks/ # useAppConfig 等 |
||||
|
layouts/ # BasicLayout、登录页、各类 widgets |
||||
|
plugins/ # Motion 等插件 |
||||
|
request/ # RequestClient(axios 封装 + 拦截器体系) |
||||
|
constants/ # 全局常量(LOGIN_PATH 等) |
||||
|
icons/ # Iconify 图标封装 |
||||
|
locales/ # vue-i18n 初始化、loadLocalesMap 工具 |
||||
|
preferences/ # 对外暴露 @core/preferences 的公共 API |
||||
|
stores/ # Pinia 全局 store:useAccessStore、useUserStore、useTabbarStore |
||||
|
styles/ # 全局 CSS / TailwindCSS 基础样式 |
||||
|
types/ # 共享 TypeScript 类型 |
||||
|
utils/ # 共享工具函数(mergeRouteModules、mapTree 等) |
||||
|
|
||||
|
internal/ |
||||
|
lint-configs/ # ESLint、Prettier、Stylelint、commitlint 配置包 |
||||
|
node-utils/ # 构建时 Node 工具 |
||||
|
tailwind-config/ # 共享 Tailwind 配置 |
||||
|
tsconfig/ # 基础 tsconfig |
||||
|
vite-config/ # 共享 Vite 配置工厂 + 插件集合 |
||||
|
|
||||
|
scripts/ |
||||
|
vsh/ # CLI 工具(lint、check-dep、check-circular、publint) |
||||
|
turbo-run/ # 交互式 turbo 运行器 |
||||
|
|
||||
|
playground/ # 组件演示场 |
||||
|
docs/ # VitePress 文档 |
||||
|
``` |
||||
|
|
||||
|
## 核心架构说明 |
||||
|
|
||||
|
### 应用启动流程 |
||||
|
|
||||
|
每个应用的 `src/main.ts` 调用 `bootstrap(namespace)`(位于 `src/bootstrap.ts`),依次执行: |
||||
|
|
||||
|
1. 初始化**组件适配器**(`src/adapter/component/index.ts`)——将通用表单组件名映射到具体 UI 库的组件。 |
||||
|
2. 调用 `initSetupVbenForm()`(`src/adapter/form.ts`)配置通用表单系统。 |
||||
|
3. 依次初始化 i18n、Pinia stores、权限指令、Tippy、路由、MotionPlugin,最后挂载到 `#app`。 |
||||
|
|
||||
|
### 偏好设置系统 |
||||
|
|
||||
|
`@vben/preferences` 导出单例 `preferences`(`PreferenceManager`)。它是响应式的,自动持久化到 localStorage(以应用 namespace 为前缀),并驱动主题 CSS 变量的更新。各应用在 `src/preferences.ts` 中调用 `defineOverridesPreferences()` 覆盖默认值,无需修改核心代码。 |
||||
|
|
||||
|
### 权限/访问系统 |
||||
|
|
||||
|
`@vben/access`(`packages/effects/access`)支持三种访问模式: |
||||
|
|
||||
|
- **frontend**:根据用户角色过滤静态路由。 |
||||
|
- **backend**:从接口(`getAllMenusApi`)获取菜单并动态注册路由。 |
||||
|
- **mixed**:同时使用以上两种方式。 |
||||
|
|
||||
|
路由守卫(`src/router/guard.ts`)在登录后首次导航时调用 `generateAccess()`,将结果存入 `useAccessStore`,再重定向到目标页。`v-access` 指令和 `<AccessControl>` 组件用于按权限码或角色控制 UI 元素显示。 |
||||
|
|
||||
|
### 请求客户端 |
||||
|
|
||||
|
`@vben/request` 将 Axios 封装为 `RequestClient`。每个应用在 `src/api/request.ts` 中创建自己的实例,挂载以下拦截器: |
||||
|
|
||||
|
- **请求拦截**:自动附加 Bearer Token 和 Accept-Language 头。 |
||||
|
- **`defaultResponseInterceptor`**:解包 `{ code, data, message }` 响应格式。 |
||||
|
- **`authenticateResponseInterceptor`**:处理 401,自动刷新 token 或跳转登录。 |
||||
|
- **`errorMessageResponseInterceptor`**:调用 `message.error()` 显示错误。 |
||||
|
|
||||
|
在 API 文件中从 `#/api/request` 引入 `requestClient`(自动解包响应)或 `baseRequestClient`(原始响应)。 |
||||
|
|
||||
|
### 路由组织 |
||||
|
|
||||
|
- `src/router/routes/modules/*.ts`:需要权限验证的动态路由。 |
||||
|
- `src/router/routes/core/`:始终可访问的路由(登录页、404 等)。 |
||||
|
- `mergeRouteModules(import.meta.glob(...))` 用于聚合路由模块文件。 |
||||
|
- 动态路由在运行时由权限系统注册。 |
||||
|
|
||||
|
### 适配器模式 |
||||
|
|
||||
|
每个 UI 库应用在 `src/adapter/` 下提供适配器,将 `@vben/common-ui` 的通用 form/modal/drawer 组件桥接到具体组件库。这是 `web-antd`、`web-ele` 等应用之间的主要差异所在。 |
||||
|
|
||||
|
### 全局 Pinia Store |
||||
|
|
||||
|
- `useAccessStore`:token、路由、菜单、锁屏、登录过期状态。 |
||||
|
- `useUserStore`:用户信息、角色、homePath。 |
||||
|
- `useTabbarStore`:已打开标签页管理。 |
||||
|
|
||||
|
所有 store 通过 `@vben/stores` 的 `initStores(app, { namespace })` 统一初始化。 |
||||
|
|
||||
|
### Mock 后端 |
||||
|
|
||||
|
`apps/backend-mock` 是一个 Nitro 服务器,可单独启动:`pnpm -F @vben/backend-mock start`。Vite 开发服务器通过 `vite.config.ts`(位于 `internal/vite-config`)将 API 请求代理到该服务。 |
||||
|
|
||||
|
## 开发约定 |
||||
|
|
||||
|
- **路径别名**:`#/*` 指向各应用的 `./src/*`(在 `package.json#imports` 中定义)。 |
||||
|
- **依赖版本管理**:内部包使用 `workspace:*`,第三方包使用 `catalog:`(版本集中在 `pnpm-workspace.yaml#catalog` 中管理)。 |
||||
|
- **提交规范**:遵循 Conventional Commits(`feat`、`fix`、`chore`、`docs`、`refactor`、`perf`、`test`、`ci`、`style`、`types`、`revert`),由 lefthook + commitlint 强制执行。 |
||||
|
- **pre-commit 钩子**(lefthook):自动对暂存文件执行 prettier + eslint + stylelint,推荐使用 `pnpm commit`(czg)提交。 |
||||
|
- **新增页面**:在 `src/views/` 下创建 `.vue` 文件,在 `src/router/routes/modules/` 下添加路由模块;若使用 backend 模式,还需确保后端接口返回对应菜单数据。 |
||||
|
- **国际化**:统一使用 `$t('key')`,locale 文件位于 `packages/locales/`,项目级国际化文件位于 `src/locales/langs`。 |
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1,11 +0,0 @@ |
|||||
import tailwindcssConfig from '@vben/tailwind-config'; |
|
||||
|
|
||||
export default { |
|
||||
...tailwindcssConfig, |
|
||||
content: [ |
|
||||
...tailwindcssConfig.content, |
|
||||
'.vitepress/**/*.{js,mts,ts,vue}', |
|
||||
'src/demos/**/*.{js,mts,ts,vue}', |
|
||||
'src/**/*.md', |
|
||||
], |
|
||||
}; |
|
||||
@ -0,0 +1,49 @@ |
|||||
|
import type { Linter } from 'eslint'; |
||||
|
|
||||
|
import { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults'; |
||||
|
import { SelectorKind } from 'eslint-plugin-better-tailwindcss/types'; |
||||
|
|
||||
|
import { interopDefault } from '../util'; |
||||
|
|
||||
|
export async function tailwindcss(): Promise<Linter.Config[]> { |
||||
|
const [pluginBetterTailwindcss] = await Promise.all([ |
||||
|
interopDefault(import('eslint-plugin-better-tailwindcss')), |
||||
|
] as const); |
||||
|
|
||||
|
return [ |
||||
|
{ |
||||
|
plugins: { |
||||
|
'better-tailwindcss': pluginBetterTailwindcss, |
||||
|
}, |
||||
|
// shadcn-ui 内部组件是自动生成的,不做太多限制
|
||||
|
ignores: ['packages/@core/ui-kit/shadcn-ui/**/**'], |
||||
|
settings: { |
||||
|
'better-tailwindcss': { |
||||
|
entryPoint: 'packages/@core/base/design/src/css/global.css', |
||||
|
selectors: [ |
||||
|
...getDefaultSelectors(), // preserve default selectors
|
||||
|
{ |
||||
|
kind: SelectorKind.Attribute, |
||||
|
match: [{ type: 'objectValues' }], |
||||
|
name: '^classNames$', |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
}, |
||||
|
rules: { |
||||
|
...pluginBetterTailwindcss.configs.recommended.rules, |
||||
|
'better-tailwindcss/enforce-consistent-class-order': [ |
||||
|
'error', |
||||
|
{ |
||||
|
detectComponentClasses: true, |
||||
|
unknownClassOrder: 'asc', |
||||
|
unknownClassPosition: 'start', |
||||
|
}, |
||||
|
], |
||||
|
// Let Prettier own wrapping decisions to avoid ping-pong formatting.
|
||||
|
'better-tailwindcss/enforce-consistent-line-wrapping': 'off', |
||||
|
'better-tailwindcss/no-unknown-classes': 'off', |
||||
|
}, |
||||
|
}, |
||||
|
]; |
||||
|
} |
||||
@ -1,10 +0,0 @@ |
|||||
import { defineBuildConfig } from 'unbuild'; |
|
||||
|
|
||||
export default defineBuildConfig({ |
|
||||
clean: true, |
|
||||
declaration: true, |
|
||||
entries: ['src/index', './src/postcss.config'], |
|
||||
rollup: { |
|
||||
emitCJS: true, |
|
||||
}, |
|
||||
}); |
|
||||
@ -1,67 +0,0 @@ |
|||||
{ |
|
||||
"name": "@vben/tailwind-config", |
|
||||
"version": "5.6.0", |
|
||||
"private": true, |
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin", |
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", |
|
||||
"repository": { |
|
||||
"type": "git", |
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git", |
|
||||
"directory": "internal/tailwind-config" |
|
||||
}, |
|
||||
"license": "MIT", |
|
||||
"type": "module", |
|
||||
"scripts": { |
|
||||
"stub": "pnpm unbuild --stub" |
|
||||
}, |
|
||||
"files": [ |
|
||||
"dist" |
|
||||
], |
|
||||
"main": "./dist/index.mjs", |
|
||||
"module": "./dist/index.mjs", |
|
||||
"types": "./dist/index.d.ts", |
|
||||
"typesVersions": { |
|
||||
"*": { |
|
||||
"*": [ |
|
||||
"./dist/*", |
|
||||
"./*" |
|
||||
] |
|
||||
} |
|
||||
}, |
|
||||
"exports": { |
|
||||
".": { |
|
||||
"types": "./src/index.ts", |
|
||||
"import": "./dist/index.mjs", |
|
||||
"require": "./dist/index.cjs" |
|
||||
}, |
|
||||
"./postcss": { |
|
||||
"types": "./src/postcss.config.ts", |
|
||||
"import": "./dist/postcss.config.mjs", |
|
||||
"require": "./dist/postcss.config.cjs", |
|
||||
"default": "./dist/postcss.config.mjs" |
|
||||
}, |
|
||||
"./*": "./*" |
|
||||
}, |
|
||||
"peerDependencies": { |
|
||||
"tailwindcss": "^3.4.3" |
|
||||
}, |
|
||||
"dependencies": { |
|
||||
"@iconify/json": "catalog:", |
|
||||
"@iconify/tailwind": "catalog:", |
|
||||
"@manypkg/get-packages": "catalog:", |
|
||||
"@tailwindcss/nesting": "catalog:", |
|
||||
"@tailwindcss/typography": "catalog:", |
|
||||
"autoprefixer": "catalog:", |
|
||||
"cssnano": "catalog:", |
|
||||
"jiti": "catalog:", |
|
||||
"postcss": "catalog:", |
|
||||
"postcss-antd-fixes": "catalog:", |
|
||||
"postcss-import": "catalog:", |
|
||||
"postcss-preset-env": "catalog:", |
|
||||
"tailwindcss": "catalog:", |
|
||||
"tailwindcss-animate": "catalog:" |
|
||||
}, |
|
||||
"devDependencies": { |
|
||||
"@types/postcss-import": "catalog:" |
|
||||
} |
|
||||
} |
|
||||
@ -1,266 +0,0 @@ |
|||||
import type { Config } from 'tailwindcss'; |
|
||||
|
|
||||
import path from 'node:path'; |
|
||||
|
|
||||
import { addDynamicIconSelectors } from '@iconify/tailwind'; |
|
||||
import { getPackagesSync } from '@manypkg/get-packages'; |
|
||||
import typographyPlugin from '@tailwindcss/typography'; |
|
||||
import animate from 'tailwindcss-animate'; |
|
||||
|
|
||||
import { enterAnimationPlugin } from './plugins/entry'; |
|
||||
|
|
||||
// import defaultTheme from 'tailwindcss/defaultTheme';
|
|
||||
|
|
||||
const { packages } = getPackagesSync(process.cwd()); |
|
||||
|
|
||||
const tailwindPackages: string[] = []; |
|
||||
|
|
||||
packages.forEach((pkg) => { |
|
||||
// apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui
|
|
||||
// if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) {
|
|
||||
tailwindPackages.push(pkg.dir); |
|
||||
// }
|
|
||||
}); |
|
||||
|
|
||||
const shadcnUiColors = { |
|
||||
accent: { |
|
||||
DEFAULT: 'hsl(var(--accent))', |
|
||||
foreground: 'hsl(var(--accent-foreground))', |
|
||||
hover: 'hsl(var(--accent-hover))', |
|
||||
lighter: 'has(val(--accent-lighter))', |
|
||||
}, |
|
||||
background: { |
|
||||
deep: 'hsl(var(--background-deep))', |
|
||||
DEFAULT: 'hsl(var(--background))', |
|
||||
}, |
|
||||
border: { |
|
||||
DEFAULT: 'hsl(var(--border))', |
|
||||
}, |
|
||||
card: { |
|
||||
DEFAULT: 'hsl(var(--card))', |
|
||||
foreground: 'hsl(var(--card-foreground))', |
|
||||
}, |
|
||||
destructive: { |
|
||||
...createColorsPalette('destructive'), |
|
||||
DEFAULT: 'hsl(var(--destructive))', |
|
||||
}, |
|
||||
|
|
||||
foreground: { |
|
||||
DEFAULT: 'hsl(var(--foreground))', |
|
||||
}, |
|
||||
|
|
||||
input: { |
|
||||
background: 'hsl(var(--input-background))', |
|
||||
DEFAULT: 'hsl(var(--input))', |
|
||||
}, |
|
||||
muted: { |
|
||||
DEFAULT: 'hsl(var(--muted))', |
|
||||
foreground: 'hsl(var(--muted-foreground))', |
|
||||
}, |
|
||||
popover: { |
|
||||
DEFAULT: 'hsl(var(--popover))', |
|
||||
foreground: 'hsl(var(--popover-foreground))', |
|
||||
}, |
|
||||
primary: { |
|
||||
...createColorsPalette('primary'), |
|
||||
DEFAULT: 'hsl(var(--primary))', |
|
||||
}, |
|
||||
|
|
||||
ring: 'hsl(var(--ring))', |
|
||||
secondary: { |
|
||||
DEFAULT: 'hsl(var(--secondary))', |
|
||||
desc: 'hsl(var(--secondary-desc))', |
|
||||
foreground: 'hsl(var(--secondary-foreground))', |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
const customColors = { |
|
||||
green: { |
|
||||
...createColorsPalette('green'), |
|
||||
foreground: 'hsl(var(--success-foreground))', |
|
||||
}, |
|
||||
header: { |
|
||||
DEFAULT: 'hsl(var(--header))', |
|
||||
}, |
|
||||
heavy: { |
|
||||
DEFAULT: 'hsl(var(--heavy))', |
|
||||
foreground: 'hsl(var(--heavy-foreground))', |
|
||||
}, |
|
||||
main: { |
|
||||
DEFAULT: 'hsl(var(--main))', |
|
||||
}, |
|
||||
overlay: { |
|
||||
content: 'hsl(var(--overlay-content))', |
|
||||
DEFAULT: 'hsl(var(--overlay))', |
|
||||
}, |
|
||||
red: { |
|
||||
...createColorsPalette('red'), |
|
||||
foreground: 'hsl(var(--destructive-foreground))', |
|
||||
}, |
|
||||
sidebar: { |
|
||||
deep: 'hsl(var(--sidebar-deep))', |
|
||||
DEFAULT: 'hsl(var(--sidebar))', |
|
||||
}, |
|
||||
success: { |
|
||||
...createColorsPalette('success'), |
|
||||
DEFAULT: 'hsl(var(--success))', |
|
||||
}, |
|
||||
warning: { |
|
||||
...createColorsPalette('warning'), |
|
||||
DEFAULT: 'hsl(var(--warning))', |
|
||||
}, |
|
||||
yellow: { |
|
||||
...createColorsPalette('yellow'), |
|
||||
foreground: 'hsl(var(--warning-foreground))', |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
export default { |
|
||||
content: [ |
|
||||
'./index.html', |
|
||||
...tailwindPackages.map((item) => |
|
||||
path.join(item, 'src/**/*.{vue,js,ts,jsx,tsx,svelte,astro,html}'), |
|
||||
), |
|
||||
], |
|
||||
darkMode: 'selector', |
|
||||
plugins: [ |
|
||||
animate, |
|
||||
typographyPlugin, |
|
||||
addDynamicIconSelectors(), |
|
||||
enterAnimationPlugin, |
|
||||
], |
|
||||
prefix: '', |
|
||||
theme: { |
|
||||
container: { |
|
||||
center: true, |
|
||||
padding: '2rem', |
|
||||
screens: { |
|
||||
'2xl': '1400px', |
|
||||
}, |
|
||||
}, |
|
||||
extend: { |
|
||||
animation: { |
|
||||
'accordion-down': 'accordion-down 0.2s ease-out', |
|
||||
'accordion-up': 'accordion-up 0.2s ease-out', |
|
||||
'collapsible-down': 'collapsible-down 0.2s ease-in-out', |
|
||||
'collapsible-up': 'collapsible-up 0.2s ease-in-out', |
|
||||
float: 'float 5s linear 0ms infinite', |
|
||||
}, |
|
||||
|
|
||||
animationDuration: { |
|
||||
'2000': '2000ms', |
|
||||
'3000': '3000ms', |
|
||||
}, |
|
||||
borderRadius: { |
|
||||
lg: 'var(--radius)', |
|
||||
md: 'calc(var(--radius) - 2px)', |
|
||||
sm: 'calc(var(--radius) - 4px)', |
|
||||
xl: 'calc(var(--radius) + 4px)', |
|
||||
}, |
|
||||
boxShadow: { |
|
||||
float: `0 6px 16px 0 rgb(0 0 0 / 8%),
|
|
||||
0 3px 6px -4px rgb(0 0 0 / 12%), |
|
||||
0 9px 28px 8px rgb(0 0 0 / 5%)`,
|
|
||||
}, |
|
||||
colors: { |
|
||||
...customColors, |
|
||||
...shadcnUiColors, |
|
||||
}, |
|
||||
fontFamily: { |
|
||||
sans: [ |
|
||||
'var(--font-family)', |
|
||||
// ...defaultTheme.fontFamily.sans
|
|
||||
], |
|
||||
}, |
|
||||
keyframes: { |
|
||||
'accordion-down': { |
|
||||
from: { height: '0' }, |
|
||||
to: { height: 'var(--reka-accordion-content-height)' }, |
|
||||
}, |
|
||||
'accordion-up': { |
|
||||
from: { height: 'var(--reka-accordion-content-height)' }, |
|
||||
to: { height: '0' }, |
|
||||
}, |
|
||||
'collapsible-down': { |
|
||||
from: { height: '0' }, |
|
||||
to: { height: 'var(--reka-collapsible-content-height)' }, |
|
||||
}, |
|
||||
'collapsible-up': { |
|
||||
from: { height: 'var(--reka-collapsible-content-height)' }, |
|
||||
to: { height: '0' }, |
|
||||
}, |
|
||||
float: { |
|
||||
'0%': { transform: 'translateY(0)' }, |
|
||||
'50%': { transform: 'translateY(-20px)' }, |
|
||||
'100%': { transform: 'translateY(0)' }, |
|
||||
}, |
|
||||
}, |
|
||||
zIndex: { |
|
||||
'100': '100', |
|
||||
'1000': '1000', |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
safelist: ['dark'], |
|
||||
} as Config; |
|
||||
|
|
||||
function createColorsPalette(name: string) { |
|
||||
// backgroundLightest: '#EFF6FF', // Tailwind CSS 默认的 `blue-50`
|
|
||||
// backgroundLighter: '#DBEAFE', // Tailwind CSS 默认的 `blue-100`
|
|
||||
// backgroundLight: '#BFDBFE', // Tailwind CSS 默认的 `blue-200`
|
|
||||
// borderLight: '#93C5FD', // Tailwind CSS 默认的 `blue-300`
|
|
||||
// border: '#60A5FA', // Tailwind CSS 默认的 `blue-400`
|
|
||||
// main: '#3B82F6', // Tailwind CSS 默认的 `blue-500`
|
|
||||
// hover: '#2563EB', // Tailwind CSS 默认的 `blue-600`
|
|
||||
// active: '#1D4ED8', // Tailwind CSS 默认的 `blue-700`
|
|
||||
// backgroundDark: '#1E40AF', // Tailwind CSS 默认的 `blue-800`
|
|
||||
// backgroundDarker: '#1E3A8A', // Tailwind CSS 默认的 `blue-900`
|
|
||||
// backgroundDarkest: '#172554', // Tailwind CSS 默认的 `blue-950`
|
|
||||
|
|
||||
// • backgroundLightest (#EFF6FF): 适用于最浅的背景色,可能用于非常轻微的阴影或卡片的背景。
|
|
||||
// • backgroundLighter (#DBEAFE): 适用于略浅的背景色,通常用于次要背景或略浅的区域。
|
|
||||
// • backgroundLight (#BFDBFE): 适用于浅色背景,可能用于输入框或表单区域的背景。
|
|
||||
// • borderLight (#93C5FD): 适用于浅色边框,可能用于输入框或卡片的边框。
|
|
||||
// • border (#60A5FA): 适用于普通边框,可能用于按钮或卡片的边框。
|
|
||||
// • main (#3B82F6): 适用于主要的主题色,通常用于按钮、链接或主要的强调色。
|
|
||||
// • hover (#2563EB): 适用于鼠标悬停状态下的颜色,例如按钮悬停时的背景色或边框色。
|
|
||||
// • active (#1D4ED8): 适用于激活状态下的颜色,例如按钮按下时的背景色或边框色。
|
|
||||
// • backgroundDark (#1E40AF): 适用于深色背景,可能用于主要按钮或深色卡片背景。
|
|
||||
// • backgroundDarker (#1E3A8A): 适用于更深的背景,通常用于头部导航栏或页脚。
|
|
||||
// • backgroundDarkest (#172554): 适用于最深的背景,可能用于非常深色的区域或极端对比色。
|
|
||||
|
|
||||
return { |
|
||||
50: `hsl(var(--${name}-50))`, |
|
||||
100: `hsl(var(--${name}-100))`, |
|
||||
200: `hsl(var(--${name}-200))`, |
|
||||
300: `hsl(var(--${name}-300))`, |
|
||||
400: `hsl(var(--${name}-400))`, |
|
||||
500: `hsl(var(--${name}-500))`, |
|
||||
600: `hsl(var(--${name}-600))`, |
|
||||
700: `hsl(var(--${name}-700))`, |
|
||||
// 800: `hsl(var(--${name}-800))`,
|
|
||||
// 900: `hsl(var(--${name}-900))`,
|
|
||||
// 950: `hsl(var(--${name}-950))`,
|
|
||||
// 激活状态下的颜色,适用于按钮按下时的背景色或边框色。
|
|
||||
active: `hsl(var(--${name}-700))`, |
|
||||
// 浅色背景,适用于输入框或表单区域的背景。
|
|
||||
'background-light': `hsl(var(--${name}-200))`, |
|
||||
// 适用于略浅的背景色,通常用于次要背景或略浅的区域。
|
|
||||
'background-lighter': `hsl(var(--${name}-100))`, |
|
||||
// 最浅的背景色,适用于非常轻微的阴影或卡片的背景。
|
|
||||
'background-lightest': `hsl(var(--${name}-50))`, |
|
||||
// 适用于普通边框,可能用于按钮或卡片的边框。
|
|
||||
border: `hsl(var(--${name}-400))`, |
|
||||
// 浅色边框,适用于输入框或卡片的边框。
|
|
||||
'border-light': `hsl(var(--${name}-300))`, |
|
||||
foreground: `hsl(var(--${name}-foreground))`, |
|
||||
// 鼠标悬停状态下的颜色,适用于按钮悬停时的背景色或边框色。
|
|
||||
hover: `hsl(var(--${name}-600))`, |
|
||||
// 主色文本
|
|
||||
text: `hsl(var(--${name}-500))`, |
|
||||
// 主色文本激活态
|
|
||||
'text-active': `hsl(var(--${name}-700))`, |
|
||||
// 主色文本悬浮态
|
|
||||
'text-hover': `hsl(var(--${name}-600))`, |
|
||||
}; |
|
||||
} |
|
||||
@ -1,3 +0,0 @@ |
|||||
declare module '@tailwindcss/nesting' { |
|
||||
export default any; |
|
||||
} |
|
||||
@ -1,53 +0,0 @@ |
|||||
import plugin from 'tailwindcss/plugin.js'; |
|
||||
|
|
||||
const enterAnimationPlugin = plugin(({ addUtilities }) => { |
|
||||
const maxChild = 5; |
|
||||
const utilities: Record<string, any> = {}; |
|
||||
for (let i = 1; i <= maxChild; i++) { |
|
||||
const baseDelay = 0.1; |
|
||||
const delay = `${baseDelay * i}s`; |
|
||||
|
|
||||
utilities[`.enter-x:nth-child(${i})`] = { |
|
||||
animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`, |
|
||||
opacity: '0', |
|
||||
transform: `translateX(50px)`, |
|
||||
}; |
|
||||
|
|
||||
utilities[`.enter-y:nth-child(${i})`] = { |
|
||||
animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`, |
|
||||
opacity: '0', |
|
||||
transform: `translateY(50px)`, |
|
||||
}; |
|
||||
|
|
||||
utilities[`.-enter-x:nth-child(${i})`] = { |
|
||||
animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`, |
|
||||
opacity: '0', |
|
||||
transform: `translateX(-50px)`, |
|
||||
}; |
|
||||
|
|
||||
utilities[`.-enter-y:nth-child(${i})`] = { |
|
||||
animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`, |
|
||||
opacity: '0', |
|
||||
transform: `translateY(-50px)`, |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
// 添加动画关键帧
|
|
||||
addUtilities(utilities); |
|
||||
addUtilities({ |
|
||||
'@keyframes enter-x-animation': { |
|
||||
to: { |
|
||||
opacity: '1', |
|
||||
transform: 'translateX(0)', |
|
||||
}, |
|
||||
}, |
|
||||
'@keyframes enter-y-animation': { |
|
||||
to: { |
|
||||
opacity: '1', |
|
||||
transform: 'translateY(0)', |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
export { enterAnimationPlugin }; |
|
||||
@ -1,15 +0,0 @@ |
|||||
import config from '.'; |
|
||||
|
|
||||
export default { |
|
||||
plugins: { |
|
||||
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}), |
|
||||
// Specifying the config is not necessary in most cases, but it is included
|
|
||||
autoprefixer: {}, |
|
||||
// 修复 element-plus 和 ant-design-vue 的样式和tailwindcss冲突问题
|
|
||||
'postcss-antd-fixes': { prefixes: ['ant', 'el'] }, |
|
||||
'postcss-import': {}, |
|
||||
'postcss-preset-env': {}, |
|
||||
tailwindcss: { config }, |
|
||||
'tailwindcss/nesting': {}, |
|
||||
}, |
|
||||
}; |
|
||||
@ -1,6 +0,0 @@ |
|||||
{ |
|
||||
"$schema": "https://json.schemastore.org/tsconfig", |
|
||||
"extends": "@vben/tsconfig/node.json", |
|
||||
"include": ["src"], |
|
||||
"exclude": ["node_modules"] |
|
||||
} |
|
||||
@ -0,0 +1,40 @@ |
|||||
|
import type { Plugin } from 'vite'; |
||||
|
|
||||
|
const REFERENCE_LINE = '@reference "@vben-core/design/theme";\n'; |
||||
|
|
||||
|
/** |
||||
|
* Auto-inject @reference into Vue SFC <style> blocks that use @apply. |
||||
|
* |
||||
|
* In Tailwind CSS v4, each Vue SFC <style scoped> block is processed as an |
||||
|
* independent CSS module. If a style block uses @apply with custom theme |
||||
|
* utilities (e.g. bg-primary, text-foreground), it needs access to the |
||||
|
* @theme definition via @reference. This plugin auto-injects it so |
||||
|
* individual components don't need to add it manually. |
||||
|
*/ |
||||
|
export function viteTailwindReferencePlugin(): Plugin { |
||||
|
return { |
||||
|
enforce: 'pre', |
||||
|
name: 'vite:tailwind-reference', |
||||
|
transform(code, id) { |
||||
|
// Only process Vue SFC style blocks
|
||||
|
if (!id.includes('.vue')) { |
||||
|
return null; |
||||
|
} |
||||
|
if (!id.includes('type=style')) { |
||||
|
return null; |
||||
|
} |
||||
|
// Skip if already has @reference
|
||||
|
if (code.includes('@reference')) { |
||||
|
return null; |
||||
|
} |
||||
|
// Only inject if the style block uses @apply
|
||||
|
if (!code.includes('@apply')) { |
||||
|
return null; |
||||
|
} |
||||
|
return { |
||||
|
code: REFERENCE_LINE + code, |
||||
|
map: null, |
||||
|
}; |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config'; |
|
||||
@ -1 +0,0 @@ |
|||||
export { default } from '@vben/tailwind-config/postcss'; |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue