mirror of
https://github.com/zuiidea/antd-admin
synced 2026-10-30 05:13:43 +00:00
chore: scaffold apps/basic from with-lingui
Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import { theme } from "antd";
|
||||
import type { ConfigProviderProps, ThemeConfig } from "antd";
|
||||
|
||||
/**
|
||||
* Common theme token builders
|
||||
* Reduces duplication between light and dark theme definitions
|
||||
*/
|
||||
|
||||
export const SHARED_DESIGN_TOKENS = {
|
||||
fontFamily:
|
||||
"ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif",
|
||||
|
||||
borderRadius: 8,
|
||||
borderRadiusSM: 6,
|
||||
borderRadiusLG: 12,
|
||||
} as const;
|
||||
|
||||
export const CONTROL_COMPONENTS = {
|
||||
Button: {
|
||||
primaryShadow: "none",
|
||||
defaultShadow: "none",
|
||||
dangerShadow: "none",
|
||||
},
|
||||
Input: {
|
||||
activeShadow: "none",
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Calculate table row selected color based on theme algorithm
|
||||
*/
|
||||
export function buildTableTokens(cfg: ThemeConfig) {
|
||||
const rowSelectedBg = theme.getDesignToken(cfg).colorFillAlter;
|
||||
return {
|
||||
rowSelectedBg,
|
||||
rowSelectedHoverBg: rowSelectedBg,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build menu tokens for light theme
|
||||
*/
|
||||
export const MENU_LIGHT = {
|
||||
itemSelectedBg: "rgba(0, 0, 0, 0.06)",
|
||||
itemSelectedColor: "rgba(0, 0, 0, 0.88)",
|
||||
subMenuItemSelectedColor: "rgba(0, 0, 0, 0.88)",
|
||||
itemHoverBg: "rgba(0, 0, 0, 0.04)",
|
||||
itemHoverColor: "rgba(0, 0, 0, 0.88)",
|
||||
itemActiveBg: "rgba(0, 0, 0, 0.08)",
|
||||
itemBorderRadius: SHARED_DESIGN_TOKENS.borderRadiusSM,
|
||||
itemMarginInline: 8,
|
||||
horizontalItemSelectedColor: "rgba(0, 0, 0, 0.88)",
|
||||
horizontalItemHoverColor: "rgba(0, 0, 0, 0.88)",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Build menu tokens for dark theme
|
||||
*/
|
||||
export const MENU_DARK = {
|
||||
itemSelectedBg: "rgba(255, 255, 255, 0.08)",
|
||||
itemSelectedColor: "rgba(255, 255, 255, 0.85)",
|
||||
subMenuItemSelectedColor: "rgba(255, 255, 255, 0.85)",
|
||||
itemHoverBg: "rgba(255, 255, 255, 0.06)",
|
||||
itemHoverColor: "rgba(255, 255, 255, 0.85)",
|
||||
itemActiveBg: "rgba(255, 255, 255, 0.1)",
|
||||
itemBorderRadius: SHARED_DESIGN_TOKENS.borderRadiusSM,
|
||||
itemMarginInline: 8,
|
||||
horizontalItemSelectedColor: "rgba(255, 255, 255, 0.85)",
|
||||
horizontalItemHoverColor: "rgba(255, 255, 255, 0.85)",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Build light theme config
|
||||
*/
|
||||
export function buildLightThemeConfig(): ConfigProviderProps {
|
||||
const lightSeed: ThemeConfig["token"] = {
|
||||
colorBgLayout: "#ffffff",
|
||||
...SHARED_DESIGN_TOKENS,
|
||||
};
|
||||
|
||||
return {
|
||||
theme: {
|
||||
algorithm: theme.defaultAlgorithm,
|
||||
token: lightSeed,
|
||||
components: {
|
||||
...CONTROL_COMPONENTS,
|
||||
Table: buildTableTokens({
|
||||
algorithm: theme.defaultAlgorithm,
|
||||
token: lightSeed,
|
||||
}),
|
||||
Menu: MENU_LIGHT,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build dark theme config
|
||||
*/
|
||||
export function buildDarkThemeConfig(): ConfigProviderProps {
|
||||
const darkSeed: ThemeConfig["token"] = {
|
||||
...SHARED_DESIGN_TOKENS,
|
||||
};
|
||||
|
||||
return {
|
||||
theme: {
|
||||
algorithm: theme.darkAlgorithm,
|
||||
token: darkSeed,
|
||||
components: {
|
||||
...CONTROL_COMPONENTS,
|
||||
Table: buildTableTokens({
|
||||
algorithm: theme.darkAlgorithm,
|
||||
token: darkSeed,
|
||||
}),
|
||||
Menu: MENU_DARK,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useMemo } from "react";
|
||||
import type { ConfigProviderProps } from "antd";
|
||||
import { buildLightThemeConfig, buildDarkThemeConfig } from "./tokenBuilders";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
|
||||
/**
|
||||
* Theme selection for ConfigProvider: light/dark share radius, font, control tweaks.
|
||||
* @see https://ant.design/docs/react/customize-theme
|
||||
*/
|
||||
export function useAppTheme(): ConfigProviderProps {
|
||||
const darkMode = useSettingsStore((s) => s.darkMode);
|
||||
|
||||
return useMemo(() => (darkMode ? buildDarkThemeConfig() : buildLightThemeConfig()), [darkMode]);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
export function hasPermission(point: string): boolean {
|
||||
return useAuthStore.getState().hasPermission(point);
|
||||
}
|
||||
|
||||
export function usePermission(point: string): boolean {
|
||||
const user = useAuthStore((s) => s.user);
|
||||
return user?.permissions.includes(point) ?? false;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
type MutationLifecycle<TValues> = {
|
||||
onMutate?: (values: TValues) => void;
|
||||
onSuccess?: (values: TValues) => void;
|
||||
onError?: (values: TValues) => void;
|
||||
};
|
||||
|
||||
type UseResourceCRUDOptions<TListData, TCreateValues, TUpdateValues> = {
|
||||
queryKey: readonly unknown[];
|
||||
invalidateKey: readonly unknown[];
|
||||
queryFn: () => Promise<unknown>;
|
||||
select: (raw: unknown) => TListData;
|
||||
createFn: (values: TCreateValues) => Promise<unknown>;
|
||||
updateFn: (values: TUpdateValues) => Promise<unknown>;
|
||||
deleteFn: (id: string) => Promise<unknown>;
|
||||
createLifecycle?: MutationLifecycle<TCreateValues>;
|
||||
updateLifecycle?: MutationLifecycle<TUpdateValues>;
|
||||
deleteLifecycle?: MutationLifecycle<string>;
|
||||
};
|
||||
|
||||
export function useResourceCRUD<TListData, TCreateValues, TUpdateValues>(
|
||||
options: UseResourceCRUDOptions<TListData, TCreateValues, TUpdateValues>,
|
||||
) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: options.queryKey,
|
||||
queryFn: options.queryFn,
|
||||
select: options.select,
|
||||
});
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: options.createFn,
|
||||
onMutate: (values) => {
|
||||
options.createLifecycle?.onMutate?.(values);
|
||||
},
|
||||
onSuccess: (_data, values) => {
|
||||
void queryClient.invalidateQueries({ queryKey: options.invalidateKey });
|
||||
options.createLifecycle?.onSuccess?.(values);
|
||||
},
|
||||
onError: (_error, values) => {
|
||||
options.createLifecycle?.onError?.(values);
|
||||
},
|
||||
});
|
||||
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: options.updateFn,
|
||||
onMutate: (values) => {
|
||||
options.updateLifecycle?.onMutate?.(values);
|
||||
},
|
||||
onSuccess: (_data, values) => {
|
||||
void queryClient.invalidateQueries({ queryKey: options.invalidateKey });
|
||||
options.updateLifecycle?.onSuccess?.(values);
|
||||
},
|
||||
onError: (_error, values) => {
|
||||
options.updateLifecycle?.onError?.(values);
|
||||
},
|
||||
});
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: options.deleteFn,
|
||||
onMutate: (id) => {
|
||||
options.deleteLifecycle?.onMutate?.(id);
|
||||
},
|
||||
onSuccess: (_data, id) => {
|
||||
void queryClient.invalidateQueries({ queryKey: options.invalidateKey });
|
||||
options.deleteLifecycle?.onSuccess?.(id);
|
||||
},
|
||||
onError: (_error, id) => {
|
||||
options.deleteLifecycle?.onError?.(id);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data: query.data,
|
||||
isLoading: query.isLoading,
|
||||
createMutation,
|
||||
updateMutation,
|
||||
deleteMutation,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user