Compare commits
14 Commits
09edc936b6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10d37978dd | ||
|
|
58406d2d54 | ||
|
|
aca60e107d | ||
|
|
ea43542878 | ||
|
|
f3fbbcc4ab | ||
|
|
ceb48abc16 | ||
|
|
f18aa76676 | ||
|
|
1216e08bb9 | ||
|
|
f2cba9f5c4 | ||
|
|
03a6d711c3 | ||
|
|
aa8707cf6f | ||
|
|
dc3228d313 | ||
|
|
4c3d3e0489 | ||
|
|
91a97ffb93 |
@@ -5,41 +5,27 @@ module.exports = {
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'prettier/prettier',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'plugin:json/recommended',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react', '@typescript-eslint', 'simple-import-sort'],
|
||||
plugins: ['react', '@typescript-eslint'],
|
||||
rules: {
|
||||
'prettier/prettier': ['error', { singleQuote: true }],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'off',
|
||||
{ argsIgnorePattern: ['^_', 'tocoServices'] },
|
||||
],
|
||||
'simple-import-sort/exports': 'error',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
"@typescript-eslint/ban-types": 'off',
|
||||
'react-hooks/exhaustive-deps': 'error',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'react/no-children-prop': 'off',
|
||||
'import/no-anonymous-default-export': 'off',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.js'],
|
||||
rules: {
|
||||
'react/no-children-prop': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.json'],
|
||||
rules: {
|
||||
'prettier/prettier': ['error', { singleQuote: false }],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.config.js', '**/setupProxy.js', 'script/**/*.js'],
|
||||
files: ['**/*.config.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
},
|
||||
@@ -47,6 +33,6 @@ module.exports = {
|
||||
],
|
||||
globals: {
|
||||
tocoRefs: true,
|
||||
tocoModals: true,
|
||||
tocoOverlays: true,
|
||||
},
|
||||
};
|
||||
0
template/.gitignore → .gitignore
vendored
@@ -55,7 +55,7 @@
|
||||
navigate:页面跳转方法,用法:navigate('/path')
|
||||
tocoServices:页面的请求
|
||||
tocoRefs:页面上所有带 id 组件的 refs
|
||||
tocoModals:页面上所有带 id 弹窗的 refs
|
||||
tocoOverlays:页面上所有带 id 弹窗/抽屉等浮层的 refs
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
@@ -10,37 +10,31 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.5.1",
|
||||
"@df/toco-ui": "{{uiVersion}}",
|
||||
"@toco-design/components": "{{uiVersion}}",
|
||||
"@reduxjs/toolkit": "^1.9.7",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"axios": "^1.7.2",
|
||||
"classnames": "^2.5.1",
|
||||
"path-to-regexp": "^8.2.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-intl": "^6.6.2",
|
||||
"react-intl": "~6.6.2",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.22.2",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
"react-router-dom": "^6.22.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^17.0.45",
|
||||
"@types/react": "^18.2.60",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"craco-alias": "^3.0.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-json": "^3.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-organize-imports": "^3.2.4",
|
||||
"prettier-plugin-packagejson": "^2.4.12",
|
||||
"typescript": "^5"
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^5",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
@@ -1,5 +1,5 @@
|
||||
import { customValueTypeMap } from '@/components';
|
||||
import { ConfigProvider, App as TocoApp, theme } from '@df/toco-ui';
|
||||
import { customValueTypeMap } from '@components';
|
||||
import { ConfigProvider, App as TocoApp, theme as antTheme, ProConfigProvider } from '@toco-design/components';
|
||||
import { Router } from '@remix-run/router';
|
||||
import enUS from 'antd/locale/en_US';
|
||||
import zhCN from 'antd/locale/zh_CN';
|
||||
@@ -10,14 +10,13 @@ import { RouterProvider } from 'react-router-dom';
|
||||
import { Locale, Theme } from './enum';
|
||||
import { useAppSelector } from './hooks';
|
||||
import * as langs from './langs';
|
||||
import globalMessage from './message';
|
||||
import { store } from './store';
|
||||
import appTheme from './theme';
|
||||
|
||||
const { darkAlgorithm } = theme;
|
||||
const { darkAlgorithm } = antTheme;
|
||||
|
||||
const AppInternal: React.FC<{ router: Router }> = (props) => {
|
||||
const { router } = props;
|
||||
const { message } = TocoApp.useApp();
|
||||
|
||||
const locale = useAppSelector((state) => state.common.locale);
|
||||
const theme = useAppSelector((state) => state.common.theme);
|
||||
@@ -33,21 +32,29 @@ const AppInternal: React.FC<{ router: Router }> = (props) => {
|
||||
};
|
||||
|
||||
const themeConfig = useMemo(() => {
|
||||
const algorithmArr = Array.isArray(appTheme.algorithm) ? appTheme.algorithm : appTheme.algorithm ? [appTheme.algorithm] : [];
|
||||
if (theme === Theme.DARK) {
|
||||
if (!algorithmArr.includes(antTheme.darkAlgorithm)) {
|
||||
algorithmArr.push(antTheme.darkAlgorithm);
|
||||
}
|
||||
} else {
|
||||
const index = algorithmArr.indexOf(antTheme.darkAlgorithm);
|
||||
if (index > -1) {
|
||||
algorithmArr.splice(index, 1);
|
||||
}
|
||||
}
|
||||
return {
|
||||
algorithm: theme === Theme.DARK ? [darkAlgorithm] : [],
|
||||
cssVar: true,
|
||||
algorithm: algorithmArr,
|
||||
cssVar: appTheme.cssVar ?? true,
|
||||
token: primaryColor
|
||||
? {
|
||||
...appTheme.token,
|
||||
colorPrimary: primaryColor,
|
||||
}
|
||||
: undefined,
|
||||
: appTheme.token,
|
||||
};
|
||||
}, [primaryColor, theme]);
|
||||
|
||||
useEffect(() => {
|
||||
globalMessage.setMessage(message);
|
||||
}, [message]);
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute(
|
||||
'data-color-scheme',
|
||||
@@ -60,9 +67,12 @@ const AppInternal: React.FC<{ router: Router }> = (props) => {
|
||||
<ConfigProvider
|
||||
locale={antdLocaleMap[locale]}
|
||||
theme={themeConfig}
|
||||
valueTypeMap={customValueTypeMap}
|
||||
>
|
||||
<ProConfigProvider valueTypeMap={customValueTypeMap}>
|
||||
<TocoApp style={{ height: '100%' }}>
|
||||
<RouterProvider router={router} />
|
||||
</TocoApp>
|
||||
</ProConfigProvider>
|
||||
</ConfigProvider>
|
||||
</IntlProvider>
|
||||
);
|
||||
@@ -71,9 +81,7 @@ const AppInternal: React.FC<{ router: Router }> = (props) => {
|
||||
const App: React.FC<{ router: Router }> = (props) => {
|
||||
return (
|
||||
<StoreProvider store={store}>
|
||||
<TocoApp style={{ height: '100%' }}>
|
||||
<AppInternal {...props} />
|
||||
</TocoApp>
|
||||
</StoreProvider>
|
||||
);
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
* 带主题等上下文的、可全局使用的message api
|
||||
* toco-ui/antd 组件库的message无法直接全局使用,主题上下文会丢失
|
||||
*/
|
||||
import { message as tocoMessage } from '@df/toco-ui';
|
||||
import { message as tocoMessage } from '@toco-design/components';
|
||||
|
||||
type Message = ReturnType<typeof tocoMessage.useMessage>[0];
|
||||
|
||||
7
src/app/theme.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { ThemeConfig, theme } from '@toco-design/components';
|
||||
|
||||
const appTheme: ThemeConfig = {
|
||||
|
||||
};
|
||||
|
||||
export default appTheme;
|
||||
2
template/src/global.d.ts → src/global.d.ts
vendored
@@ -3,7 +3,7 @@
|
||||
|
||||
declare global {
|
||||
var tocoRefs: Record<string, any>;
|
||||
var tocoModals: Record<string, any>;
|
||||
var tocoOverlays: Record<string, any>;
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -1,5 +1,5 @@
|
||||
import '@/style/index.css';
|
||||
import { tocoGlobal } from '@df/toco-ui';
|
||||
import { tocoGlobal } from '@toco-design/components';
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './app';
|
||||
@@ -22,4 +22,4 @@ reportWebVitals();
|
||||
|
||||
// global variables
|
||||
globalThis.tocoRefs = tocoGlobal.getRefs();
|
||||
globalThis.tocoModals = tocoGlobal.getModals();
|
||||
globalThis.tocoOverlays = tocoGlobal.getOverlays();
|
||||
@@ -14,6 +14,7 @@
|
||||
}
|
||||
height: var(--header-height);
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Theme } from '@/app/enum';
|
||||
import { useAppSelector } from '@/app/hooks';
|
||||
import globalMessage from '@/app/message';
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { Button, Image, Popover } from '@df/toco-ui';
|
||||
import { App, Button, Image, Popover } from '@toco-design/components';
|
||||
import classNames from 'classnames';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styles from './index.module.css';
|
||||
import { HeaderOnly, HeaderSider, SiderOnly } from './layouts';
|
||||
import Settings from './settings';
|
||||
@@ -22,6 +24,12 @@ type LayoutProps = {
|
||||
const Layout: React.FC<LayoutProps> = (props) => {
|
||||
const { type: typeProp } = props;
|
||||
const theme = useAppSelector((state) => state.common.theme);
|
||||
const navigate = useNavigate();
|
||||
const { message } = App.useApp();
|
||||
|
||||
useEffect(() => {
|
||||
globalMessage.setMessage(message);
|
||||
}, [message]);
|
||||
|
||||
const Component = useMemo(() => {
|
||||
const type = typeProp ?? LayoutType.Default;
|
||||
@@ -37,6 +45,10 @@ const Layout: React.FC<LayoutProps> = (props) => {
|
||||
return HeaderSider;
|
||||
}, [typeProp]);
|
||||
|
||||
const gotoHome = useCallback(() => {
|
||||
navigate('/');
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<SuspenseLayout>
|
||||
<Component
|
||||
@@ -45,6 +57,7 @@ const Layout: React.FC<LayoutProps> = (props) => {
|
||||
logo={
|
||||
<div
|
||||
className={classNames(styles.logo, { dark: theme === Theme.DARK })}
|
||||
onClick={gotoHome}
|
||||
>
|
||||
<Image
|
||||
className={styles.icon}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Theme } from '@/app/enum';
|
||||
import { routerConfig } from '@/router';
|
||||
import { Layout, Menu, MenuProps } from '@df/toco-ui';
|
||||
import { SelectInfo } from 'rc-menu/lib/interface';
|
||||
import { Layout, Menu, MenuProps } from '@toco-design/components';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Outlet, useMatches, useNavigate } from 'react-router-dom';
|
||||
import { LayoutProps } from '.';
|
||||
@@ -26,7 +25,8 @@ const HeaderOnly: React.FC<LayoutProps> = (props) => {
|
||||
}, []);
|
||||
|
||||
const onKeyChange = useCallback(
|
||||
(info: SelectInfo) => {
|
||||
(...args: Parameters<Required<MenuProps>['onSelect']>) => {
|
||||
const info = args[0];
|
||||
const keys = info.keyPath;
|
||||
setSelectedKeys(info.keyPath);
|
||||
navigate(keys.reverse().join('/'));
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Theme } from '@/app/enum';
|
||||
import { routerConfig } from '@/router';
|
||||
import { Layout, Menu, MenuProps } from '@df/toco-ui';
|
||||
import { SelectInfo } from 'rc-menu/lib/interface';
|
||||
import { Layout, Menu, MenuProps } from '@toco-design/components';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Outlet, useMatches, useNavigate } from 'react-router-dom';
|
||||
import { LayoutProps } from '.';
|
||||
@@ -37,7 +36,8 @@ const HeaderSider: React.FC<LayoutProps> = (props) => {
|
||||
}, []);
|
||||
|
||||
const onHeaderKeyChange = useCallback(
|
||||
(info: SelectInfo) => {
|
||||
(...args: Parameters<Required<MenuProps>['onSelect']>) => {
|
||||
const info = args[0];
|
||||
setHeaderKey(info.key);
|
||||
navigate(info.key);
|
||||
},
|
||||
@@ -50,7 +50,8 @@ const HeaderSider: React.FC<LayoutProps> = (props) => {
|
||||
}, [headerKey]);
|
||||
|
||||
const onSiderKeyChange = useCallback(
|
||||
(info: SelectInfo) => {
|
||||
(...args: Parameters<Required<MenuProps>['onSelect']>) => {
|
||||
const info = args[0];
|
||||
const keys = info.keyPath;
|
||||
setSiderSelectedKeys(info.keyPath);
|
||||
const parts = [headerKey, ...keys];
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Theme } from '@/app/enum';
|
||||
import { routerConfig } from '@/router';
|
||||
import { Layout, Menu, MenuProps } from '@df/toco-ui';
|
||||
import { SelectInfo } from 'rc-menu/lib/interface';
|
||||
import { Layout, Menu, MenuProps } from '@toco-design/components';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Outlet, useMatches, useNavigate } from 'react-router-dom';
|
||||
import { LayoutProps } from '.';
|
||||
@@ -34,7 +33,8 @@ const SiderOnly: React.FC<LayoutProps> = (props) => {
|
||||
}, []);
|
||||
|
||||
const onKeyChange = useCallback(
|
||||
(info: SelectInfo) => {
|
||||
(...args: Parameters<Required<MenuProps>['onSelect']>) => {
|
||||
const info = args[0];
|
||||
const keys = info.keyPath;
|
||||
setSelectedKeys(info.keyPath);
|
||||
navigate(keys.reverse().join('/'));
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RootRoute } from '@/router';
|
||||
import { MenuProps } from '@df/toco-ui';
|
||||
import { MenuProps } from '@toco-design/components';
|
||||
import { parse } from 'path-to-regexp';
|
||||
import { UIMatch } from 'react-router-dom';
|
||||
|
||||
@@ -28,7 +28,7 @@ export const parseMenuItems = (
|
||||
return {
|
||||
key: removeLeadingSlashes(item.path),
|
||||
label: item.menu.title,
|
||||
children: children && children.length > 0 ? children : 0,
|
||||
children: children && children.length > 0 ? children : undefined,
|
||||
};
|
||||
})
|
||||
.filter((p) => !!p);
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '@/app/common';
|
||||
import { Locale, Theme } from '@/app/enum';
|
||||
import { useAppDispatch, useAppSelector } from '@/app/hooks';
|
||||
import { Radio, RadioChangeEvent, Tooltip } from '@df/toco-ui';
|
||||
import { Radio, RadioChangeEvent, Tooltip } from '@toco-design/components';
|
||||
import React, { useCallback } from 'react';
|
||||
import { FormattedMessage, useIntl } from 'react-intl';
|
||||
import ColorContainer, { changeColor } from './color';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Spin as AntSpin, SpinProps } from '@df/toco-ui';
|
||||
import { Spin as AntSpin, SpinProps } from '@toco-design/components';
|
||||
const Spin = (props: SpinProps) => {
|
||||
const { size = 'default' } = props;
|
||||
const sizeMap = {
|
||||
@@ -17,7 +17,11 @@
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
"@/*": ["./*"],
|
||||
"@components": ["./components"],
|
||||
"@components/*": ["./components/*"],
|
||||
"@pages": ["./pages"],
|
||||
"@pages/*": ["./pages/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
@@ -1,4 +0,0 @@
|
||||
projectName: toco
|
||||
uiVersion: 0.1.36
|
||||
iconFontSrc: ""
|
||||
layout: Default
|
||||