feat: migrate to Tailwind CSS v4 (#7614)

* 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 components
This commit is contained in:
xingyu
2026-03-10 05:08:45 +08:00
committed by GitHub
parent aa7d8630b5
commit a4736a49f8
289 changed files with 5286 additions and 6331 deletions

View File

@@ -135,7 +135,7 @@ setupVbenVxeTable({
vxeUI.renderer.add('CellOperation', {
renderTableDefault({ attrs, options, props }, { column, row }) {
const defaultProps = { size: 'small', type: 'link', ...props };
let align = 'end';
let align: string;
switch (column.align) {
case 'center': {
align = 'center';

View File

@@ -107,8 +107,7 @@ export const useAuthStore = defineStore('auth', () => {
}
async function fetchUserInfo() {
let userInfo: null | UserInfo = null;
userInfo = await getUserInfoApi();
const userInfo = await getUserInfoApi();
userStore.setUserInfo(userInfo);
return userInfo;
}

View File

@@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
</AnalysisChartsTabs>
<div class="mt-5 w-full md:flex">
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问数量">
<AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问数量">
<AnalyticsVisitsData />
</AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问来源">
<AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问来源">
<AnalyticsVisitsSource />
</AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源">

View File

@@ -37,7 +37,7 @@ const { isFullscreen: isDomFullscreen, toggle: toggleDom } =
<div
ref="domRef"
class="mx-auto mt-10 flex h-64 w-1/2 items-center justify-center rounded-md bg-yellow-400"
class="mx-auto mt-10 flex-center h-64 w-1/2 rounded-md bg-yellow-400"
>
<Button class="mr-2" type="primary" @click="toggleDom">
{{ isDomFullscreen ? 'Exit Dom Full Screen' : 'Enter Dom Full Screen' }}

View File

@@ -33,7 +33,7 @@ const el6 = ref<SliderCaptchaActionType>();
<template>
<Page description="用于前端简单的拖动校验场景" title="滑块校验">
<Card class="mb-5" title="基础示例">
<div class="flex items-center justify-center p-4 px-[30%]">
<div class="flex-center p-4 px-[30%]">
<SliderCaptcha ref="el1" @success="handleSuccess" />
<Button class="ml-2" type="primary" @click="handleBtnClick(el1)">
还原
@@ -41,7 +41,7 @@ const el6 = ref<SliderCaptchaActionType>();
</div>
</Card>
<Card class="mb-5" title="自定义圆角">
<div class="flex items-center justify-center p-4 px-[30%]">
<div class="flex-center p-4 px-[30%]">
<SliderCaptcha
ref="el2"
class="rounded-full"
@@ -53,7 +53,7 @@ const el6 = ref<SliderCaptchaActionType>();
</div>
</Card>
<Card class="mb-5" title="自定义背景色">
<div class="flex items-center justify-center p-4 px-[30%]">
<div class="flex-center p-4 px-[30%]">
<SliderCaptcha
ref="el3"
:bar-style="{
@@ -69,7 +69,7 @@ const el6 = ref<SliderCaptchaActionType>();
</div>
</Card>
<Card class="mb-5" title="自定义拖拽图标">
<div class="flex items-center justify-center p-4 px-[30%]">
<div class="flex-center p-4 px-[30%]">
<SliderCaptcha ref="el4" @success="handleSuccess">
<template #actionIcon="{ isPassing }">
<Bell v-if="isPassing" />
@@ -82,7 +82,7 @@ const el6 = ref<SliderCaptchaActionType>();
</div>
</Card>
<Card class="mb-5" title="自定义文本">
<div class="flex items-center justify-center p-4 px-[30%]">
<div class="flex-center p-4 px-[30%]">
<SliderCaptcha
ref="el5"
success-text="成功"
@@ -95,7 +95,7 @@ const el6 = ref<SliderCaptchaActionType>();
</div>
</Card>
<Card class="mb-5" title="自定义内容(slot)">
<div class="flex items-center justify-center p-4 px-[30%]">
<div class="flex-center p-4 px-[30%]">
<SliderCaptcha ref="el6" @success="handleSuccess">
<template #text="{ isPassing }">
<template v-if="isPassing">

View File

@@ -20,7 +20,7 @@ const avatar = computed(() => {
<template>
<Page description="用于前端简单的拖动校验场景" title="滑块旋转校验">
<Card class="mb-5" title="基本示例">
<div class="flex items-center justify-center p-4">
<div class="flex-center p-4">
<SliderRotateCaptcha :src="avatar" @success="handleSuccess" />
</div>
</Card>

View File

@@ -14,7 +14,7 @@ function handleSuccess() {
title="拼图滑块校验"
>
<Card class="mb-5" title="基本示例">
<div class="flex items-center justify-center p-4">
<div class="flex-center p-4">
<SliderTranslateCaptcha
src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/pro-avatar.webp"
:canvas-width="420"

View File

@@ -85,7 +85,7 @@ function onFinished() {
</Button>
</template>
<Card title="基本用法">
<div class="flex w-full items-center justify-center pb-4">
<div class="flex-center w-full pb-4">
<CountTo v-bind="props" @started="onStarted" @finished="onFinished" />
</div>
<Form :model="props">
@@ -105,7 +105,7 @@ function onFinished() {
<template #addonAfter>
<IconifyIcon
v-tippy="`设置一个随机值`"
class="size-5 cursor-pointer outline-none"
class="size-5 cursor-pointer outline-hidden"
icon="ix:random-filled"
@click="changeNumber"
/>

View File

@@ -138,7 +138,7 @@ async function fillPartialData() {
</template>
<div class="space-y-4">
<div class="rounded bg-blue-50 p-4">
<div class="rounded-sm bg-blue-50 p-4">
<h3 class="mb-2 font-medium">测试说明</h3>
<ul class="list-inside list-disc space-y-1 text-sm">
<li>所有验证方法在验证失败时都会自动滚动到第一个错误字段</li>
@@ -146,7 +146,7 @@ async function fillPartialData() {
</ul>
</div>
<div class="rounded border p-4">
<div class="rounded-sm border p-4">
<h4 class="mb-3 font-medium">验证方法测试</h4>
<div class="flex flex-wrap gap-2">
<Button type="primary" @click="testValidateAndSubmit">
@@ -162,7 +162,7 @@ async function fillPartialData() {
</div>
</div>
<div class="rounded border p-4">
<div class="rounded-sm border p-4">
<h4 class="mb-3 font-medium">数据填充测试</h4>
<div class="flex flex-wrap gap-2">
<Button @click="fillPartialData"> 填充部分数据 </Button>

View File

@@ -28,7 +28,7 @@ export const json1 = {
export const json2 = JSON.parse(`
{
"id": "chatcmpl-123",
"id": "chatgpt-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-3.5-turbo-0613",

View File

@@ -42,11 +42,7 @@ const leftMaxWidth = ref(props.leftMaxWidth || 100);
<template #left="{ isCollapsed, expand }">
<div v-if="isCollapsed" @click="expand">
<Tooltip title="点击展开左侧">
<Button
shape="circle"
type="primary"
class="flex items-center justify-center"
>
<Button shape="circle" type="primary" class="flex-center">
<template #icon>
<IconifyIcon class="text-2xl" icon="bi:arrow-right" />
</template>
@@ -56,7 +52,7 @@ const leftMaxWidth = ref(props.leftMaxWidth || 100);
<div
v-else
:style="{ minWidth: '200px' }"
class="mr-2 rounded-[var(--radius)] border border-border bg-card p-2"
class="mr-2 rounded-(--radius) border border-border bg-card p-2"
>
<p>这里是左侧内容</p>
<p>这里是左侧内容</p>

View File

@@ -37,16 +37,13 @@ const loadingV = refAutoReset(false, 3000);
<Loading
:spinning="loading"
text="正在加载..."
class="flex h-full w-full items-center justify-center"
class="flex-center size-full"
>
<Button type="primary" @click="loading = true">默认动画</Button>
</Loading>
</div>
<div class="size-40">
<Loading
:spinning="loading"
class="flex h-full w-full items-center justify-center"
>
<Loading :spinning="loading" class="flex-center size-full">
<Button type="primary" @click="loading = true">自定义动画1</Button>
<template #icon>
<IconifyIcon
@@ -57,10 +54,7 @@ const loadingV = refAutoReset(false, 3000);
</Loading>
</div>
<div class="size-40">
<Loading
:spinning="loading"
class="flex h-full w-full items-center justify-center"
>
<Loading :spinning="loading" class="flex-center size-full">
<Button type="primary" @click="loading = true">自定义动画2</Button>
<template #icon>
<IconifyIcon
@@ -90,10 +84,7 @@ const loadingV = refAutoReset(false, 3000);
<template #actions>
Spinner组件是Loading组件的一个特例只有一个固定的统一样式
</template>
<Spinner
:spinning="spinning"
class="flex size-40 items-center justify-center"
>
<Spinner :spinning="spinning" class="flex-center size-40">
<Button type="primary" @click="spinning = true">显示Spinner</Button>
</Spinner>
</Card>

View File

@@ -84,9 +84,7 @@ function openDocPage() {
title="使用组件(将内部作为一个整体添加动画)"
:body-style="{ padding: 0 }"
>
<div
class="relative flex min-h-32 items-center justify-center gap-2 overflow-hidden"
>
<div class="relative flex-center min-h-32 gap-2 overflow-hidden">
<Motion
v-bind="motionProps"
v-if="showCard2"
@@ -96,9 +94,7 @@ function openDocPage() {
<span>附属组件会作为整体处理动画</span>
</Motion>
</div>
<div
class="relative flex min-h-32 items-center justify-center gap-2 overflow-hidden"
>
<div class="relative flex-center min-h-32 gap-2 overflow-hidden">
<div v-if="showCard2" class="flex items-center gap-2">
<span>顺序延迟</span>
<Motion
@@ -158,9 +154,7 @@ function openDocPage() {
title="分组动画(每个子元素都会应用相同的独立动画)"
:body-style="{ padding: 0 }"
>
<div
class="relative flex min-h-32 items-center justify-center gap-2 overflow-hidden"
>
<div class="relative flex-center min-h-32 gap-2 overflow-hidden">
<MotionGroup v-bind="motionGroupProps" v-if="showCard3">
<Button size="large">按钮1</Button>
<Button size="large">按钮2</Button>

View File

@@ -50,7 +50,7 @@ const resize = (size?: TSize, rect?: TSize) => {
>
<div
:style="{ backgroundColor: colorMap[idx] }"
class="h-full w-full"
class="size-full"
></div>
</VResize>
</template>

View File

@@ -122,7 +122,7 @@ function onDelete(row: SystemMenuApi.SystemMenu) {
</template>
<template #title="{ row }">
<div class="flex w-full items-center gap-1">
<div class="size-5 flex-shrink-0">
<div class="size-5 shrink-0">
<IconifyIcon
v-if="row.type === 'button'"
icon="carbon:security"