Merge remote-tracking branch 'vben/main' into vben-dev
# Conflicts: # .gitignore # .husky/pre-commit # .vscode/extensions.json # .vscode/settings.json # README.ja-JP.md # README.md # README.zh-CN.md # apps/backend-mock/api/auth/codes.ts # apps/backend-mock/api/auth/login.post.ts # apps/backend-mock/api/auth/logout.post.ts # apps/backend-mock/api/auth/refresh.post.ts # apps/backend-mock/api/menu/all.ts # apps/backend-mock/api/status.ts # apps/backend-mock/api/table/list.ts # apps/backend-mock/api/test.get.ts # apps/backend-mock/api/test.post.ts # apps/backend-mock/api/user/info.ts # apps/backend-mock/middleware/1.api.ts # apps/backend-mock/nitro.config.ts # apps/backend-mock/package.json # apps/backend-mock/routes/[...].ts # apps/backend-mock/tsconfig.json # apps/backend-mock/utils/cookie-utils.ts # apps/backend-mock/utils/jwt-utils.ts # apps/backend-mock/utils/mock-data.ts # apps/backend-mock/utils/response.ts # apps/web-antd/.env # apps/web-antd/src/adapter/component/index.ts # apps/web-antd/src/adapter/form.ts # apps/web-antd/src/api/request.ts # apps/web-antd/src/layouts/auth.vue # apps/web-antd/src/layouts/basic.vue # apps/web-antd/src/preferences.ts # apps/web-antd/src/router/guard.ts # apps/web-antd/src/router/routes/modules/demos.ts # apps/web-antd/src/store/auth.ts # apps/web-antd/src/views/demos/antd/index.vue # apps/web-antdv-next/vite.config.ts # apps/web-ele/.env # apps/web-ele/index.html # apps/web-ele/package.json # apps/web-ele/src/adapter/component/index.ts # apps/web-ele/src/adapter/form.ts # apps/web-ele/src/adapter/vxe-table.ts # apps/web-ele/src/api/request.ts # apps/web-ele/src/bootstrap.ts # apps/web-ele/src/layouts/auth.vue # apps/web-ele/src/layouts/basic.vue # apps/web-ele/src/locales/index.ts # apps/web-ele/src/locales/langs/en-US/demos.json # apps/web-ele/src/locales/langs/en-US/page.json # apps/web-ele/src/locales/langs/zh-CN/demos.json # apps/web-ele/src/locales/langs/zh-CN/page.json # apps/web-ele/src/preferences.ts # apps/web-ele/src/router/guard.ts # apps/web-ele/src/router/routes/core.ts # apps/web-ele/src/router/routes/modules/dashboard.ts # apps/web-ele/src/router/routes/modules/demos.ts # apps/web-ele/src/router/routes/modules/vben.ts # apps/web-ele/src/store/auth.ts # apps/web-ele/src/views/_core/authentication/code-login.vue # apps/web-ele/src/views/_core/authentication/forget-password.vue # apps/web-ele/src/views/_core/authentication/register.vue # apps/web-ele/src/views/dashboard/analytics/analytics-trends.vue # apps/web-ele/src/views/dashboard/analytics/analytics-visits-data.vue # apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue # apps/web-ele/src/views/dashboard/analytics/analytics-visits-source.vue # apps/web-ele/src/views/dashboard/analytics/analytics-visits.vue # apps/web-ele/src/views/dashboard/analytics/index.vue # apps/web-ele/src/views/demos/element/index.vue # apps/web-ele/src/views/demos/form/basic.vue # apps/web-ele/tsconfig.json # apps/web-ele/tsconfig.node.json # apps/web-naive/.env # apps/web-naive/index.html # apps/web-naive/package.json # apps/web-naive/src/adapter/component/index.ts # apps/web-naive/src/adapter/form.ts # apps/web-naive/src/adapter/vxe-table.ts # apps/web-naive/src/api/request.ts # apps/web-naive/src/bootstrap.ts # apps/web-naive/src/layouts/auth.vue # apps/web-naive/src/layouts/basic.vue # apps/web-naive/src/locales/index.ts # apps/web-naive/src/locales/langs/en-US/demos.json # apps/web-naive/src/locales/langs/en-US/page.json # apps/web-naive/src/locales/langs/zh-CN/demos.json # apps/web-naive/src/locales/langs/zh-CN/page.json # apps/web-naive/src/preferences.ts # apps/web-naive/src/router/guard.ts # apps/web-naive/src/router/routes/core.ts # apps/web-naive/src/router/routes/modules/dashboard.ts # apps/web-naive/src/router/routes/modules/demos.ts # apps/web-naive/src/router/routes/modules/vben.ts # apps/web-naive/src/store/auth.ts # apps/web-naive/src/views/_core/authentication/code-login.vue # apps/web-naive/src/views/_core/authentication/forget-password.vue # apps/web-naive/src/views/_core/authentication/register.vue # apps/web-naive/src/views/dashboard/analytics/analytics-trends.vue # apps/web-naive/src/views/dashboard/analytics/analytics-visits-data.vue # apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue # apps/web-naive/src/views/dashboard/analytics/analytics-visits-source.vue # apps/web-naive/src/views/dashboard/analytics/analytics-visits.vue # apps/web-naive/src/views/dashboard/analytics/index.vue # apps/web-naive/src/views/demos/form/basic.vue # apps/web-naive/src/views/demos/naive/index.vue # apps/web-naive/tsconfig.json # apps/web-naive/tsconfig.node.json # apps/web-naive/vite.config.ts # docs/.vitepress/components/demo-preview.vue # docs/.vitepress/components/preview-group.vue # docs/.vitepress/config/en.mts # docs/.vitepress/config/index.mts # docs/.vitepress/config/plugins/demo-preview.ts # docs/.vitepress/config/shared.mts # docs/.vitepress/config/zh.mts # docs/.vitepress/theme/components/site-layout.vue # docs/.vitepress/theme/components/vben-contributors.vue # docs/.vitepress/theme/styles/variables.css # docs/package.json # docs/src/_env/adapter/component.ts # docs/src/_env/adapter/form.ts # docs/src/_env/adapter/vxe-table.ts # docs/src/commercial/community.md # docs/src/components/common-ui/vben-count-to-animator.md # docs/src/components/common-ui/vben-drawer.md # docs/src/components/common-ui/vben-form.md # docs/src/components/common-ui/vben-modal.md # docs/src/components/common-ui/vben-vxe-table.md # docs/src/components/layout-ui/page.md # docs/src/demos/vben-drawer/auto-height/drawer.vue # docs/src/demos/vben-drawer/basic/index.vue # docs/src/demos/vben-drawer/dynamic/index.vue # docs/src/demos/vben-drawer/shared-data/drawer.vue # docs/src/demos/vben-drawer/shared-data/index.vue # docs/src/demos/vben-form/api/index.vue # docs/src/demos/vben-form/basic/index.vue # docs/src/demos/vben-form/custom/index.vue # docs/src/demos/vben-form/dynamic/index.vue # docs/src/demos/vben-form/query/index.vue # docs/src/demos/vben-form/rules/index.vue # docs/src/demos/vben-modal/auto-height/modal.vue # docs/src/demos/vben-modal/basic/index.vue # docs/src/demos/vben-modal/dynamic/index.vue # docs/src/demos/vben-modal/shared-data/index.vue # docs/src/demos/vben-modal/shared-data/modal.vue # docs/src/demos/vben-vxe-table/basic/index.vue # docs/src/demos/vben-vxe-table/custom-cell/index.vue # docs/src/demos/vben-vxe-table/edit-row/index.vue # docs/src/demos/vben-vxe-table/fixed/index.vue # docs/src/demos/vben-vxe-table/form/index.vue # docs/src/demos/vben-vxe-table/remote/index.vue # docs/src/demos/vben-vxe-table/tree/index.vue # docs/src/demos/vben-vxe-table/virtual/index.vue # docs/src/en/guide/essentials/development.md # docs/src/en/guide/essentials/external-module.md # docs/src/en/guide/essentials/route.md # docs/src/en/guide/essentials/server.md # docs/src/en/guide/essentials/settings.md # docs/src/en/guide/in-depth/access.md # docs/src/en/guide/in-depth/loading.md # docs/src/en/guide/in-depth/locale.md # docs/src/en/guide/in-depth/theme.md # docs/src/en/guide/introduction/quick-start.md # docs/src/en/guide/introduction/thin.md # docs/src/en/guide/introduction/vben.md # docs/src/en/guide/other/faq.md # docs/src/en/guide/project/dir.md # docs/src/en/guide/project/standard.md # docs/src/en/guide/project/tailwindcss.md # docs/src/en/index.md # docs/src/friend-links/index.md # docs/src/guide/essentials/build.md # docs/src/guide/essentials/development.md # docs/src/guide/essentials/external-module.md # docs/src/guide/essentials/route.md # docs/src/guide/essentials/server.md # docs/src/guide/essentials/settings.md # docs/src/guide/in-depth/access.md # docs/src/guide/in-depth/check-updates.md # docs/src/guide/in-depth/loading.md # docs/src/guide/in-depth/locale.md # docs/src/guide/in-depth/theme.md # docs/src/guide/introduction/quick-start.md # docs/src/guide/introduction/thin.md # docs/src/guide/introduction/vben.md # docs/src/guide/introduction/why.md # docs/src/guide/other/faq.md # docs/src/guide/project/dir.md # docs/src/guide/project/standard.md # docs/src/guide/project/tailwindcss.md # docs/src/index.md # docs/tsconfig.json # package.json # packages/@core/ui-kit/shadcn-ui/src/components/logo/logo.vue # packages/constants/src/core.ts # packages/effects/common-ui/src/components/index.ts # packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue # packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue # packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue # packages/effects/layouts/src/authentication/authentication.vue # packages/effects/layouts/src/basic/layout.vue # packages/effects/layouts/src/widgets/notification/notification.vue # packages/effects/layouts/src/widgets/notification/types.ts # packages/icons/src/iconify/index.ts # packages/icons/src/svg/index.ts # packages/stores/src/modules/user.ts # playground/src/adapter/component/index.ts # playground/src/store/auth.ts # pnpm-lock.yaml # vben-admin.code-workspace
This commit is contained in:
@@ -15,9 +15,22 @@ export function useAppConfig(
|
||||
? window._VBEN_ADMIN_PRO_APP_CONF_
|
||||
: (env as VbenAdminProAppConfigRaw);
|
||||
|
||||
const { VITE_GLOB_API_URL } = config;
|
||||
const {
|
||||
VITE_GLOB_API_URL,
|
||||
VITE_GLOB_AUTH_DINGDING_CORP_ID,
|
||||
VITE_GLOB_AUTH_DINGDING_CLIENT_ID,
|
||||
} = config;
|
||||
|
||||
return {
|
||||
const applicationConfig: ApplicationConfig = {
|
||||
apiURL: VITE_GLOB_API_URL,
|
||||
auth: {},
|
||||
};
|
||||
if (VITE_GLOB_AUTH_DINGDING_CORP_ID && VITE_GLOB_AUTH_DINGDING_CLIENT_ID) {
|
||||
applicationConfig.auth.dingding = {
|
||||
clientId: VITE_GLOB_AUTH_DINGDING_CLIENT_ID,
|
||||
corpId: VITE_GLOB_AUTH_DINGDING_CORP_ID,
|
||||
};
|
||||
}
|
||||
|
||||
return applicationConfig;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export function useAntdDesignTokens() {
|
||||
colorSuccess: '',
|
||||
colorTextBase: '',
|
||||
colorWarning: '',
|
||||
zIndexPopupBase: 2000, // 调整基础弹层层级,避免下拉等组件被弹窗或者最大化状态下的表格遮挡
|
||||
});
|
||||
|
||||
const getCssVariableValue = (variable: string, isColor: boolean = true) => {
|
||||
@@ -192,67 +193,107 @@ export function useElementPlusDesignTokens() {
|
||||
|
||||
'--el-border-radius-base': getCssVariableValue('--radius', false),
|
||||
'--el-color-danger': getCssVariableValue('--destructive-500'),
|
||||
'--el-color-danger-dark-2': getCssVariableValue('--destructive'),
|
||||
'--el-color-danger-light-3': getCssVariableValue('--destructive-400'),
|
||||
'--el-color-danger-light-5': getCssVariableValue('--destructive-300'),
|
||||
'--el-color-danger-light-7': getCssVariableValue('--destructive-200'),
|
||||
'--el-color-danger-dark-2': isDark.value
|
||||
? getCssVariableValue('--destructive-400')
|
||||
: getCssVariableValue('--destructive-600'),
|
||||
'--el-color-danger-light-3': isDark.value
|
||||
? getCssVariableValue('--destructive-600')
|
||||
: getCssVariableValue('--destructive-400'),
|
||||
'--el-color-danger-light-5': isDark.value
|
||||
? getCssVariableValue('--destructive-700')
|
||||
: getCssVariableValue('--destructive-300'),
|
||||
'--el-color-danger-light-7': isDark.value
|
||||
? getCssVariableValue('--destructive-800')
|
||||
: getCssVariableValue('--destructive-200'),
|
||||
'--el-color-danger-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--destructive-900')
|
||||
: getCssVariableValue('--destructive-100'),
|
||||
'--el-color-danger-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--destructive-950')
|
||||
: getCssVariableValue('--destructive-50'),
|
||||
|
||||
'--el-color-error': getCssVariableValue('--destructive-500'),
|
||||
'--el-color-error-dark-2': getCssVariableValue('--destructive'),
|
||||
'--el-color-error-light-3': getCssVariableValue('--destructive-400'),
|
||||
'--el-color-error-light-5': getCssVariableValue('--destructive-300'),
|
||||
'--el-color-error-light-7': getCssVariableValue('--destructive-200'),
|
||||
'--el-color-error-dark-2': isDark.value
|
||||
? getCssVariableValue('--destructive-400')
|
||||
: getCssVariableValue('--destructive-600'),
|
||||
'--el-color-error-light-3': isDark.value
|
||||
? getCssVariableValue('--destructive-600')
|
||||
: getCssVariableValue('--destructive-400'),
|
||||
'--el-color-error-light-5': isDark.value
|
||||
? getCssVariableValue('--destructive-700')
|
||||
: getCssVariableValue('--destructive-300'),
|
||||
'--el-color-error-light-7': isDark.value
|
||||
? getCssVariableValue('--destructive-800')
|
||||
: getCssVariableValue('--destructive-200'),
|
||||
'--el-color-error-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--destructive-900')
|
||||
: getCssVariableValue('--destructive-100'),
|
||||
'--el-color-error-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--destructive-950')
|
||||
: getCssVariableValue('--destructive-50'),
|
||||
|
||||
'--el-color-info-light-5': border,
|
||||
'--el-color-info-light-8': border,
|
||||
'--el-color-info-light-9': getCssVariableValue('--info'), // getCssVariableValue('--secondary'),
|
||||
|
||||
'--el-color-primary': getCssVariableValue('--primary-500'),
|
||||
'--el-color-primary-dark-2': getCssVariableValue('--primary'),
|
||||
'--el-color-primary-light-3': getCssVariableValue('--primary-400'),
|
||||
'--el-color-primary-light-5': getCssVariableValue('--primary-300'),
|
||||
'--el-color-primary-dark-2': isDark.value
|
||||
? getCssVariableValue('--primary-400')
|
||||
: getCssVariableValue('--primary-600'),
|
||||
'--el-color-primary-light-3': isDark.value
|
||||
? getCssVariableValue('--primary-600')
|
||||
: getCssVariableValue('--primary-400'),
|
||||
'--el-color-primary-light-5': isDark.value
|
||||
? getCssVariableValue('--primary-700')
|
||||
: getCssVariableValue('--primary-300'),
|
||||
'--el-color-primary-light-7': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--primary-800')
|
||||
: getCssVariableValue('--primary-200'),
|
||||
'--el-color-primary-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--primary-900')
|
||||
: getCssVariableValue('--primary-100'),
|
||||
'--el-color-primary-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--primary-950')
|
||||
: getCssVariableValue('--primary-50'),
|
||||
|
||||
'--el-color-success': getCssVariableValue('--success-500'),
|
||||
'--el-color-success-dark-2': getCssVariableValue('--success'),
|
||||
'--el-color-success-light-3': getCssVariableValue('--success-400'),
|
||||
'--el-color-success-light-5': getCssVariableValue('--success-300'),
|
||||
'--el-color-success-light-7': getCssVariableValue('--success-200'),
|
||||
'--el-color-success-dark-2': isDark.value
|
||||
? getCssVariableValue('--success-400')
|
||||
: getCssVariableValue('--success-600'),
|
||||
'--el-color-success-light-3': isDark.value
|
||||
? getCssVariableValue('--success-600')
|
||||
: getCssVariableValue('--success-400'),
|
||||
'--el-color-success-light-5': isDark.value
|
||||
? getCssVariableValue('--success-700')
|
||||
: getCssVariableValue('--success-300'),
|
||||
'--el-color-success-light-7': isDark.value
|
||||
? getCssVariableValue('--success-800')
|
||||
: getCssVariableValue('--success-200'),
|
||||
'--el-color-success-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--success-900')
|
||||
: getCssVariableValue('--success-100'),
|
||||
'--el-color-success-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--success-950')
|
||||
: getCssVariableValue('--success-50'),
|
||||
|
||||
'--el-color-warning': getCssVariableValue('--warning-500'),
|
||||
'--el-color-warning-dark-2': getCssVariableValue('--warning'),
|
||||
'--el-color-warning-light-3': getCssVariableValue('--warning-400'),
|
||||
'--el-color-warning-light-5': getCssVariableValue('--warning-300'),
|
||||
'--el-color-warning-light-7': getCssVariableValue('--warning-200'),
|
||||
'--el-color-warning-dark-2': isDark.value
|
||||
? getCssVariableValue('--warning-400')
|
||||
: getCssVariableValue('--warning-600'),
|
||||
'--el-color-warning-light-3': isDark.value
|
||||
? getCssVariableValue('--warning-600')
|
||||
: getCssVariableValue('--warning-400'),
|
||||
'--el-color-warning-light-5': isDark.value
|
||||
? getCssVariableValue('--warning-700')
|
||||
: getCssVariableValue('--warning-300'),
|
||||
'--el-color-warning-light-7': isDark.value
|
||||
? getCssVariableValue('--warning-800')
|
||||
: getCssVariableValue('--warning-200'),
|
||||
'--el-color-warning-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--warning-900')
|
||||
: getCssVariableValue('--warning-100'),
|
||||
'--el-color-warning-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--warning-950')
|
||||
: getCssVariableValue('--warning-50'),
|
||||
|
||||
'--el-fill-color': getCssVariableValue('--accent'),
|
||||
@@ -278,3 +319,103 @@ export function useElementPlusDesignTokens() {
|
||||
{ immediate: true },
|
||||
);
|
||||
}
|
||||
|
||||
export function useTDesignDesignTokens() {
|
||||
const { isDark } = usePreferences();
|
||||
const rootStyles = getComputedStyle(document.documentElement);
|
||||
|
||||
const getCssVariableValue = (variable: string, isColor: boolean = true) => {
|
||||
const value = rootStyles.getPropertyValue(variable);
|
||||
return isColor ? convertToRgb(`hsl(${value})`) : value;
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成某个语义色对应的 TDesign 变量(语义变体 + 1~10 色阶)。
|
||||
* TDesign 的色阶在亮色模式下 1 最浅、10 最深,暗色模式下方向相反,
|
||||
* 这里根据当前模式把 Vben 的 50~900 色板映射到对应方向。
|
||||
* @param tdName TDesign 颜色名,如 `brand`、`error`
|
||||
* @param vbenName Vben 颜色名,如 `primary`、`destructive`
|
||||
*/
|
||||
const getColorTokens = (tdName: string, vbenName: string) => {
|
||||
const dark = isDark.value;
|
||||
const getColor = (level?: number) =>
|
||||
getCssVariableValue(
|
||||
level === undefined ? `--${vbenName}` : `--${vbenName}-${level}`,
|
||||
);
|
||||
|
||||
// 亮色模式 1 最浅、10 最深;暗色模式相反
|
||||
const lightScale = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
|
||||
const scaleLevels = dark ? [...lightScale].toReversed() : lightScale;
|
||||
const scale: Record<string, string> = {};
|
||||
scaleLevels.forEach((level, index) => {
|
||||
scale[`--td-${tdName}-color-${index + 1}`] = getColor(level);
|
||||
});
|
||||
|
||||
return {
|
||||
[`--td-${tdName}-color`]: getColor(),
|
||||
[`--td-${tdName}-color-active`]: getColor(dark ? 300 : 700),
|
||||
[`--td-${tdName}-color-disabled`]: getColor(dark ? 800 : 200),
|
||||
[`--td-${tdName}-color-focus`]: getColor(dark ? 900 : 100),
|
||||
[`--td-${tdName}-color-hover`]: getColor(dark ? 400 : 600),
|
||||
[`--td-${tdName}-color-light`]: getColor(dark ? 950 : 50),
|
||||
[`--td-${tdName}-color-light-hover`]: getColor(dark ? 900 : 100),
|
||||
...scale,
|
||||
};
|
||||
};
|
||||
|
||||
watch(
|
||||
() => preferences.theme,
|
||||
() => {
|
||||
const variables: Record<string, string> = {
|
||||
// 品牌色(主题色)、功能色
|
||||
...getColorTokens('brand', 'primary'),
|
||||
...getColorTokens('error', 'destructive'),
|
||||
...getColorTokens('warning', 'warning'),
|
||||
...getColorTokens('success', 'success'),
|
||||
|
||||
// 文字颜色
|
||||
'--td-text-color-anti': getCssVariableValue('--primary-foreground'),
|
||||
'--td-text-color-brand': getCssVariableValue('--primary'),
|
||||
'--td-text-color-disabled': getCssVariableValue('--muted-foreground'),
|
||||
'--td-text-color-link': getCssVariableValue('--primary'),
|
||||
'--td-text-color-placeholder': getCssVariableValue(
|
||||
'--input-placeholder',
|
||||
),
|
||||
'--td-text-color-primary': getCssVariableValue('--foreground'),
|
||||
'--td-text-color-secondary': getCssVariableValue('--muted-foreground'),
|
||||
|
||||
// 背景颜色
|
||||
'--td-bg-color-component': getCssVariableValue('--accent'),
|
||||
'--td-bg-color-component-active':
|
||||
getCssVariableValue('--accent-darker'),
|
||||
'--td-bg-color-component-disabled': getCssVariableValue('--muted'),
|
||||
'--td-bg-color-component-hover': getCssVariableValue('--accent-hover'),
|
||||
'--td-bg-color-container': getCssVariableValue('--background'),
|
||||
'--td-bg-color-container-active':
|
||||
getCssVariableValue('--accent-darker'),
|
||||
'--td-bg-color-container-hover': getCssVariableValue('--accent'),
|
||||
'--td-bg-color-container-select': getCssVariableValue('--accent'),
|
||||
'--td-bg-color-page': getCssVariableValue('--background-deep'),
|
||||
'--td-bg-color-secondarycontainer': getCssVariableValue('--card'),
|
||||
|
||||
// 边框颜色
|
||||
'--td-border-level-1-color': getCssVariableValue('--border'),
|
||||
'--td-border-level-2-color': getCssVariableValue('--border'),
|
||||
'--td-component-border': getCssVariableValue('--border'),
|
||||
'--td-component-stroke': getCssVariableValue('--border'),
|
||||
|
||||
// 圆角
|
||||
'--td-radius-default': getCssVariableValue('--radius', false),
|
||||
};
|
||||
|
||||
// TDesign 将亮/暗变量定义在 `:root[theme-mode='light'|'dark']` 下,
|
||||
// 需要写入相同优先级的选择器才能覆盖默认值
|
||||
const selector = isDark.value
|
||||
? `:root[theme-mode='dark']`
|
||||
: `:root[theme-mode='light']`;
|
||||
|
||||
updateCSSVariables(variables, `__vben_design_tdesign_styles__`, selector);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,44 +1,140 @@
|
||||
import type { Arrayable, MaybeElementRef } from '@vueuse/core';
|
||||
|
||||
import { computed, onUnmounted, ref, watch } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { computed, effectScope, ref, unref, watch } from 'vue';
|
||||
|
||||
import { isFunction } from '@vben/utils';
|
||||
|
||||
import { useMouseInElement } from '@vueuse/core';
|
||||
import { tryOnScopeDispose, useElementHover } from '@vueuse/core';
|
||||
|
||||
interface HoverDelayOptions {
|
||||
/** 鼠标进入延迟时间 */
|
||||
enterDelay?: (() => number) | number;
|
||||
/** 鼠标离开延迟时间 */
|
||||
leaveDelay?: (() => number) | number;
|
||||
}
|
||||
|
||||
const DEFAULT_LEAVE_DELAY = 500; // 鼠标离开延迟时间,默认为 500ms
|
||||
const DEFAULT_ENTER_DELAY = 0; // 鼠标进入延迟时间,默认为 0(立即响应)
|
||||
|
||||
/**
|
||||
* 监测鼠标是否在元素内部,如果在元素内部则返回 true,否则返回 false
|
||||
* @param refElement 所有需要检测的元素。如果提供了一个数组,那么鼠标在任何一个元素内部都会返回 true
|
||||
* @param delay 延迟更新状态的时间
|
||||
* @param refElement 所有需要检测的元素。支持单个元素、元素数组或响应式引用的元素数组。如果鼠标在任何一个元素内部都会返回 true
|
||||
* @param delay 延迟更新状态的时间,可以是数字或包含进入/离开延迟的配置对象
|
||||
* @returns 返回一个数组,第一个元素是一个 ref,表示鼠标是否在元素内部,第二个元素是一个控制器,可以通过 enable 和 disable 方法来控制监听器的启用和禁用
|
||||
*/
|
||||
export function useHoverToggle(
|
||||
refElement: Arrayable<MaybeElementRef>,
|
||||
delay: (() => number) | number = 500,
|
||||
refElement: Arrayable<MaybeElementRef> | Ref<HTMLElement[] | null>,
|
||||
delay: (() => number) | HoverDelayOptions | number = DEFAULT_LEAVE_DELAY,
|
||||
) {
|
||||
const isOutsides: Array<Ref<boolean>> = [];
|
||||
const value = ref(false);
|
||||
const timer = ref<ReturnType<typeof setTimeout> | undefined>();
|
||||
const refs = Array.isArray(refElement) ? refElement : [refElement];
|
||||
refs.forEach((refEle) => {
|
||||
const listener = useMouseInElement(refEle, { handleOutside: true });
|
||||
isOutsides.push(listener.isOutside);
|
||||
});
|
||||
const isOutsideAll = computed(() => isOutsides.every((v) => v.value));
|
||||
// 兼容旧版本API
|
||||
const normalizedOptions: HoverDelayOptions =
|
||||
typeof delay === 'number' || isFunction(delay)
|
||||
? { enterDelay: DEFAULT_ENTER_DELAY, leaveDelay: delay }
|
||||
: {
|
||||
enterDelay: DEFAULT_ENTER_DELAY,
|
||||
leaveDelay: DEFAULT_LEAVE_DELAY,
|
||||
...delay,
|
||||
};
|
||||
|
||||
function setValueDelay(val: boolean) {
|
||||
timer.value && clearTimeout(timer.value);
|
||||
timer.value = setTimeout(
|
||||
() => {
|
||||
value.value = val;
|
||||
timer.value = undefined;
|
||||
},
|
||||
isFunction(delay) ? delay() : delay,
|
||||
);
|
||||
const value = ref(false);
|
||||
const enterTimer = ref<ReturnType<typeof setTimeout> | undefined>();
|
||||
const leaveTimer = ref<ReturnType<typeof setTimeout> | undefined>();
|
||||
const hoverScopes = ref<ReturnType<typeof effectScope>[]>([]);
|
||||
|
||||
// 使用计算属性包装 refElement,使其响应式变化
|
||||
const refs = computed(() => {
|
||||
const raw = unref(refElement);
|
||||
if (raw === null) return [];
|
||||
return Array.isArray(raw) ? raw : [raw];
|
||||
});
|
||||
// 存储所有 hover 状态
|
||||
const isHovers = ref<Array<Ref<boolean>>>([]);
|
||||
|
||||
// 更新 hover 监听的函数
|
||||
function updateHovers() {
|
||||
// 停止并清理之前的作用域
|
||||
hoverScopes.value.forEach((scope) => scope.stop());
|
||||
hoverScopes.value = [];
|
||||
|
||||
isHovers.value = refs.value.map((refEle) => {
|
||||
if (!refEle) {
|
||||
return ref(false);
|
||||
}
|
||||
const eleRef = computed(() => {
|
||||
const ele = unref(refEle);
|
||||
return ele instanceof Element ? ele : (ele?.$el as Element);
|
||||
});
|
||||
|
||||
// 为每个元素创建独立的作用域
|
||||
const scope = effectScope();
|
||||
const hoverRef = scope.run(() => useElementHover(eleRef)) || ref(false);
|
||||
hoverScopes.value.push(scope);
|
||||
|
||||
return hoverRef;
|
||||
});
|
||||
}
|
||||
|
||||
const watcher = watch(
|
||||
// 监听元素数量变化,避免过度执行
|
||||
const elementsCount = computed(() => {
|
||||
const raw = unref(refElement);
|
||||
if (raw === null) return 0;
|
||||
return Array.isArray(raw) ? raw.length : 1;
|
||||
});
|
||||
|
||||
// 初始设置
|
||||
updateHovers();
|
||||
|
||||
// 只在元素数量变化时重新设置监听器
|
||||
const stopWatcher = watch(elementsCount, updateHovers, { deep: false });
|
||||
|
||||
const isOutsideAll = computed(() => isHovers.value.every((v) => !v.value));
|
||||
|
||||
function clearTimers() {
|
||||
if (enterTimer.value) {
|
||||
clearTimeout(enterTimer.value);
|
||||
enterTimer.value = undefined;
|
||||
}
|
||||
if (leaveTimer.value) {
|
||||
clearTimeout(leaveTimer.value);
|
||||
leaveTimer.value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function setValueDelay(val: boolean) {
|
||||
clearTimers();
|
||||
|
||||
if (val) {
|
||||
// 鼠标进入
|
||||
const enterDelay = normalizedOptions.enterDelay ?? DEFAULT_ENTER_DELAY;
|
||||
const delayTime = isFunction(enterDelay) ? enterDelay() : enterDelay;
|
||||
|
||||
if (delayTime <= 0) {
|
||||
value.value = true;
|
||||
} else {
|
||||
enterTimer.value = setTimeout(() => {
|
||||
value.value = true;
|
||||
enterTimer.value = undefined;
|
||||
}, delayTime);
|
||||
}
|
||||
} else {
|
||||
// 鼠标离开
|
||||
const leaveDelay = normalizedOptions.leaveDelay ?? DEFAULT_LEAVE_DELAY;
|
||||
const delayTime = isFunction(leaveDelay) ? leaveDelay() : leaveDelay;
|
||||
|
||||
if (delayTime <= 0) {
|
||||
value.value = false;
|
||||
} else {
|
||||
leaveTimer.value = setTimeout(() => {
|
||||
value.value = false;
|
||||
leaveTimer.value = undefined;
|
||||
}, delayTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hoverWatcher = watch(
|
||||
isOutsideAll,
|
||||
(val) => {
|
||||
setValueDelay(!val);
|
||||
@@ -48,15 +144,19 @@ export function useHoverToggle(
|
||||
|
||||
const controller = {
|
||||
enable() {
|
||||
watcher.resume();
|
||||
hoverWatcher.resume();
|
||||
},
|
||||
disable() {
|
||||
watcher.pause();
|
||||
hoverWatcher.pause();
|
||||
},
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
timer.value && clearTimeout(timer.value);
|
||||
tryOnScopeDispose(() => {
|
||||
clearTimers();
|
||||
// 停止监听器
|
||||
stopWatcher();
|
||||
// 停止所有剩余的作用域
|
||||
hoverScopes.value.forEach((scope) => scope.stop());
|
||||
});
|
||||
|
||||
return [value, controller] as [typeof value, typeof controller];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Ref } from 'vue';
|
||||
import { computed, ref, unref } from 'vue';
|
||||
|
||||
import { computed, ref, unref, watch } from 'vue';
|
||||
|
||||
/**
|
||||
* Paginates an array of items
|
||||
@@ -21,7 +22,11 @@ function pagination<T = any>(list: T[], pageNo: number, pageSize: number): T[] {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function usePagination<T = any>(list: Ref<T[]>, pageSize: number) {
|
||||
export function usePagination<T = any>(
|
||||
list: Ref<T[]>,
|
||||
pageSize: number,
|
||||
totalChangeToFirstPage = true,
|
||||
) {
|
||||
const currentPage = ref(1);
|
||||
const pageSizeRef = ref(pageSize);
|
||||
|
||||
@@ -37,11 +42,21 @@ export function usePagination<T = any>(list: Ref<T[]>, pageSize: number) {
|
||||
return unref(list).length;
|
||||
});
|
||||
|
||||
if (totalChangeToFirstPage) {
|
||||
watch(total, () => {
|
||||
setCurrentPage(1);
|
||||
});
|
||||
}
|
||||
|
||||
function setCurrentPage(page: number) {
|
||||
if (page < 1 || page > unref(totalPages)) {
|
||||
throw new Error('Invalid page number');
|
||||
if (page === 1 && unref(totalPages) === 0) {
|
||||
currentPage.value = 1;
|
||||
} else {
|
||||
if (page < 1 || page > unref(totalPages)) {
|
||||
throw new Error('Invalid page number');
|
||||
}
|
||||
currentPage.value = page;
|
||||
}
|
||||
currentPage.value = page;
|
||||
}
|
||||
|
||||
function setPageSize(pageSize: number) {
|
||||
@@ -53,5 +68,5 @@ export function usePagination<T = any>(list: Ref<T[]>, pageSize: number) {
|
||||
currentPage.value = 1;
|
||||
}
|
||||
|
||||
return { setCurrentPage, total, setPageSize, paginationList };
|
||||
return { setCurrentPage, total, setPageSize, paginationList, currentPage };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { type RouteLocationNormalized, useRoute, useRouter } from 'vue-router';
|
||||
import type { ComputedRef } from 'vue';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { useTabbarStore } from '@vben/stores';
|
||||
|
||||
@@ -39,19 +42,36 @@ export function useTabs() {
|
||||
await tabbarStore.toggleTabPin(tab || route);
|
||||
}
|
||||
|
||||
async function refreshTab() {
|
||||
await tabbarStore.refresh(router);
|
||||
async function refreshTab(name?: string) {
|
||||
await tabbarStore.refresh(name || router);
|
||||
}
|
||||
|
||||
async function openTabInNewWindow(tab?: RouteLocationNormalized) {
|
||||
await tabbarStore.openTabInNewWindow(tab || route);
|
||||
await tabbarStore.openTabInNewWindow(tab || route, router);
|
||||
}
|
||||
|
||||
async function closeTabByKey(key: string) {
|
||||
await tabbarStore.closeTabByKey(key, router);
|
||||
}
|
||||
|
||||
async function setTabTitle(title: string) {
|
||||
/**
|
||||
* 设置当前标签页的标题
|
||||
*
|
||||
* @description 支持设置静态标题字符串或动态计算标题
|
||||
* @description 动态标题会在每次渲染时重新计算,适用于多语言或状态相关的标题
|
||||
*
|
||||
* @param title - 标题内容
|
||||
* - 静态标题: 直接传入字符串
|
||||
* - 动态标题: 传入 ComputedRef
|
||||
*
|
||||
* @example
|
||||
* // 静态标题
|
||||
* setTabTitle('标签页')
|
||||
*
|
||||
* // 动态标题(多语言)
|
||||
* setTabTitle(computed(() => t('page.title')))
|
||||
*/
|
||||
async function setTabTitle(title: ComputedRef<string> | string) {
|
||||
tabbarStore.setUpdateTime();
|
||||
await tabbarStore.setTabTitle(route, title);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ import type { Watermark, WatermarkOptions } from 'watermark-js-plus';
|
||||
|
||||
import { nextTick, onUnmounted, readonly, ref } from 'vue';
|
||||
|
||||
import { updatePreferences } from '@vben/preferences';
|
||||
|
||||
const watermark = ref<Watermark>();
|
||||
const unmountedHooked = ref<boolean>(false);
|
||||
const cachedOptions = ref<Partial<WatermarkOptions>>({
|
||||
@@ -50,7 +48,6 @@ export function useWatermark() {
|
||||
// 这里填写水印颜色
|
||||
cachedOptions.value.globalAlpha = 0.15;
|
||||
watermark.value = new Watermark(cachedOptions.value);
|
||||
updatePreferences({ app: { watermark: true } });
|
||||
await watermark.value?.create();
|
||||
}
|
||||
|
||||
@@ -71,7 +68,6 @@ export function useWatermark() {
|
||||
watermark.value.destroy();
|
||||
watermark.value = undefined;
|
||||
}
|
||||
updatePreferences({ app: { watermark: false } });
|
||||
}
|
||||
|
||||
// 只在第一次调用时注册卸载钩子,防止重复注册以致于在路由切换时销毁了水印
|
||||
|
||||
Reference in New Issue
Block a user