├── .env.mock ├── .env.production ├── .env.test ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .huskyrc ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .stylelintignore ├── .stylelintrc.js ├── commitlint.config.js ├── index.html ├── mock └── user.ts ├── package-lock.json ├── package.json ├── readme.md ├── src ├── assets │ ├── images │ │ ├── arrow_down.png │ │ ├── back.png │ │ ├── default_goods.png │ │ ├── del.png │ │ ├── delete.png │ │ ├── edit.png │ │ ├── finsh_icon.png │ │ ├── goods_img.png │ │ ├── logo192.png │ │ ├── package_icon.png │ │ ├── scan_blue.png │ │ ├── scan_grey.png │ │ ├── search.png │ │ └── subgoods_iocn.png │ └── style │ │ ├── color │ │ ├── bezierEasing.less │ │ ├── colorPalette.less │ │ ├── colors.less │ │ └── tinyColor.less │ │ ├── common.less │ │ ├── default.less │ │ └── index.less ├── components │ ├── HeaderDropdown │ │ ├── index.module.less │ │ └── index.tsx │ ├── Image │ │ └── index.tsx │ ├── NoticeIcon │ │ ├── NoticeIcon.tsx │ │ ├── NoticeList.module.less │ │ ├── NoticeList.tsx │ │ └── index.module.less │ ├── index.ts │ └── style │ │ ├── bezierEasing.less │ │ ├── colorPalette.less │ │ ├── colors.less │ │ ├── default.less │ │ └── tinyColor.less ├── http │ ├── fetch.ts │ ├── interceptors │ │ ├── auth-intereceptor.ts │ │ ├── error-response-interceptor.ts │ │ ├── response-log-interceptor.ts │ │ ├── token-interceptor.ts │ │ └── types.ts │ └── response.ts ├── index.tsx ├── kits │ ├── checkLogin.tsx │ ├── checkPermission.tsx │ ├── hooks.ts │ ├── index.ts │ ├── lazyLoad.tsx │ ├── msg │ │ └── index.ts │ └── util │ │ ├── getEnv.ts │ │ └── regexUtil.ts ├── pages │ ├── chart │ │ ├── chart-line.tsx │ │ ├── chartLine.module.less │ │ └── index.less │ ├── error403.tsx │ ├── error404.tsx │ ├── login │ │ ├── index.tsx │ │ └── login.module.less │ ├── user │ │ ├── type.ts │ │ ├── user-add.tsx │ │ ├── user-list.tsx │ │ ├── user.module.less │ │ ├── util.ts │ │ └── web-api.ts │ └── workspace │ │ ├── components │ │ ├── breadcrumbs.tsx │ │ ├── header.less │ │ ├── header.tsx │ │ └── menu.tsx │ │ ├── index.module.less │ │ ├── index.tsx │ │ ├── slice.ts │ │ ├── type.ts │ │ └── util.ts ├── router │ ├── index.tsx │ └── routeMap.tsx └── store │ ├── index.ts │ └── rootReducer.ts ├── tsconfig.json ├── typings ├── env.d.ts └── global.d.ts └── vite.config.ts /.env.mock: -------------------------------------------------------------------------------- 1 | 2 | APP_MODE = 'development' 3 | 4 | # title 5 | APP_DOCUMENT_TITLE = 'admin' 6 | 7 | # port 8 | APP_HOST_PORT = 3000 9 | 10 | # host 11 | APP_HOST_API = http://localhost 12 | 13 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | 2 | APP_MODE = 'production' 3 | 4 | # title 5 | APP_DOCUMENT_TITLE = 'admin' 6 | 7 | # port 8 | APP_HOST_PORT = 3000 9 | 10 | # host 11 | APP_HOST_API = http://127.0.0.1 12 | 13 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | 2 | APP_MODE = 'production' 3 | 4 | # title 5 | APP_DOCUMENT_TITLE = 'admin' 6 | 7 | # port 8 | APP_HOST_PORT = 3000 9 | 10 | # host 11 | APP_HOST_API = http://127.0.0.1 12 | 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | node_modules 3 | *.md 4 | *.woff 5 | *.ttf 6 | .vscode 7 | .idea 8 | dist 9 | /public 10 | /docs 11 | .husky 12 | .local 13 | /bin 14 | .eslintrc.js 15 | .prettierrc.js 16 | /mock/* 17 | /dist/* 18 | 19 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint:recommended", 3 | "plugin:react/recommended", 4 | "plugin:@typescript-eslint/recommended", 5 | "plugin:react/jsx-runtime", 6 | "plugin:react-hooks/recommended", 7 | "prettier", 8 | "plugin:prettier/recommended"], 9 | env: { 10 | browser: true, 11 | commonjs: true, 12 | es6: true, 13 | }, 14 | parser: '@typescript-eslint/parser', 15 | parserOptions: { 16 | ecmaFeatures: { 17 | jsx: true, 18 | modules: true, 19 | }, 20 | sourceType: 'module', 21 | ecmaVersion: 6 22 | }, 23 | plugins: ["react", "@typescript-eslint", "react-hooks", "prettier"], 24 | settings: { 25 | 'import/resolver': { 26 | node: { 27 | extensions: ['.tsx', '.ts', '.js', '.json'], 28 | }, 29 | alias: [['@', './src']], 30 | }, 31 | 'react': { 32 | version: "18.2.0" 33 | } 34 | }, 35 | /* 36 | * "off" 或 0 ==> 关闭规则 37 | * "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行) 38 | * "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错) 39 | */ 40 | rules: { 41 | // eslint (http://eslint.cn/docs/rules) 42 | "quotes": [2, "single"], 43 | "semi": [1, "never"], 44 | "class-methods-use-this": "off", 45 | "import/no-named-as-default": "off", 46 | "import/no-extraneous-dependencies": "off", 47 | "import/no-unresolved": "off", 48 | "import/extensions": "off", 49 | "react/jsx-filename-extension": "off", 50 | "no-param-reassign": "off", 51 | "no-unused-expressions": "off", 52 | "jsx-a11y/alt-text": "off", 53 | "no-shadow": "off", 54 | "react/jsx-one-expression-per-line": "off", 55 | "no-use-before-define": "off", 56 | "react/button-has-type": "off", 57 | "no-undef": "off", 58 | "no-unused-vars": "off", 59 | "import/prefer-default-export": "off", 60 | "jsx-a11y/click-events-have-key-events": "off", 61 | "jsx-a11y/no-noninteractive-element-interactions": "off", 62 | "import/order": "off", 63 | "jsx-a11y/anchor-is-valid": "off", 64 | "jsx-a11y/no-static-element-interactions": "off", 65 | "react/require-default-props": "off", 66 | "react/destructuring-assignment": "off", 67 | "no-nested-ternary": "off", 68 | "no-console": "off", 69 | "no-sequences": "off", 70 | "no-multi-assign": "off", 71 | "func-names": "off", 72 | "prefer-destructuring": "off", 73 | "consistent-return": "off", 74 | "no-useless-escape": "off", 75 | "no-new-func": "off", 76 | "global-require": "off", 77 | "import/no-dynamic-require": "off", 78 | "react/display-name": "off", 79 | "react/prop-types": "off", 80 | 81 | // typeScript (https://typescript-eslint.io/rules) 82 | "@typescript-eslint/no-unused-vars": "off", // 禁止定义未使用的变量 83 | "@typescript-eslint/no-inferrable-types": "off", // 可以轻松推断的显式类型可能会增加不必要的冗长 84 | "@typescript-eslint/no-namespace": "off", // 禁止使用自定义 TypeScript 模块和命名空间。 85 | "@typescript-eslint/no-explicit-any": "off", // 禁止使用 any 类型 86 | "@typescript-eslint/ban-ts-ignore": "off", // 禁止使用 @ts-ignore 87 | "@typescript-eslint/ban-types": "off", // 禁止使用特定类型 88 | "@typescript-eslint/explicit-function-return-type": "off", // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明 89 | "@typescript-eslint/no-var-requires": "off", // 不允许在 import 语句中使用 require 语句 90 | "@typescript-eslint/no-empty-function": "off", // 禁止空函数 91 | "@typescript-eslint/no-use-before-define": "off", // 禁止在变量定义之前使用它们 92 | "@typescript-eslint/ban-ts-comment": "off", // 禁止 @ts- 使用注释或要求在指令后进行描述 93 | "@typescript-eslint/no-non-null-assertion": "off", // 不允许使用后缀运算符的非空断言(!) 94 | "@typescript-eslint/explicit-module-boundary-types": "off", // 要求导出函数和类的公共类方法的显式返回和参数类型 95 | 96 | // react (https://github.com/jsx-eslint/eslint-plugin-react) 97 | "react-hooks/rules-of-hooks": "off", 98 | "react-hooks/exhaustive-deps": "off", 99 | "react/jsx-key": "off" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn lint-staged --allow-empty 5 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | # ~/.huskyrc 2 | # This loads nvm.sh and sets the correct PATH before running hook 3 | export NVM_DIR="$HOME/.nvm" 4 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.19.1 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | .local 3 | /node_modules/** 4 | 5 | **/*.svg 6 | **/*.sh 7 | 8 | /public/* 9 | 10 | src/components/* 11 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | // @see: https://www.prettier.cn 2 | 3 | module.exports = { 4 | // 超过最大值换行 5 | printWidth: 120, 6 | // 缩进字节数 7 | tabWidth: 2, 8 | // 使用制表符而不是空格缩进行 9 | useTabs: false, 10 | // 结尾不用分号(true有,false没有) 11 | semi: false, 12 | // 使用单引号(true单双引号,false双引号) 13 | singleQuote: true, 14 | // 更改引用对象属性的时间 可选值"" 15 | quoteProps: "as-needed", 16 | // 在对象,数组括号与文字之间加空格 "{ foo: bar }" 17 | bracketSpacing: true, 18 | // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none 19 | trailingComma: "none", 20 | // 在JSX中使用单引号而不是双引号 21 | jsxSingleQuote: false, 22 | // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号 23 | arrowParens: "avoid", 24 | // 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。 25 | insertPragma: false, 26 | // 指定要使用的解析器,不需要写文件开头的 @prettier 27 | requirePragma: false, 28 | // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行 29 | proseWrap: "preserve", 30 | // 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的 31 | htmlWhitespaceSensitivity: "css", 32 | // 换行符使用 lf 结尾是 可选值"" 33 | endOfLine: "lf", 34 | // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 35 | rangeStart: 0, 36 | rangeEnd: Infinity, 37 | 38 | importOrder: ["react", "react-redux", "", "^@reduxjs/(.*)$", "^@comps$", "^@store$", "^@(.*)$", "^@pages/(.*)$", "^[./]"], 39 | importOrderSeparation: false, 40 | importOrderSortSpecifiers: true 41 | }; 42 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | /public/* 3 | public/* 4 | 5 | 6 | src/components/* 7 | src/assets/style/color/* 8 | src/assets/style/default.less 9 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | // @see: https://stylelint.io 2 | 3 | module.exports = { 4 | extends: [ 5 | "stylelint-config-standard", // 配置stylelint拓展插件 6 | "stylelint-config-recess-order" // 配置stylelint css属性书写顺序插件, 7 | ], 8 | plugins: ["stylelint-less"], // 配置stylelint less拓展插件 9 | rules: { 10 | indentation: null, // 指定缩进空格 11 | "no-descending-specificity": null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器 12 | "function-url-quotes": "always", // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)" 13 | "unit-case": null, // 指定单位的大小写 "lower(全小写)"|"upper(全大写)" 14 | "color-hex-length": "long", // 指定 16 进制颜色的简写或扩写 "short(16进制简写)"|"long(16进制扩写)" 15 | "rule-empty-line-before": "never", // 要求或禁止在规则之前的空行 "always(规则之前必须始终有一个空行)"|"never(规则前绝不能有空行)"|"always-multi-line(多行规则之前必须始终有一个空行)"|"never-multi-line(多行规则之前绝不能有空行。)" 16 | "font-family-no-missing-generic-family-keyword": null, // 禁止在字体族名称列表中缺少通用字体族关键字 17 | "property-no-unknown": null, // 禁止未知的属性(true 为不允许) 18 | "no-empty-source": null, // 禁止空源码 19 | "declaration-block-trailing-semicolon": null, // 要求或不允许在声明块中使用尾随分号 string:"always(必须始终有一个尾随分号)"|"never(不得有尾随分号)" 20 | "selector-class-pattern": null, // 强制选择器类名的格式 21 | "value-no-vendor-prefix": null, // 关闭 vendor-prefix(为了解决多行省略 -webkit-box) 22 | "at-rule-no-unknown": null, 23 | "selector-pseudo-class-no-unknown": [ 24 | true, 25 | { 26 | ignorePseudoClasses: ["global", "v-deep", "deep"] 27 | } 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | const checkType = header => { 2 | header = `${header}` 3 | const enumType = ['feat', 'fix', 'style', 'chore', 'test', 'ci', 'refactor', 'revert', 'reformat', 'docs', 'perf'] 4 | const realType = header.split(':')[0] 5 | return enumType.includes(realType) 6 | } 7 | 8 | const checkSubject = header => { 9 | header = `${header}` 10 | const realSubject = header.split(':')[1] 11 | if (!realSubject) { 12 | return false 13 | } 14 | return realSubject.length > 0 15 | } 16 | 17 | /* 18 | * @Description: commit-msg提交信息格式规范 19 | * 20 | * commit-msg格式: : 21 | * - chore: 其他修改, 比如改变构建流程、或者增加依赖库、工具等 22 | * - ci: 持续集成修改 23 | * - docs: 文档修改 24 | * - feat: 新特性、新功能 25 | * - fix: 修改bug 26 | * - perf: 优化相关,比如提升性能、体验 27 | * - refactor: 代码重构 28 | * - revert: 回滚到上一个版本 29 | * - style: 代码格式修改, 注意不是 css 修改 30 | * - test: 测试用例修改 31 | */ 32 | module.exports = { 33 | extends: ['@commitlint/config-conventional'], 34 | rules: { 35 | 'type-enum-rule': [2, 'never'], 36 | 'subject-enum-rule': [2, 'never'], 37 | 'type-enum': [0, 'never'], 38 | 'type-empty': [0, 'always'], 39 | 'subject-empty': [0, 'always'] 40 | }, 41 | plugins: [ 42 | { 43 | rules: { 44 | 'type-enum-rule': ({ header }) => { 45 | return [ 46 | checkType(header), 47 | '需要包含提交类型,格式如: "feat: 开发新功能" 中的feat, ' + 48 | '可选值有: feat/fix/style/test/chore/ci/..., 类型后面紧跟英文冒号分隔主题信息' 49 | ] 50 | }, 51 | 'subject-enum-rule': ({ header }) => { 52 | return [checkSubject(header), '需要包含提交主题, 格式如: "feat: 开发新功能" 中的 开发新功能'] 53 | } 54 | } 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%- title %> 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /mock/user.ts: -------------------------------------------------------------------------------- 1 | import { MockMethod } from 'vite-plugin-mock' 2 | // import Mock from 'mockjs' 3 | 4 | export default [ 5 | { 6 | url: '/user/list', 7 | response:() => { 8 | return { 9 | "code": 0, 10 | "msg": null, 11 | "data": { 12 | "dataList|10" :[ 13 | { 14 | "id": "@id", 15 | "name": "@cname", 16 | "phone": "@pick([13913998972, 19941558406])", 17 | "deptName": "前端开发部" 18 | } 19 | ], 20 | "totalCount": 20, 21 | "totalPageCount": 2 22 | } 23 | } 24 | } 25 | }, 26 | { 27 | url: '/api/getUserInfo', 28 | method: 'get', 29 | response: () => { 30 | return { 31 | code: 200, 32 | data: { 33 | nickname: '@cname', 34 | age: '@integer(10-100)', 35 | uid: '@id', 36 | url: '@image', 37 | city: '@city', 38 | country: '@county(true)', 39 | province: '@province', 40 | mobile_phone: '@phone', 41 | email: '@email', 42 | region: '@region', 43 | menus: [ 44 | { 45 | menu_name: '一级导航', 46 | id: '@id', 47 | code: 'Nav1', 48 | children: [ 49 | { 50 | code: 'about', 51 | menu_url: 'views/about', 52 | access_permissions: '["about"]', 53 | children: [], 54 | menu_name: '测试1', 55 | id: '@id' 56 | }, 57 | { 58 | code: 'home', 59 | menu_url: 'views/home', 60 | access_permissions: '["home"]', 61 | children: [], 62 | menu_name: '测试2', 63 | id: '@id' 64 | } 65 | ] 66 | }, 67 | 68 | ] 69 | }, 70 | } 71 | }, 72 | }, 73 | ] as MockMethod[] 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-template-vite", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev:mock": "cross-env useMock=true appEnv=mock vite", 7 | "dev:test": "cross-env appEnv=test vite", 8 | "build": "tsc && cross-env appEnv=production vite build", 9 | "preview": "vite preview", 10 | "lint:eslint": "eslint --fix --ext .js,.ts,.tsx ./src", 11 | "lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,html,md}\"", 12 | "lint:stylelint": "stylelint --fix \"**/*.{less,postcss,css,scss}\" --custom-syntax postcss-less --cache --cache-location node_modules/.cache/stylelint/", 13 | "format": "npm run lint:eslint && npm run lint:stylelint && npm run lint:prettier", 14 | "prepare": "husky install", 15 | "server": "serve ./dist -p 52232" 16 | }, 17 | "dependencies": { 18 | "@ant-design/icons": "^6.0.0", 19 | "@loadable/component": "^5.16.7", 20 | "@reduxjs/toolkit": "^2.8.1", 21 | "@wangeditor-next/editor": "^5.6.34", 22 | "@wangeditor-next/editor-for-react": "^1.0.9", 23 | "ahooks": "^3.8.4", 24 | "antd": "^5.4.2", 25 | "axios": "^1.9.0", 26 | "classnames": "^2.3.2", 27 | "consola": "^3.4.2", 28 | "dayjs": "^1.11.13", 29 | "echarts": "^5.4.3", 30 | "events": "^3.3.0", 31 | "lodash": "^4.17.21", 32 | "normalize.css": "^8.0.1", 33 | "prop-types": "^15.8.1", 34 | "qs": "^6.14.0", 35 | "react": "^18.2.0", 36 | "react-dom": "^18.2.0", 37 | "react-hook-form": "^7.56.4", 38 | "react-redux": "^9.2.0", 39 | "react-router": "^7.6.0", 40 | "react-router-dom": "^7.6.0", 41 | "redux": "^5.0.1", 42 | "redux-logger": "^4.0.0" 43 | }, 44 | "devDependencies": { 45 | "@commitlint/cli": "^17.6.7", 46 | "@commitlint/config-conventional": "^17.6.7", 47 | "@trivago/prettier-plugin-sort-imports": "^4.2.0", 48 | "@types/loadable__component": "^5.13.4", 49 | "@types/lodash": "^4.14.196", 50 | "@types/react": "^18.2.18", 51 | "@types/react-dom": "^18.2.7", 52 | "@types/react-redux": "^7.1.25", 53 | "@types/redux-logger": "^3.0.9", 54 | "@typescript-eslint/eslint-plugin": "^6.2.1", 55 | "@typescript-eslint/parser": "^6.2.1", 56 | "@vitejs/plugin-legacy": "^6.1.1", 57 | "@vitejs/plugin-react": "^4.4.1", 58 | "cross-env": "^7.0.3", 59 | "eslint": "^8.46.0", 60 | "eslint-config-prettier": "^8.9.0", 61 | "eslint-plugin-prettier": "^5.0.0", 62 | "eslint-plugin-react": "^7.33.1", 63 | "eslint-plugin-react-hooks": "^4.6.0", 64 | "husky": "^8.0.3", 65 | "less": "^4.1.3", 66 | "lint-staged": "^13.2.3", 67 | "mockjs": "^1.1.0", 68 | "picocolors": "^1.0.0", 69 | "postcss": "^8.4.27", 70 | "postcss-less": "^6.0.0", 71 | "prettier": "^3.0.0", 72 | "rimraf": "^5.0.1", 73 | "stylelint": "^16.19.1", 74 | "stylelint-config-recess-order": "^6.0.0", 75 | "stylelint-config-standard": "^38.0.0", 76 | "stylelint-less": "^3.0.1", 77 | "stylelint-order": "^7.0.0", 78 | "typescript": "^5.1.6", 79 | "vite": "^6.3.5", 80 | "vite-plugin-checker": "^0.6.2", 81 | "vite-plugin-eslint": "^1.8.1", 82 | "vite-plugin-html": "^3.2.0", 83 | "vite-plugin-mock": "^3.0.0", 84 | "vite-plugin-progress": "^0.0.7", 85 | "vite-plugin-style-import": "^2.0.0" 86 | }, 87 | "husky": { 88 | "hooks": { 89 | "pre-commit": "lint-staged" 90 | } 91 | }, 92 | "lint-staged": { 93 | "src/**/*.{js,jsx,ts,tsx}": [ 94 | "eslint --fix", 95 | "prettier --write --loglevel warn" 96 | ] 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/readme.md -------------------------------------------------------------------------------- /src/assets/images/arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/arrow_down.png -------------------------------------------------------------------------------- /src/assets/images/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/back.png -------------------------------------------------------------------------------- /src/assets/images/default_goods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/default_goods.png -------------------------------------------------------------------------------- /src/assets/images/del.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/del.png -------------------------------------------------------------------------------- /src/assets/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/delete.png -------------------------------------------------------------------------------- /src/assets/images/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/edit.png -------------------------------------------------------------------------------- /src/assets/images/finsh_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/finsh_icon.png -------------------------------------------------------------------------------- /src/assets/images/goods_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/goods_img.png -------------------------------------------------------------------------------- /src/assets/images/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/logo192.png -------------------------------------------------------------------------------- /src/assets/images/package_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/package_icon.png -------------------------------------------------------------------------------- /src/assets/images/scan_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/scan_blue.png -------------------------------------------------------------------------------- /src/assets/images/scan_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/scan_grey.png -------------------------------------------------------------------------------- /src/assets/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/search.png -------------------------------------------------------------------------------- /src/assets/images/subgoods_iocn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuoguoDad/react-template-vite/db6ab1ba60aca9f815d45f01d9846e9c2094f729/src/assets/images/subgoods_iocn.png -------------------------------------------------------------------------------- /src/assets/style/color/bezierEasing.less: -------------------------------------------------------------------------------- 1 | /* stylelint-disable */ 2 | .bezierEasingMixin() { 3 | @functions: ~`(function() { 4 | var NEWTON_ITERATIONS = 4; 5 | var NEWTON_MIN_SLOPE = 0.001; 6 | var SUBDIVISION_PRECISION = 0.0000001; 7 | var SUBDIVISION_MAX_ITERATIONS = 10; 8 | 9 | var kSplineTableSize = 11; 10 | var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); 11 | 12 | var float32ArraySupported = typeof Float32Array === 'function'; 13 | 14 | function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } 15 | function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } 16 | function C (aA1) { return 3.0 * aA1; } 17 | 18 | // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. 19 | function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } 20 | 21 | // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. 22 | function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } 23 | 24 | function binarySubdivide (aX, aA, aB, mX1, mX2) { 25 | var currentX, currentT, i = 0; 26 | do { 27 | currentT = aA + (aB - aA) / 2.0; 28 | currentX = calcBezier(currentT, mX1, mX2) - aX; 29 | if (currentX > 0.0) { 30 | aB = currentT; 31 | } else { 32 | aA = currentT; 33 | } 34 | } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); 35 | return currentT; 36 | } 37 | 38 | function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { 39 | for (var i = 0; i < NEWTON_ITERATIONS; ++i) { 40 | var currentSlope = getSlope(aGuessT, mX1, mX2); 41 | if (currentSlope === 0.0) { 42 | return aGuessT; 43 | } 44 | var currentX = calcBezier(aGuessT, mX1, mX2) - aX; 45 | aGuessT -= currentX / currentSlope; 46 | } 47 | return aGuessT; 48 | } 49 | 50 | var BezierEasing = function (mX1, mY1, mX2, mY2) { 51 | if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { 52 | throw new Error('bezier x values must be in [0, 1] range'); 53 | } 54 | 55 | // Precompute samples table 56 | var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); 57 | if (mX1 !== mY1 || mX2 !== mY2) { 58 | for (var i = 0; i < kSplineTableSize; ++i) { 59 | sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); 60 | } 61 | } 62 | 63 | function getTForX (aX) { 64 | var intervalStart = 0.0; 65 | var currentSample = 1; 66 | var lastSample = kSplineTableSize - 1; 67 | 68 | for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { 69 | intervalStart += kSampleStepSize; 70 | } 71 | --currentSample; 72 | 73 | // Interpolate to provide an initial guess for t 74 | var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); 75 | var guessForT = intervalStart + dist * kSampleStepSize; 76 | 77 | var initialSlope = getSlope(guessForT, mX1, mX2); 78 | if (initialSlope >= NEWTON_MIN_SLOPE) { 79 | return newtonRaphsonIterate(aX, guessForT, mX1, mX2); 80 | } else if (initialSlope === 0.0) { 81 | return guessForT; 82 | } else { 83 | return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); 84 | } 85 | } 86 | 87 | return function BezierEasing (x) { 88 | if (mX1 === mY1 && mX2 === mY2) { 89 | return x; // linear 90 | } 91 | // Because JavaScript number are imprecise, we should guarantee the extremes are right. 92 | if (x === 0) { 93 | return 0; 94 | } 95 | if (x === 1) { 96 | return 1; 97 | } 98 | return calcBezier(getTForX(x), mY1, mY2); 99 | }; 100 | }; 101 | 102 | this.colorEasing = BezierEasing(0.26, 0.09, 0.37, 0.18); 103 | // less 3 requires a return 104 | return ''; 105 | })()`; 106 | } 107 | // It is hacky way to make this function will be compiled preferentially by less 108 | // resolve error: `ReferenceError: colorPalette is not defined` 109 | // https://github.com/ant-design/ant-motion/issues/44 110 | .bezierEasingMixin(); 111 | -------------------------------------------------------------------------------- /src/assets/style/color/colorPalette.less: -------------------------------------------------------------------------------- 1 | /* stylelint-disable no-duplicate-selectors */ 2 | @import "bezierEasing"; 3 | @import "tinyColor"; 4 | 5 | // We create a very complex algorithm which take the place of original tint/shade color system 6 | // to make sure no one can understand it 👻 7 | // and create an entire color palette magicly by inputing just a single primary color. 8 | // We are using bezier-curve easing function and some color manipulations like tint/shade/darken/spin 9 | .colorPaletteMixin() { 10 | @functions: ~`(function() { 11 | var hueStep = 2; 12 | var saturationStep = 0.16; 13 | var saturationStep2 = 0.05; 14 | var brightnessStep1 = 0.05; 15 | var brightnessStep2 = 0.15; 16 | var lightColorCount = 5; 17 | var darkColorCount = 4; 18 | 19 | var getHue = function(hsv, i, isLight) { 20 | var hue; 21 | if (hsv.h >= 60 && hsv.h <= 240) { 22 | hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i; 23 | } else { 24 | hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i; 25 | } 26 | if (hue < 0) { 27 | hue += 360; 28 | } else if (hue >= 360) { 29 | hue -= 360; 30 | } 31 | return Math.round(hue); 32 | }; 33 | var getSaturation = function(hsv, i, isLight) { 34 | // grey color don't change saturation 35 | if (hsv.h === 0 && hsv.s === 0) { 36 | return hsv.s; 37 | } 38 | var saturation; 39 | if (isLight) { 40 | saturation = hsv.s - saturationStep * i; 41 | } else if (i === darkColorCount) { 42 | saturation = hsv.s + saturationStep; 43 | } else { 44 | saturation = hsv.s + saturationStep2 * i; 45 | } 46 | if (saturation > 1) { 47 | saturation = 1; 48 | } 49 | if (isLight && i === lightColorCount && saturation > 0.1) { 50 | saturation = 0.1; 51 | } 52 | if (saturation < 0.06) { 53 | saturation = 0.06; 54 | } 55 | return Number(saturation.toFixed(2)); 56 | }; 57 | var getValue = function(hsv, i, isLight) { 58 | var value; 59 | if (isLight) { 60 | value = hsv.v + brightnessStep1 * i; 61 | }else{ 62 | value = hsv.v - brightnessStep2 * i 63 | } 64 | if (value > 1) { 65 | value = 1; 66 | } 67 | return Number(value.toFixed(2)) 68 | }; 69 | 70 | this.colorPalette = function(color, index) { 71 | var isLight = index <= 6; 72 | var hsv = tinycolor(color).toHsv(); 73 | var i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1; 74 | return tinycolor({ 75 | h: getHue(hsv, i, isLight), 76 | s: getSaturation(hsv, i, isLight), 77 | v: getValue(hsv, i, isLight), 78 | }).toHexString(); 79 | }; 80 | })()`; 81 | } 82 | // It is hacky way to make this function will be compiled preferentially by less 83 | // resolve error: `ReferenceError: colorPalette is not defined` 84 | // https://github.com/ant-design/ant-motion/issues/44 85 | .colorPaletteMixin(); 86 | -------------------------------------------------------------------------------- /src/assets/style/color/colors.less: -------------------------------------------------------------------------------- 1 | @import 'colorPalette'; 2 | 3 | // color palettes 4 | @blue-base: #1890ff; 5 | @blue-1: color(~`colorPalette('@{blue-6}', 1) `); 6 | @blue-2: color(~`colorPalette('@{blue-6}', 2) `); 7 | @blue-3: color(~`colorPalette('@{blue-6}', 3) `); 8 | @blue-4: color(~`colorPalette('@{blue-6}', 4) `); 9 | @blue-5: color(~`colorPalette('@{blue-6}', 5) `); 10 | @blue-6: @blue-base; 11 | @blue-7: color(~`colorPalette('@{blue-6}', 7) `); 12 | @blue-8: color(~`colorPalette('@{blue-6}', 8) `); 13 | @blue-9: color(~`colorPalette('@{blue-6}', 9) `); 14 | @blue-10: color(~`colorPalette('@{blue-6}', 10) `); 15 | 16 | @purple-base: #722ed1; 17 | @purple-1: color(~`colorPalette('@{purple-6}', 1) `); 18 | @purple-2: color(~`colorPalette('@{purple-6}', 2) `); 19 | @purple-3: color(~`colorPalette('@{purple-6}', 3) `); 20 | @purple-4: color(~`colorPalette('@{purple-6}', 4) `); 21 | @purple-5: color(~`colorPalette('@{purple-6}', 5) `); 22 | @purple-6: @purple-base; 23 | @purple-7: color(~`colorPalette('@{purple-6}', 7) `); 24 | @purple-8: color(~`colorPalette('@{purple-6}', 8) `); 25 | @purple-9: color(~`colorPalette('@{purple-6}', 9) `); 26 | @purple-10: color(~`colorPalette('@{purple-6}', 10) `); 27 | 28 | @cyan-base: #13c2c2; 29 | @cyan-1: color(~`colorPalette('@{cyan-6}', 1) `); 30 | @cyan-2: color(~`colorPalette('@{cyan-6}', 2) `); 31 | @cyan-3: color(~`colorPalette('@{cyan-6}', 3) `); 32 | @cyan-4: color(~`colorPalette('@{cyan-6}', 4) `); 33 | @cyan-5: color(~`colorPalette('@{cyan-6}', 5) `); 34 | @cyan-6: @cyan-base; 35 | @cyan-7: color(~`colorPalette('@{cyan-6}', 7) `); 36 | @cyan-8: color(~`colorPalette('@{cyan-6}', 8) `); 37 | @cyan-9: color(~`colorPalette('@{cyan-6}', 9) `); 38 | @cyan-10: color(~`colorPalette('@{cyan-6}', 10) `); 39 | 40 | @green-base: #52c41a; 41 | @green-1: color(~`colorPalette('@{green-6}', 1) `); 42 | @green-2: color(~`colorPalette('@{green-6}', 2) `); 43 | @green-3: color(~`colorPalette('@{green-6}', 3) `); 44 | @green-4: color(~`colorPalette('@{green-6}', 4) `); 45 | @green-5: color(~`colorPalette('@{green-6}', 5) `); 46 | @green-6: @green-base; 47 | @green-7: color(~`colorPalette('@{green-6}', 7) `); 48 | @green-8: color(~`colorPalette('@{green-6}', 8) `); 49 | @green-9: color(~`colorPalette('@{green-6}', 9) `); 50 | @green-10: color(~`colorPalette('@{green-6}', 10) `); 51 | 52 | @magenta-base: #eb2f96; 53 | @magenta-1: color(~`colorPalette('@{magenta-6}', 1) `); 54 | @magenta-2: color(~`colorPalette('@{magenta-6}', 2) `); 55 | @magenta-3: color(~`colorPalette('@{magenta-6}', 3) `); 56 | @magenta-4: color(~`colorPalette('@{magenta-6}', 4) `); 57 | @magenta-5: color(~`colorPalette('@{magenta-6}', 5) `); 58 | @magenta-6: @magenta-base; 59 | @magenta-7: color(~`colorPalette('@{magenta-6}', 7) `); 60 | @magenta-8: color(~`colorPalette('@{magenta-6}', 8) `); 61 | @magenta-9: color(~`colorPalette('@{magenta-6}', 9) `); 62 | @magenta-10: color(~`colorPalette('@{magenta-6}', 10) `); 63 | 64 | // alias of magenta 65 | @pink-base: #eb2f96; 66 | @pink-1: color(~`colorPalette('@{pink-6}', 1) `); 67 | @pink-2: color(~`colorPalette('@{pink-6}', 2) `); 68 | @pink-3: color(~`colorPalette('@{pink-6}', 3) `); 69 | @pink-4: color(~`colorPalette('@{pink-6}', 4) `); 70 | @pink-5: color(~`colorPalette('@{pink-6}', 5) `); 71 | @pink-6: @pink-base; 72 | @pink-7: color(~`colorPalette('@{pink-6}', 7) `); 73 | @pink-8: color(~`colorPalette('@{pink-6}', 8) `); 74 | @pink-9: color(~`colorPalette('@{pink-6}', 9) `); 75 | @pink-10: color(~`colorPalette('@{pink-6}', 10) `); 76 | 77 | @red-base: #f5222d; 78 | @red-1: color(~`colorPalette('@{red-6}', 1) `); 79 | @red-2: color(~`colorPalette('@{red-6}', 2) `); 80 | @red-3: color(~`colorPalette('@{red-6}', 3) `); 81 | @red-4: color(~`colorPalette('@{red-6}', 4) `); 82 | @red-5: color(~`colorPalette('@{red-6}', 5) `); 83 | @red-6: @red-base; 84 | @red-7: color(~`colorPalette('@{red-6}', 7) `); 85 | @red-8: color(~`colorPalette('@{red-6}', 8) `); 86 | @red-9: color(~`colorPalette('@{red-6}', 9) `); 87 | @red-10: color(~`colorPalette('@{red-6}', 10) `); 88 | 89 | @orange-base: #fa8c16; 90 | @orange-1: color(~`colorPalette('@{orange-6}', 1) `); 91 | @orange-2: color(~`colorPalette('@{orange-6}', 2) `); 92 | @orange-3: color(~`colorPalette('@{orange-6}', 3) `); 93 | @orange-4: color(~`colorPalette('@{orange-6}', 4) `); 94 | @orange-5: color(~`colorPalette('@{orange-6}', 5) `); 95 | @orange-6: @orange-base; 96 | @orange-7: color(~`colorPalette('@{orange-6}', 7) `); 97 | @orange-8: color(~`colorPalette('@{orange-6}', 8) `); 98 | @orange-9: color(~`colorPalette('@{orange-6}', 9) `); 99 | @orange-10: color(~`colorPalette('@{orange-6}', 10) `); 100 | 101 | @yellow-base: #fadb14; 102 | @yellow-1: color(~`colorPalette('@{yellow-6}', 1) `); 103 | @yellow-2: color(~`colorPalette('@{yellow-6}', 2) `); 104 | @yellow-3: color(~`colorPalette('@{yellow-6}', 3) `); 105 | @yellow-4: color(~`colorPalette('@{yellow-6}', 4) `); 106 | @yellow-5: color(~`colorPalette('@{yellow-6}', 5) `); 107 | @yellow-6: @yellow-base; 108 | @yellow-7: color(~`colorPalette('@{yellow-6}', 7) `); 109 | @yellow-8: color(~`colorPalette('@{yellow-6}', 8) `); 110 | @yellow-9: color(~`colorPalette('@{yellow-6}', 9) `); 111 | @yellow-10: color(~`colorPalette('@{yellow-6}', 10) `); 112 | 113 | @volcano-base: #fa541c; 114 | @volcano-1: color(~`colorPalette('@{volcano-6}', 1) `); 115 | @volcano-2: color(~`colorPalette('@{volcano-6}', 2) `); 116 | @volcano-3: color(~`colorPalette('@{volcano-6}', 3) `); 117 | @volcano-4: color(~`colorPalette('@{volcano-6}', 4) `); 118 | @volcano-5: color(~`colorPalette('@{volcano-6}', 5) `); 119 | @volcano-6: @volcano-base; 120 | @volcano-7: color(~`colorPalette('@{volcano-6}', 7) `); 121 | @volcano-8: color(~`colorPalette('@{volcano-6}', 8) `); 122 | @volcano-9: color(~`colorPalette('@{volcano-6}', 9) `); 123 | @volcano-10: color(~`colorPalette('@{volcano-6}', 10) `); 124 | 125 | @geekblue-base: #2f54eb; 126 | @geekblue-1: color(~`colorPalette('@{geekblue-6}', 1) `); 127 | @geekblue-2: color(~`colorPalette('@{geekblue-6}', 2) `); 128 | @geekblue-3: color(~`colorPalette('@{geekblue-6}', 3) `); 129 | @geekblue-4: color(~`colorPalette('@{geekblue-6}', 4) `); 130 | @geekblue-5: color(~`colorPalette('@{geekblue-6}', 5) `); 131 | @geekblue-6: @geekblue-base; 132 | @geekblue-7: color(~`colorPalette('@{geekblue-6}', 7) `); 133 | @geekblue-8: color(~`colorPalette('@{geekblue-6}', 8) `); 134 | @geekblue-9: color(~`colorPalette('@{geekblue-6}', 9) `); 135 | @geekblue-10: color(~`colorPalette('@{geekblue-6}', 10) `); 136 | 137 | @lime-base: #a0d911; 138 | @lime-1: color(~`colorPalette('@{lime-6}', 1) `); 139 | @lime-2: color(~`colorPalette('@{lime-6}', 2) `); 140 | @lime-3: color(~`colorPalette('@{lime-6}', 3) `); 141 | @lime-4: color(~`colorPalette('@{lime-6}', 4) `); 142 | @lime-5: color(~`colorPalette('@{lime-6}', 5) `); 143 | @lime-6: @lime-base; 144 | @lime-7: color(~`colorPalette('@{lime-6}', 7) `); 145 | @lime-8: color(~`colorPalette('@{lime-6}', 8) `); 146 | @lime-9: color(~`colorPalette('@{lime-6}', 9) `); 147 | @lime-10: color(~`colorPalette('@{lime-6}', 10) `); 148 | 149 | @gold-base: #faad14; 150 | @gold-1: color(~`colorPalette('@{gold-6}', 1) `); 151 | @gold-2: color(~`colorPalette('@{gold-6}', 2) `); 152 | @gold-3: color(~`colorPalette('@{gold-6}', 3) `); 153 | @gold-4: color(~`colorPalette('@{gold-6}', 4) `); 154 | @gold-5: color(~`colorPalette('@{gold-6}', 5) `); 155 | @gold-6: @gold-base; 156 | @gold-7: color(~`colorPalette('@{gold-6}', 7) `); 157 | @gold-8: color(~`colorPalette('@{gold-6}', 8) `); 158 | @gold-9: color(~`colorPalette('@{gold-6}', 9) `); 159 | @gold-10: color(~`colorPalette('@{gold-6}', 10) `); 160 | 161 | @preset-colors: pink, magenta, red, volcano, orange, yellow, gold, cyan, lime, green, blue, geekblue, 162 | purple; 163 | -------------------------------------------------------------------------------- /src/assets/style/common.less: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | html, 6 | body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | #root { 11 | height: 100%; 12 | min-height: 100%; 13 | } 14 | #content { 15 | width: 100%; 16 | height: 100%; 17 | padding: 0; 18 | margin: 0; 19 | } 20 | 21 | /* ======= table =========== */ 22 | .table-column { 23 | height: 39px; 24 | font-size: 12px; 25 | color: #666666; 26 | } 27 | .table-column a { 28 | color: #5490df; 29 | text-decoration: none; 30 | } 31 | .table-column a:hover { 32 | text-decoration: none; 33 | } 34 | .table-column a:visited { 35 | text-decoration: none; 36 | } 37 | 38 | /* ======= pagination =========== */ 39 | .common-pagination { 40 | float: right; 41 | } 42 | 43 | /* ======= logo =========== */ 44 | .common-logo { 45 | height: 64px; 46 | padding-left: 25px; 47 | overflow: hidden; 48 | line-height: 64px; 49 | background: #002140; 50 | transition: all 0.3s; 51 | img { 52 | display: inline-block; 53 | height: 32px; 54 | vertical-align: middle; 55 | } 56 | h1 { 57 | display: inline-block; 58 | margin: 0 0 11px 12px; 59 | font-family: 'Myriad Pro', 'Helvetica Neue', Arial, Helvetica, sans-serif; 60 | font-size: 20px; 61 | font-weight: 600; 62 | color: #ffffff; 63 | vertical-align: middle; 64 | } 65 | } 66 | .form-input { 67 | font-size: 12px; 68 | } 69 | .layout-sider { 70 | position: relative; 71 | z-index: 10; 72 | height: 100%; 73 | background: #002140; 74 | box-shadow: 2px 0 6px rgb(0 21 41 / 35%); 75 | } 76 | -------------------------------------------------------------------------------- /src/assets/style/default.less: -------------------------------------------------------------------------------- 1 | /* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */ 2 | @import './color/colors'; 3 | 4 | @theme: default; 5 | 6 | // The prefix to use on all css classes from ant. 7 | @ant-prefix: ant; 8 | 9 | // An override for the html selector for theme prefixes 10 | @html-selector: html; 11 | 12 | // [CSS-VARIABLE-REPLACE-BEGIN: html-variables] 13 | // [CSS-VARIABLE-REPLACE-END: html-variables] 14 | 15 | // -------- Colors ----------- 16 | // >>> Primary 17 | @primary-color: @blue-6; 18 | @primary-color-hover: color(~`colorPalette('@{primary-color}', 5) `); 19 | @primary-color-active: color(~`colorPalette('@{primary-color}', 7) `); 20 | @primary-color-outline: fade(@primary-color, @outline-fade); 21 | 22 | @processing-color: @blue-6; 23 | 24 | // >>> Info 25 | @info-color: @primary-color; 26 | @info-color-deprecated-bg: color(~`colorPalette('@{info-color}', 1) `); 27 | @info-color-deprecated-border: color(~`colorPalette('@{info-color}', 3) `); 28 | 29 | // >>> Success 30 | @success-color: @green-6; 31 | @success-color-hover: color(~`colorPalette('@{success-color}', 5) `); 32 | @success-color-active: color(~`colorPalette('@{success-color}', 7) `); 33 | @success-color-outline: fade(@success-color, @outline-fade); 34 | @success-color-deprecated-bg: color(~`colorPalette('@{success-color}', 1) `); 35 | @success-color-deprecated-border: color(~`colorPalette('@{success-color}', 3) `); 36 | 37 | // >>> Warning 38 | @warning-color: @gold-6; 39 | @warning-color-hover: color(~`colorPalette('@{warning-color}', 5) `); 40 | @warning-color-active: color(~`colorPalette('@{warning-color}', 7) `); 41 | @warning-color-outline: fade(@warning-color, @outline-fade); 42 | @warning-color-deprecated-bg: color(~`colorPalette('@{warning-color}', 1) `); 43 | @warning-color-deprecated-border: color(~`colorPalette('@{warning-color}', 3) `); 44 | 45 | // >>> Error 46 | @error-color: @red-5; 47 | @error-color-hover: color(~`colorPalette('@{error-color}', 5) `); 48 | @error-color-active: color(~`colorPalette('@{error-color}', 7) `); 49 | @error-color-outline: fade(@error-color, @outline-fade); 50 | @error-color-deprecated-bg: color(~`colorPalette('@{error-color}', 1) `); 51 | @error-color-deprecated-border: color(~`colorPalette('@{error-color}', 3) `); 52 | 53 | @highlight-color: @red-5; 54 | @normal-color: #d9d9d9; 55 | @white: #fff; 56 | @black: #000; 57 | 58 | // Color used by default to control hover and active backgrounds and for 59 | // alert info backgrounds. 60 | @primary-1: color(~`colorPalette('@{primary-color}', 1) `); // replace tint(@primary-color, 90%) 61 | @primary-2: color(~`colorPalette('@{primary-color}', 2) `); // replace tint(@primary-color, 80%) 62 | @primary-3: color(~`colorPalette('@{primary-color}', 3) `); // unused 63 | @primary-4: color(~`colorPalette('@{primary-color}', 4) `); // unused 64 | @primary-5: color( 65 | ~`colorPalette('@{primary-color}', 5) ` 66 | ); // color used to control the text color in many active and hover states, replace tint(@primary-color, 20%) 67 | @primary-6: @primary-color; // color used to control the text color of active buttons, don't use, use @primary-color 68 | @primary-7: color(~`colorPalette('@{primary-color}', 7) `); // replace shade(@primary-color, 5%) 69 | @primary-8: color(~`colorPalette('@{primary-color}', 8) `); // unused 70 | @primary-9: color(~`colorPalette('@{primary-color}', 9) `); // unused 71 | @primary-10: color(~`colorPalette('@{primary-color}', 10) `); // unused 72 | 73 | // Base Scaffolding Variables 74 | // --- 75 | 76 | // Background color for `` 77 | @body-background: #fff; 78 | // Base background color for most components 79 | @component-background: #fff; 80 | // Popover background color 81 | @popover-background: @component-background; 82 | @popover-customize-border-color: @border-color-split; 83 | @font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 84 | 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 85 | 'Noto Color Emoji'; 86 | @code-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; 87 | @text-color: fade(@black, 85%); 88 | @text-color-secondary: fade(@black, 45%); 89 | @text-color-inverse: @white; 90 | @icon-color: inherit; 91 | @icon-color-hover: fade(@black, 75%); 92 | @heading-color: fade(@black, 85%); 93 | @text-color-dark: fade(@white, 85%); 94 | @text-color-secondary-dark: fade(@white, 65%); 95 | @text-selection-bg: @primary-color; 96 | @font-variant-base: tabular-nums; 97 | @font-feature-settings-base: 'tnum'; 98 | @font-size-base: 14px; 99 | @font-size-lg: @font-size-base + 2px; 100 | @font-size-sm: 12px; 101 | @heading-1-size: ceil(@font-size-base * 2.71); 102 | @heading-2-size: ceil(@font-size-base * 2.14); 103 | @heading-3-size: ceil(@font-size-base * 1.71); 104 | @heading-4-size: ceil(@font-size-base * 1.42); 105 | @heading-5-size: ceil(@font-size-base * 1.14); 106 | // https://github.com/ant-design/ant-design/issues/20210 107 | @line-height-base: 1.5715; 108 | @border-radius-base: 2px; 109 | @border-radius-sm: 2px; 110 | 111 | // control border 112 | @control-border-radius: @border-radius-base; 113 | 114 | // arrow border 115 | @arrow-border-radius: 2px; 116 | 117 | // vertical paddings 118 | @padding-lg: 24px; // containers 119 | @padding-md: 16px; // small containers and buttons 120 | @padding-sm: 12px; // Form controls and items 121 | @padding-xs: 8px; // small items 122 | @padding-xss: 4px; // more small 123 | 124 | // vertical padding for all form controls 125 | @control-padding-horizontal: @padding-sm; 126 | @control-padding-horizontal-sm: @padding-xs; 127 | 128 | // vertical margins 129 | @margin-lg: 24px; // containers 130 | @margin-md: 16px; // small containers and buttons 131 | @margin-sm: 12px; // Form controls and items 132 | @margin-xs: 8px; // small items 133 | @margin-xss: 4px; // more small 134 | 135 | // height rules 136 | @height-base: 32px; 137 | @height-lg: 40px; 138 | @height-sm: 24px; 139 | 140 | // The background colors for active and hover states for things like 141 | // list items or table cells. 142 | @item-active-bg: @primary-1; 143 | @item-hover-bg: #f5f5f5; 144 | 145 | // ICONFONT 146 | @iconfont-css-prefix: anticon; 147 | 148 | // LINK 149 | @link-color: @primary-color; 150 | @link-hover-color: color(~`colorPalette('@{link-color}', 5) `); 151 | @link-active-color: color(~`colorPalette('@{link-color}', 7) `); 152 | @link-decoration: none; 153 | @link-hover-decoration: none; 154 | @link-focus-decoration: none; 155 | @link-focus-outline: 0; 156 | 157 | // Animation 158 | @ease-base-out: cubic-bezier(0.7, 0.3, 0.1, 1); 159 | @ease-base-in: cubic-bezier(0.9, 0, 0.3, 0.7); 160 | @ease-out: cubic-bezier(0.215, 0.61, 0.355, 1); 161 | @ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19); 162 | @ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1); 163 | @ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46); 164 | @ease-in-back: cubic-bezier(0.71, -0.46, 0.88, 0.6); 165 | @ease-in-out-back: cubic-bezier(0.71, -0.46, 0.29, 1.46); 166 | @ease-out-circ: cubic-bezier(0.08, 0.82, 0.17, 1); 167 | @ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.34); 168 | @ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86); 169 | @ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1); 170 | @ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06); 171 | @ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1); 172 | 173 | // Border color 174 | @border-color-base: hsv(0, 0, 85%); // base border outline a component 175 | @border-color-split: hsv(0, 0, 94%); // split border inside a component 176 | @border-color-inverse: @white; 177 | @border-width-base: 1px; // width of the border for a component 178 | @border-style-base: solid; // style of a components border 179 | 180 | // Outline 181 | @outline-blur-size: 0; 182 | @outline-width: 2px; 183 | @outline-color: @primary-color; // No use anymore 184 | @outline-fade: 20%; 185 | 186 | @background-color-light: hsv(0, 0, 98%); // background of header and selected item 187 | @background-color-base: hsv(0, 0, 96%); // Default grey background color 188 | 189 | // Disabled states 190 | @disabled-color: fade(#000, 25%); 191 | @disabled-bg: @background-color-base; 192 | @disabled-active-bg: tint(@black, 90%); 193 | @disabled-color-dark: fade(#fff, 35%); 194 | 195 | // Shadow 196 | @shadow-color: rgba(0, 0, 0, 0.15); 197 | @shadow-color-inverse: @component-background; 198 | @box-shadow-base: @shadow-2; 199 | @shadow-1-up: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05), 200 | 0 -12px 48px 16px rgba(0, 0, 0, 0.03); 201 | @shadow-1-down: 0 6px 16px -8px rgba(0, 0, 0, 0.08), 0 9px 28px 0 rgba(0, 0, 0, 0.05), 202 | 0 12px 48px 16px rgba(0, 0, 0, 0.03); 203 | @shadow-1-left: -6px 0 16px -8px rgba(0, 0, 0, 0.08), -9px 0 28px 0 rgba(0, 0, 0, 0.05), 204 | -12px 0 48px 16px rgba(0, 0, 0, 0.03); 205 | @shadow-1-right: 6px 0 16px -8px rgba(0, 0, 0, 0.08), 9px 0 28px 0 rgba(0, 0, 0, 0.05), 206 | 12px 0 48px 16px rgba(0, 0, 0, 0.03); 207 | @shadow-2: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 208 | 0 9px 28px 8px rgba(0, 0, 0, 0.05); 209 | 210 | // Buttons 211 | @btn-font-weight: 400; 212 | @btn-border-radius-base: @border-radius-base; 213 | @btn-border-radius-sm: @border-radius-base; 214 | @btn-border-width: @border-width-base; 215 | @btn-border-style: @border-style-base; 216 | @btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); 217 | @btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); 218 | @btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); 219 | 220 | @btn-primary-color: #fff; 221 | @btn-primary-bg: @primary-color; 222 | 223 | @btn-default-color: @text-color; 224 | @btn-default-bg: @component-background; 225 | @btn-default-border: @border-color-base; 226 | 227 | @btn-danger-color: #fff; 228 | @btn-danger-bg: @error-color; 229 | @btn-danger-border: @error-color; 230 | 231 | @btn-disable-color: @disabled-color; 232 | @btn-disable-bg: @disabled-bg; 233 | @btn-disable-border: @border-color-base; 234 | 235 | @btn-default-ghost-color: @component-background; 236 | @btn-default-ghost-bg: transparent; 237 | @btn-default-ghost-border: @component-background; 238 | 239 | @btn-font-size-lg: @font-size-lg; 240 | @btn-font-size-sm: @font-size-base; 241 | @btn-padding-horizontal-base: @padding-md - 1px; 242 | @btn-padding-horizontal-lg: @btn-padding-horizontal-base; 243 | @btn-padding-horizontal-sm: @padding-xs - 1px; 244 | 245 | @btn-height-base: @height-base; 246 | @btn-height-lg: @height-lg; 247 | @btn-height-sm: @height-sm; 248 | 249 | @btn-line-height: @line-height-base; 250 | 251 | @btn-circle-size: @btn-height-base; 252 | @btn-circle-size-lg: @btn-height-lg; 253 | @btn-circle-size-sm: @btn-height-sm; 254 | 255 | @btn-square-size: @btn-height-base; 256 | @btn-square-size-lg: @btn-height-lg; 257 | @btn-square-size-sm: @btn-height-sm; 258 | @btn-square-only-icon-size: @font-size-base + 2px; 259 | @btn-square-only-icon-size-sm: @font-size-base; 260 | @btn-square-only-icon-size-lg: @btn-font-size-lg + 2px; 261 | 262 | @btn-group-border: @primary-5; 263 | 264 | @btn-link-hover-bg: transparent; 265 | @btn-text-hover-bg: rgba(0, 0, 0, 0.018); 266 | 267 | // Checkbox 268 | @checkbox-size: 16px; 269 | @checkbox-color: @primary-color; 270 | @checkbox-check-color: #fff; 271 | @checkbox-check-bg: @checkbox-check-color; 272 | @checkbox-border-width: @border-width-base; 273 | @checkbox-border-radius: @border-radius-base; 274 | @checkbox-group-item-margin-right: 8px; 275 | 276 | // Descriptions 277 | @descriptions-bg: #fafafa; 278 | @descriptions-title-margin-bottom: 20px; 279 | @descriptions-default-padding: @padding-md @padding-lg; 280 | @descriptions-middle-padding: @padding-sm @padding-lg; 281 | @descriptions-small-padding: @padding-xs @padding-md; 282 | @descriptions-item-padding-bottom: @padding-md; 283 | @descriptions-item-trailing-colon: true; 284 | @descriptions-item-label-colon-margin-right: 8px; 285 | @descriptions-item-label-colon-margin-left: 2px; 286 | @descriptions-extra-color: @text-color; 287 | 288 | // Divider 289 | @divider-text-padding: 1em; 290 | @divider-orientation-margin: 5%; 291 | @divider-color: rgba(0, 0, 0, 6%); 292 | @divider-vertical-gutter: 8px; 293 | 294 | // Dropdown 295 | @dropdown-selected-color: @primary-color; 296 | @dropdown-menu-submenu-disabled-bg: @component-background; 297 | @dropdown-selected-bg: @item-active-bg; 298 | 299 | // Empty 300 | @empty-font-size: @font-size-base; 301 | 302 | // Radio 303 | @radio-size: 16px; 304 | @radio-top: 0.2em; 305 | @radio-border-width: 1px; 306 | @radio-dot-size: @radio-size - 8px; 307 | @radio-dot-color: @primary-color; 308 | @radio-dot-disabled-color: fade(@black, 20%); 309 | @radio-solid-checked-color: @component-background; 310 | 311 | // Radio buttons 312 | @radio-button-bg: @btn-default-bg; 313 | @radio-button-checked-bg: @btn-default-bg; 314 | @radio-button-color: @btn-default-color; 315 | @radio-button-hover-color: @primary-5; 316 | @radio-button-active-color: @primary-7; 317 | @radio-button-padding-horizontal: @padding-md - 1px; 318 | @radio-disabled-button-checked-bg: @disabled-active-bg; 319 | @radio-disabled-button-checked-color: @disabled-color; 320 | @radio-wrapper-margin-right: 8px; 321 | 322 | // Media queries breakpoints 323 | // @screen-xs and @screen-xs-min is not used in Grid 324 | // smallest break point is @screen-md 325 | @screen-xs: 480px; 326 | @screen-xs-min: @screen-xs; 327 | // 👆 Extra small screen / phone 328 | 329 | // 👇 Small screen / tablet 330 | @screen-sm: 576px; 331 | @screen-sm-min: @screen-sm; 332 | 333 | // Medium screen / desktop 334 | @screen-md: 768px; 335 | @screen-md-min: @screen-md; 336 | 337 | // Large screen / wide desktop 338 | @screen-lg: 992px; 339 | @screen-lg-min: @screen-lg; 340 | 341 | // Extra large screen / full hd 342 | @screen-xl: 1200px; 343 | @screen-xl-min: @screen-xl; 344 | 345 | // Extra extra large screen / large desktop 346 | @screen-xxl: 1600px; 347 | @screen-xxl-min: @screen-xxl; 348 | 349 | // provide a maximum 350 | @screen-xs-max: (@screen-sm-min - 1px); 351 | @screen-sm-max: (@screen-md-min - 1px); 352 | @screen-md-max: (@screen-lg-min - 1px); 353 | @screen-lg-max: (@screen-xl-min - 1px); 354 | @screen-xl-max: (@screen-xxl-min - 1px); 355 | 356 | // Grid system 357 | @grid-columns: 24; 358 | 359 | // Layout 360 | @layout-body-background: #f0f2f5; 361 | @layout-header-background: #001529; 362 | @layout-header-height: 64px; 363 | @layout-header-padding: 0 50px; 364 | @layout-header-color: @text-color; 365 | @layout-footer-padding: 24px 50px; 366 | @layout-footer-background: @layout-body-background; 367 | @layout-sider-background: @layout-header-background; 368 | @layout-trigger-height: 48px; 369 | @layout-trigger-background: #002140; 370 | @layout-trigger-color: #fff; 371 | @layout-zero-trigger-width: 36px; 372 | @layout-zero-trigger-height: 42px; 373 | // Layout light theme 374 | @layout-sider-background-light: #fff; 375 | @layout-trigger-background-light: #fff; 376 | @layout-trigger-color-light: @text-color; 377 | 378 | // z-index list, order by `z-index` 379 | @zindex-badge: auto; 380 | @zindex-table-fixed: 2; 381 | @zindex-affix: 10; 382 | @zindex-back-top: 10; 383 | @zindex-picker-panel: 10; 384 | @zindex-popup-close: 10; 385 | @zindex-modal: 1000; 386 | @zindex-modal-mask: 1000; 387 | @zindex-message: 1010; 388 | @zindex-notification: 1010; 389 | @zindex-popover: 1030; 390 | @zindex-dropdown: 1050; 391 | @zindex-picker: 1050; 392 | @zindex-popoconfirm: 1060; 393 | @zindex-tooltip: 1070; 394 | @zindex-image: 1080; 395 | 396 | // Animation 397 | @animation-duration-slow: 0.3s; // Modal 398 | @animation-duration-base: 0.2s; 399 | @animation-duration-fast: 0.1s; // Tooltip 400 | 401 | //CollapsePanel 402 | @collapse-panel-border-radius: @border-radius-base; 403 | 404 | //Dropdown 405 | @dropdown-menu-bg: @component-background; 406 | @dropdown-vertical-padding: 5px; 407 | @dropdown-edge-child-vertical-padding: 4px; 408 | @dropdown-font-size: @font-size-base; 409 | @dropdown-line-height: 22px; 410 | 411 | // Form 412 | // --- 413 | @label-required-color: @highlight-color; 414 | @label-color: @heading-color; 415 | @form-warning-input-bg: @input-bg; 416 | @form-item-margin-bottom: 24px; 417 | @form-item-trailing-colon: true; 418 | @form-vertical-label-padding: 0 0 8px; 419 | @form-vertical-label-margin: 0; 420 | @form-item-label-font-size: @font-size-base; 421 | @form-item-label-height: @input-height-base; 422 | @form-item-label-colon-margin-right: 8px; 423 | @form-item-label-colon-margin-left: 2px; 424 | @form-error-input-bg: @input-bg; 425 | 426 | // Input 427 | // --- 428 | @input-height-base: @height-base; 429 | @input-height-lg: @height-lg; 430 | @input-height-sm: @height-sm; 431 | @input-padding-horizontal: @control-padding-horizontal - 1px; 432 | @input-padding-horizontal-base: @input-padding-horizontal; 433 | @input-padding-horizontal-sm: @control-padding-horizontal-sm - 1px; 434 | @input-padding-horizontal-lg: @input-padding-horizontal; 435 | @input-padding-vertical-base: max( 436 | (round(((@input-height-base - @font-size-base * @line-height-base) / 2) * 10) / 10) - 437 | @border-width-base, 438 | 3px 439 | ); 440 | @input-padding-vertical-sm: max( 441 | (round(((@input-height-sm - @font-size-base * @line-height-base) / 2) * 10) / 10) - 442 | @border-width-base, 443 | 0 444 | ); 445 | @input-padding-vertical-lg: ( 446 | ceil(((@input-height-lg - @font-size-lg * @line-height-base) / 2) * 10) / 10 447 | ) - @border-width-base; 448 | @input-placeholder-color: hsv(0, 0, 75%); 449 | @input-color: @text-color; 450 | @input-icon-color: @input-color; 451 | @input-border-color: @border-color-base; 452 | @input-bg: @component-background; 453 | @input-number-hover-border-color: @input-hover-border-color; 454 | @input-number-handler-active-bg: #f4f4f4; 455 | @input-number-handler-hover-bg: @primary-5; 456 | @input-number-handler-bg: @component-background; 457 | @input-number-handler-border-color: @border-color-base; 458 | @input-addon-bg: @background-color-light; 459 | @input-hover-border-color: @primary-5; 460 | @input-disabled-bg: @disabled-bg; 461 | @input-outline-offset: 0 0; 462 | @input-icon-hover-color: fade(@black, 85%); 463 | @input-disabled-color: @disabled-color; 464 | 465 | // Mentions 466 | // --- 467 | @mentions-dropdown-bg: @component-background; 468 | @mentions-dropdown-menu-item-hover-bg: @mentions-dropdown-bg; 469 | 470 | // Select 471 | // --- 472 | @select-border-color: @border-color-base; 473 | @select-item-selected-color: @text-color; 474 | @select-item-selected-font-weight: 600; 475 | @select-dropdown-bg: @component-background; 476 | @select-item-selected-bg: @primary-1; 477 | @select-item-active-bg: @item-hover-bg; 478 | @select-dropdown-vertical-padding: @dropdown-vertical-padding; 479 | @select-dropdown-font-size: @dropdown-font-size; 480 | @select-dropdown-line-height: @dropdown-line-height; 481 | @select-dropdown-height: 32px; 482 | @select-background: @component-background; 483 | @select-clear-background: @select-background; 484 | @select-selection-item-bg: @background-color-base; 485 | @select-selection-item-border-color: @border-color-split; 486 | @select-single-item-height-lg: 40px; 487 | @select-multiple-item-height: @input-height-base - @input-padding-vertical-base * 2; // Normal 24px 488 | @select-multiple-item-height-lg: 32px; 489 | @select-multiple-item-spacing-half: ceil((@input-padding-vertical-base / 2)); 490 | @select-multiple-disabled-background: @input-disabled-bg; 491 | @select-multiple-item-disabled-color: #bfbfbf; 492 | @select-multiple-item-disabled-border-color: @select-border-color; 493 | 494 | // Cascader 495 | // --- 496 | @cascader-bg: @component-background; 497 | @cascader-item-selected-bg: @primary-1; 498 | @cascader-menu-bg: @component-background; 499 | @cascader-menu-border-color-split: @border-color-split; 500 | 501 | // Cascader 502 | // ---- 503 | @cascader-dropdown-vertical-padding: @dropdown-vertical-padding; 504 | @cascader-dropdown-edge-child-vertical-padding: @dropdown-edge-child-vertical-padding; 505 | @cascader-dropdown-font-size: @dropdown-font-size; 506 | @cascader-dropdown-line-height: @dropdown-line-height; 507 | 508 | // Anchor 509 | // --- 510 | @anchor-bg: transparent; 511 | @anchor-border-color: @border-color-split; 512 | @anchor-link-top: 4px; 513 | @anchor-link-left: 16px; 514 | @anchor-link-padding: @anchor-link-top 0 @anchor-link-top @anchor-link-left; 515 | 516 | // Tooltip 517 | // --- 518 | // Tooltip max width 519 | @tooltip-max-width: 250px; 520 | // Tooltip text color 521 | @tooltip-color: #fff; 522 | // Tooltip background color 523 | @tooltip-bg: rgba(0, 0, 0, 0.75); 524 | // Tooltip arrow width 525 | @tooltip-arrow-width: 8px * sqrt(2); 526 | // Tooltip distance with trigger 527 | @tooltip-distance: @tooltip-arrow-width - 1px + 4px; 528 | // Tooltip arrow color 529 | @tooltip-arrow-color: @tooltip-bg; 530 | @tooltip-border-radius: @border-radius-base; 531 | 532 | // Popover 533 | // --- 534 | // Popover body background color 535 | @popover-bg: @component-background; 536 | // Popover text color 537 | @popover-color: @text-color; 538 | // Popover maximum width 539 | @popover-min-width: 177px; 540 | @popover-min-height: 32px; 541 | // Popover arrow width 542 | @popover-arrow-width: @tooltip-arrow-width; 543 | // Popover arrow color 544 | @popover-arrow-color: @popover-bg; 545 | // Popover outer arrow width 546 | // Popover outer arrow color 547 | @popover-arrow-outer-color: @popover-bg; 548 | // Popover distance with trigger 549 | @popover-distance: @popover-arrow-width + 4px; 550 | @popover-padding-horizontal: @padding-md; 551 | 552 | // Modal 553 | // -- 554 | @modal-header-padding-vertical: @padding-md; 555 | @modal-header-padding-horizontal: @padding-lg; 556 | @modal-body-padding: @padding-lg; 557 | @modal-header-bg: @component-background; 558 | @modal-header-padding: @modal-header-padding-vertical @modal-header-padding-horizontal; 559 | @modal-header-border-width: @border-width-base; 560 | @modal-header-border-style: @border-style-base; 561 | @modal-header-title-line-height: 22px; 562 | @modal-header-title-font-size: @font-size-lg; 563 | @modal-header-border-color-split: @border-color-split; 564 | @modal-header-close-size: @modal-header-title-line-height + 2 * @modal-header-padding-vertical; 565 | @modal-content-bg: @component-background; 566 | @modal-heading-color: @heading-color; 567 | @modal-close-color: @text-color-secondary; 568 | @modal-footer-bg: transparent; 569 | @modal-footer-border-color-split: @border-color-split; 570 | @modal-footer-border-style: @border-style-base; 571 | @modal-footer-padding-vertical: 10px; 572 | @modal-footer-padding-horizontal: 16px; 573 | @modal-footer-border-width: @border-width-base; 574 | @modal-mask-bg: fade(@black, 45%); 575 | @modal-confirm-body-padding: 32px 32px 24px; 576 | @modal-confirm-title-font-size: @font-size-lg; 577 | @modal-border-radius: @border-radius-base; 578 | 579 | // Progress 580 | // -- 581 | @progress-default-color: @processing-color; 582 | @progress-remaining-color: @background-color-base; 583 | @progress-info-text-color: @progress-text-color; 584 | @progress-radius: 100px; 585 | @progress-steps-item-bg: #f3f3f3; 586 | @progress-text-font-size: 1em; 587 | @progress-text-color: @text-color; // This is for circle text color, should be renamed better 588 | @progress-circle-text-font-size: 1em; 589 | // Menu 590 | // --- 591 | @menu-inline-toplevel-item-height: 40px; 592 | @menu-item-height: 40px; 593 | @menu-item-group-height: @line-height-base; 594 | @menu-collapsed-width: 80px; 595 | @menu-bg: @component-background; 596 | @menu-popup-bg: @component-background; 597 | @menu-item-color: @text-color; 598 | @menu-inline-submenu-bg: @background-color-light; 599 | @menu-highlight-color: @primary-color; 600 | @menu-highlight-danger-color: @error-color; 601 | @menu-item-active-bg: @primary-1; 602 | @menu-item-active-danger-bg: @red-1; 603 | @menu-item-active-border-width: 3px; 604 | @menu-item-group-title-color: @text-color-secondary; 605 | @menu-item-vertical-margin: 4px; 606 | @menu-item-font-size: @font-size-base; 607 | @menu-item-boundary-margin: 8px; 608 | @menu-item-padding-horizontal: 20px; 609 | @menu-item-padding: 0 @menu-item-padding-horizontal; 610 | @menu-horizontal-line-height: 46px; 611 | @menu-icon-margin-right: 10px; 612 | @menu-icon-size: @menu-item-font-size; 613 | @menu-icon-size-lg: @font-size-lg; 614 | @menu-item-group-title-font-size: @menu-item-font-size; 615 | 616 | // dark theme 617 | @menu-dark-color: @text-color-secondary-dark; 618 | @menu-dark-danger-color: @error-color; 619 | @menu-dark-bg: @layout-header-background; 620 | @menu-dark-arrow-color: #fff; 621 | @menu-dark-inline-submenu-bg: #000c17; 622 | @menu-dark-highlight-color: #fff; 623 | @menu-dark-item-active-bg: @primary-color; 624 | @menu-dark-item-active-danger-bg: @error-color; 625 | @menu-dark-selected-item-icon-color: @white; 626 | @menu-dark-selected-item-text-color: @white; 627 | @menu-dark-item-hover-bg: transparent; 628 | // Spin 629 | // --- 630 | @spin-dot-size-sm: 14px; 631 | @spin-dot-size: 20px; 632 | @spin-dot-size-lg: 32px; 633 | 634 | // Table 635 | // -- 636 | @table-bg: @component-background; 637 | @table-header-bg: @background-color-light; 638 | @table-header-color: @heading-color; 639 | @table-header-sort-bg: @background-color-base; 640 | @table-body-sort-bg: #fafafa; 641 | @table-row-hover-bg: @background-color-light; 642 | @table-selected-row-color: inherit; 643 | @table-selected-row-bg: @primary-1; 644 | @table-body-selected-sort-bg: @table-selected-row-bg; 645 | @table-selected-row-hover-bg: darken(@table-selected-row-bg, 2%); 646 | @table-expanded-row-bg: #fbfbfb; 647 | @table-padding-vertical: 16px; 648 | @table-padding-horizontal: 16px; 649 | @table-padding-vertical-md: (@table-padding-vertical * 3 / 4); 650 | @table-padding-horizontal-md: (@table-padding-horizontal / 2); 651 | @table-padding-vertical-sm: (@table-padding-vertical / 2); 652 | @table-padding-horizontal-sm: (@table-padding-horizontal / 2); 653 | @table-border-color: @border-color-split; 654 | @table-border-radius-base: @border-radius-base; 655 | @table-footer-bg: @background-color-light; 656 | @table-footer-color: @heading-color; 657 | @table-header-bg-sm: @table-header-bg; 658 | @table-font-size: @font-size-base; 659 | @table-font-size-md: @table-font-size; 660 | @table-font-size-sm: @table-font-size; 661 | @table-header-cell-split-color: rgba(0, 0, 0, 0.06); 662 | // Sorter 663 | // Legacy: `table-header-sort-active-bg` is used for hover not real active 664 | @table-header-sort-active-bg: rgba(0, 0, 0, 0.04); 665 | @table-fixed-header-sort-active-bg: hsv(0, 0, 96%); 666 | 667 | // Filter 668 | @table-header-filter-active-bg: rgba(0, 0, 0, 0.04); 669 | @table-filter-btns-bg: inherit; 670 | @table-filter-dropdown-bg: @component-background; 671 | @table-expand-icon-bg: @component-background; 672 | @table-selection-column-width: 32px; 673 | // Sticky 674 | @table-sticky-scroll-bar-bg: fade(#000, 35%); 675 | @table-sticky-scroll-bar-radius: 4px; 676 | 677 | // Tag 678 | // -- 679 | @tag-border-radius: @border-radius-base; 680 | @tag-default-bg: @background-color-light; 681 | @tag-default-color: @text-color; 682 | @tag-font-size: @font-size-sm; 683 | @tag-line-height: 20px; 684 | 685 | // TimePicker 686 | // --- 687 | @picker-bg: @component-background; 688 | @picker-basic-cell-hover-color: @item-hover-bg; 689 | @picker-basic-cell-active-with-range-color: @primary-1; 690 | @picker-basic-cell-hover-with-range-color: lighten(@primary-color, 35%); 691 | @picker-basic-cell-disabled-bg: rgba(0, 0, 0, 0.04); 692 | @picker-border-color: @border-color-split; 693 | @picker-date-hover-range-border-color: lighten(@primary-color, 20%); 694 | @picker-date-hover-range-color: @picker-basic-cell-hover-with-range-color; 695 | @picker-time-panel-column-width: 56px; 696 | @picker-time-panel-column-height: 224px; 697 | @picker-time-panel-cell-height: 28px; 698 | @picker-panel-cell-height: 24px; 699 | @picker-panel-cell-width: 36px; 700 | @picker-text-height: 40px; 701 | @picker-panel-without-time-cell-height: 66px; 702 | 703 | // Calendar 704 | // --- 705 | @calendar-bg: @component-background; 706 | @calendar-input-bg: @input-bg; 707 | @calendar-border-color: @border-color-inverse; 708 | @calendar-item-active-bg: @item-active-bg; 709 | @calendar-column-active-bg: fade(@calendar-item-active-bg, 20%); 710 | @calendar-full-bg: @calendar-bg; 711 | @calendar-full-panel-bg: @calendar-full-bg; 712 | 713 | // Carousel 714 | // --- 715 | @carousel-dot-width: 16px; 716 | @carousel-dot-height: 3px; 717 | @carousel-dot-active-width: 24px; 718 | 719 | // Badge 720 | // --- 721 | @badge-height: 20px; 722 | @badge-height-sm: 14px; 723 | @badge-dot-size: 6px; 724 | @badge-font-size: @font-size-sm; 725 | @badge-font-size-sm: @font-size-sm; 726 | @badge-font-weight: normal; 727 | @badge-status-size: 6px; 728 | @badge-text-color: @component-background; 729 | @badge-color: @highlight-color; 730 | 731 | // Rate 732 | // --- 733 | @rate-star-color: @yellow-6; 734 | @rate-star-bg: @border-color-split; 735 | @rate-star-size: 20px; 736 | @rate-star-hover-scale: scale(1.1); 737 | 738 | // Card 739 | // --- 740 | @card-head-color: @heading-color; 741 | @card-head-background: transparent; 742 | @card-head-font-size: @font-size-lg; 743 | @card-head-font-size-sm: @font-size-base; 744 | @card-head-padding: 16px; 745 | @card-head-padding-sm: (@card-head-padding / 2); 746 | @card-head-height: 48px; 747 | @card-head-height-sm: 36px; 748 | @card-inner-head-padding: 12px; 749 | @card-padding-base: 24px; 750 | @card-padding-base-sm: (@card-padding-base / 2); 751 | @card-actions-background: @component-background; 752 | @card-actions-li-margin: 12px 0; 753 | @card-skeleton-bg: #cfd8dc; 754 | @card-background: @component-background; 755 | @card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12), 756 | 0 5px 12px 4px rgba(0, 0, 0, 0.09); 757 | @card-radius: @border-radius-base; 758 | @card-head-tabs-margin-bottom: -17px; 759 | @card-head-extra-color: @text-color; 760 | 761 | // Comment 762 | // --- 763 | @comment-bg: inherit; 764 | @comment-padding-base: @padding-md 0; 765 | @comment-nest-indent: 44px; 766 | @comment-font-size-base: @font-size-base; 767 | @comment-font-size-sm: @font-size-sm; 768 | @comment-author-name-color: @text-color-secondary; 769 | @comment-author-time-color: #ccc; 770 | @comment-action-color: @text-color-secondary; 771 | @comment-action-hover-color: #595959; 772 | @comment-actions-margin-bottom: inherit; 773 | @comment-actions-margin-top: @margin-sm; 774 | @comment-content-detail-p-margin-bottom: inherit; 775 | 776 | // Tabs 777 | // --- 778 | @tabs-card-head-background: @background-color-light; 779 | @tabs-card-height: 40px; 780 | @tabs-card-active-color: @primary-color; 781 | @tabs-card-horizontal-padding: ( 782 | (@tabs-card-height - floor(@font-size-base * @line-height-base)) / 2 783 | ) - @border-width-base @padding-md; 784 | @tabs-card-horizontal-padding-sm: 6px @padding-md; 785 | @tabs-card-horizontal-padding-lg: 7px @padding-md 6px; 786 | @tabs-title-font-size: @font-size-base; 787 | @tabs-title-font-size-lg: @font-size-lg; 788 | @tabs-title-font-size-sm: @font-size-base; 789 | @tabs-ink-bar-color: @primary-color; 790 | @tabs-bar-margin: 0 0 @margin-md 0; 791 | @tabs-horizontal-gutter: 32px; 792 | @tabs-horizontal-margin: 0 0 0 @tabs-horizontal-gutter; 793 | @tabs-horizontal-margin-rtl: 0 0 0 32px; 794 | @tabs-horizontal-padding: @padding-sm 0; 795 | @tabs-horizontal-padding-lg: @padding-md 0; 796 | @tabs-horizontal-padding-sm: @padding-xs 0; 797 | @tabs-vertical-padding: @padding-xs @padding-lg; 798 | @tabs-vertical-margin: @margin-md 0 0 0; 799 | @tabs-scrolling-size: 32px; 800 | @tabs-highlight-color: @primary-color; 801 | @tabs-hover-color: @primary-5; 802 | @tabs-active-color: @primary-7; 803 | @tabs-card-gutter: 2px; 804 | @tabs-card-tab-active-border-top: 2px solid transparent; 805 | 806 | // BackTop 807 | // --- 808 | @back-top-color: #fff; 809 | @back-top-bg: @text-color-secondary; 810 | @back-top-hover-bg: @text-color; 811 | 812 | // Avatar 813 | // --- 814 | @avatar-size-base: 32px; 815 | @avatar-size-lg: 40px; 816 | @avatar-size-sm: 24px; 817 | @avatar-font-size-base: 18px; 818 | @avatar-font-size-lg: 24px; 819 | @avatar-font-size-sm: 14px; 820 | @avatar-bg: #ccc; 821 | @avatar-color: #fff; 822 | @avatar-border-radius: @border-radius-base; 823 | @avatar-group-overlapping: -8px; 824 | @avatar-group-space: 3px; 825 | @avatar-group-border-color: #fff; 826 | 827 | // Switch 828 | // --- 829 | @switch-height: 22px; 830 | @switch-sm-height: 16px; 831 | @switch-min-width: 44px; 832 | @switch-sm-min-width: 28px; 833 | @switch-disabled-opacity: 0.4; 834 | @switch-color: @primary-color; 835 | @switch-bg: @component-background; 836 | @switch-shadow-color: fade(#00230b, 20%); 837 | @switch-padding: 2px; 838 | @switch-inner-margin-min: ceil(@switch-height * 0.3); 839 | @switch-inner-margin-max: ceil(@switch-height * 1.1); 840 | @switch-sm-inner-margin-min: ceil(@switch-sm-height * 0.3); 841 | @switch-sm-inner-margin-max: ceil(@switch-sm-height * 1.1); 842 | 843 | // Pagination 844 | // --- 845 | @pagination-item-bg: @component-background; 846 | @pagination-item-size: @height-base; 847 | @pagination-item-size-sm: 24px; 848 | @pagination-font-family: @font-family; 849 | @pagination-font-weight-active: 500; 850 | @pagination-item-bg-active: @component-background; 851 | @pagination-item-link-bg: @component-background; 852 | @pagination-item-disabled-color-active: @disabled-color; 853 | @pagination-item-disabled-bg-active: @disabled-active-bg; 854 | @pagination-item-input-bg: @component-background; 855 | @pagination-mini-options-size-changer-top: 0px; 856 | 857 | // PageHeader 858 | // --- 859 | @page-header-padding: @padding-lg; 860 | @page-header-padding-vertical: @padding-md; 861 | @page-header-padding-breadcrumb: @padding-sm; 862 | @page-header-content-padding-vertical: @padding-sm; 863 | @page-header-back-color: #000; 864 | @page-header-ghost-bg: inherit; 865 | @page-header-heading-title: @heading-4-size; 866 | @page-header-heading-sub-title: 14px; 867 | @page-header-tabs-tab-font-size: 16px; 868 | 869 | // Breadcrumb 870 | // --- 871 | @breadcrumb-base-color: @text-color-secondary; 872 | @breadcrumb-last-item-color: @text-color; 873 | @breadcrumb-font-size: @font-size-base; 874 | @breadcrumb-icon-font-size: @font-size-base; 875 | @breadcrumb-link-color: @text-color-secondary; 876 | @breadcrumb-link-color-hover: @text-color; 877 | @breadcrumb-separator-color: @text-color-secondary; 878 | @breadcrumb-separator-margin: 0 @padding-xs; 879 | 880 | // Slider 881 | // --- 882 | @slider-margin: 10px 6px 10px; 883 | @slider-rail-background-color: @background-color-base; 884 | @slider-rail-background-color-hover: #e1e1e1; 885 | @slider-track-background-color: @primary-3; 886 | @slider-track-background-color-hover: @primary-4; 887 | @slider-handle-border-width: 2px; 888 | @slider-handle-background-color: @component-background; 889 | @slider-handle-color: @primary-3; 890 | @slider-handle-color-hover: @primary-4; 891 | @slider-handle-color-focus: tint(@primary-color, 20%); 892 | @slider-handle-color-focus-shadow: fade(@primary-color, 12%); 893 | @slider-handle-color-tooltip-open: @primary-color; 894 | @slider-handle-size: 14px; 895 | @slider-handle-margin-top: -5px; 896 | @slider-handle-margin-left: -5px; 897 | @slider-handle-shadow: 0; 898 | @slider-dot-border-color: @border-color-split; 899 | @slider-dot-border-color-active: tint(@primary-color, 50%); 900 | @slider-disabled-color: @disabled-color; 901 | @slider-disabled-background-color: @component-background; 902 | 903 | // Tree 904 | // --- 905 | @tree-bg: @component-background; 906 | @tree-title-height: 24px; 907 | @tree-child-padding: 18px; 908 | @tree-directory-selected-color: #fff; 909 | @tree-directory-selected-bg: @primary-color; 910 | @tree-node-hover-bg: @item-hover-bg; 911 | @tree-node-selected-bg: @primary-2; 912 | 913 | // Collapse 914 | // --- 915 | @collapse-header-padding: @padding-sm @padding-md; 916 | @collapse-header-padding-extra: 40px; 917 | @collapse-header-bg: @background-color-light; 918 | @collapse-content-padding: @padding-md; 919 | @collapse-content-bg: @component-background; 920 | @collapse-header-arrow-left: 16px; 921 | 922 | // Skeleton 923 | // --- 924 | @skeleton-color: rgba(190, 190, 190, 0.2); 925 | @skeleton-to-color: shade(@skeleton-color, 5%); 926 | @skeleton-paragraph-margin-top: 28px; 927 | @skeleton-paragraph-li-margin-top: @margin-md; 928 | @skeleton-paragraph-li-height: 16px; 929 | @skeleton-title-height: 16px; 930 | @skeleton-title-paragraph-margin-top: @margin-lg; 931 | 932 | // Transfer 933 | // --- 934 | @transfer-header-height: 40px; 935 | @transfer-item-height: @height-base; 936 | @transfer-disabled-bg: @disabled-bg; 937 | @transfer-list-height: 200px; 938 | @transfer-item-hover-bg: @item-hover-bg; 939 | @transfer-item-selected-hover-bg: darken(@item-active-bg, 2%); 940 | @transfer-item-padding-vertical: 6px; 941 | @transfer-list-search-icon-top: 12px; 942 | 943 | // Message 944 | // --- 945 | @message-notice-content-padding: 10px 16px; 946 | @message-notice-content-bg: @component-background; 947 | // Motion 948 | // --- 949 | @wave-animation-width: 6px; 950 | 951 | // Alert 952 | // --- 953 | @alert-success-border-color: ~`colorPalette('@{success-color}', 3) `; 954 | @alert-success-bg-color: ~`colorPalette('@{success-color}', 1) `; 955 | @alert-success-icon-color: @success-color; 956 | @alert-info-border-color: ~`colorPalette('@{info-color}', 3) `; 957 | @alert-info-bg-color: ~`colorPalette('@{info-color}', 1) `; 958 | @alert-info-icon-color: @info-color; 959 | @alert-warning-border-color: ~`colorPalette('@{warning-color}', 3) `; 960 | @alert-warning-bg-color: ~`colorPalette('@{warning-color}', 1) `; 961 | @alert-warning-icon-color: @warning-color; 962 | @alert-error-border-color: ~`colorPalette('@{error-color}', 3) `; 963 | @alert-error-bg-color: ~`colorPalette('@{error-color}', 1) `; 964 | @alert-error-icon-color: @error-color; 965 | @alert-message-color: @heading-color; 966 | @alert-text-color: @text-color; 967 | @alert-close-color: @text-color-secondary; 968 | @alert-close-hover-color: @icon-color-hover; 969 | @alert-no-icon-padding-vertical: @padding-xs; 970 | @alert-with-description-no-icon-padding-vertical: @padding-md - 1px; 971 | @alert-with-description-padding-vertical: @padding-md - 1px; 972 | @alert-with-description-padding: @alert-with-description-padding-vertical 15px 973 | @alert-with-description-no-icon-padding-vertical @alert-with-description-icon-size; 974 | @alert-icon-top: 8px + @font-size-base * (@line-height-base / 2) - (@font-size-base / 2); 975 | @alert-with-description-icon-size: 24px; 976 | 977 | // List 978 | // --- 979 | @list-header-background: transparent; 980 | @list-footer-background: transparent; 981 | @list-empty-text-padding: @padding-md; 982 | @list-item-padding: @padding-sm 0; 983 | @list-item-padding-sm: @padding-xs @padding-md; 984 | @list-item-padding-lg: 16px 24px; 985 | @list-item-meta-margin-bottom: @padding-md; 986 | @list-item-meta-avatar-margin-right: @padding-md; 987 | @list-item-meta-title-margin-bottom: @padding-sm; 988 | @list-customize-card-bg: @component-background; 989 | @list-item-meta-description-font-size: @font-size-base; 990 | 991 | // Statistic 992 | // --- 993 | @statistic-title-font-size: @font-size-base; 994 | @statistic-content-font-size: 24px; 995 | @statistic-unit-font-size: 24px; 996 | @statistic-font-family: @font-family; 997 | 998 | // Drawer 999 | // --- 1000 | @drawer-header-padding: @padding-md @padding-lg; 1001 | @drawer-body-padding: @padding-lg; 1002 | @drawer-bg: @component-background; 1003 | @drawer-footer-padding-vertical: @modal-footer-padding-vertical; 1004 | @drawer-footer-padding-horizontal: @modal-footer-padding-horizontal; 1005 | @drawer-header-close-size: 56px; 1006 | @drawer-title-font-size: @font-size-lg; 1007 | @drawer-title-line-height: 22px; 1008 | 1009 | // Timeline 1010 | // --- 1011 | @timeline-width: 2px; 1012 | @timeline-color: @border-color-split; 1013 | @timeline-dot-border-width: 2px; 1014 | @timeline-dot-color: @primary-color; 1015 | @timeline-dot-bg: @component-background; 1016 | @timeline-item-padding-bottom: 20px; 1017 | 1018 | // Typography 1019 | // --- 1020 | @typography-title-font-weight: 600; 1021 | @typography-title-margin-top: 1.2em; 1022 | @typography-title-margin-bottom: 0.5em; 1023 | 1024 | // Upload 1025 | // --- 1026 | @upload-actions-color: @text-color-secondary; 1027 | 1028 | // Steps 1029 | // --- 1030 | @process-tail-color: @border-color-split; 1031 | @steps-nav-arrow-color: fade(@black, 25%); 1032 | @steps-background: @component-background; 1033 | @steps-icon-size: 32px; 1034 | @steps-icon-custom-size: @steps-icon-size; 1035 | @steps-icon-custom-top: 0px; 1036 | @steps-icon-custom-font-size: 24px; 1037 | @steps-icon-top: -0.5px; 1038 | @steps-icon-font-size: @font-size-lg; 1039 | @steps-icon-margin: 0 8px 0 0; 1040 | @steps-title-line-height: @height-base; 1041 | @steps-small-icon-size: 24px; 1042 | @steps-small-icon-margin: 0 8px 0 0; 1043 | @steps-dot-size: 8px; 1044 | @steps-dot-top: 2px; 1045 | @steps-current-dot-size: 10px; 1046 | @steps-description-max-width: 140px; 1047 | @steps-nav-content-max-width: auto; 1048 | @steps-vertical-icon-width: 16px; 1049 | @steps-vertical-tail-width: 16px; 1050 | @steps-vertical-tail-width-sm: 12px; 1051 | 1052 | // Notification 1053 | // --- 1054 | @notification-bg: @component-background; 1055 | @notification-padding-vertical: 16px; 1056 | @notification-padding-horizontal: 24px; 1057 | 1058 | // Result 1059 | // --- 1060 | @result-title-font-size: 24px; 1061 | @result-subtitle-font-size: @font-size-base; 1062 | @result-icon-font-size: 72px; 1063 | @result-extra-margin: 24px 0 0 0; 1064 | 1065 | // Image 1066 | // --- 1067 | @image-size-base: 48px; 1068 | @image-font-size-base: 24px; 1069 | @image-bg: #f5f5f5; 1070 | @image-color: #fff; 1071 | @image-mask-font-size: 16px; 1072 | @image-preview-operation-size: 18px; 1073 | @image-preview-operation-color: @text-color-dark; 1074 | @image-preview-operation-disabled-color: fade(@image-preview-operation-color, 25%); 1075 | 1076 | // Segmented 1077 | // --- 1078 | @segmented-bg: fade(@black, 4%); 1079 | @segmented-hover-bg: fade(@black, 6%); 1080 | @segmented-selected-bg: @white; 1081 | @segmented-label-color: fade(@black, 65%); 1082 | @segmented-label-hover-color: #262626; 1083 | -------------------------------------------------------------------------------- /src/assets/style/index.less: -------------------------------------------------------------------------------- 1 | @import url('common'); 2 | -------------------------------------------------------------------------------- /src/components/HeaderDropdown/index.module.less: -------------------------------------------------------------------------------- 1 | @import url('../../assets/style/default.less'); 2 | .container > * { 3 | background-color: #ffffff; 4 | border-radius: 4px; 5 | box-shadow: @shadow-1-down; 6 | } 7 | 8 | @media screen and (max-width: @screen-xs) { 9 | .container { 10 | width: 100% !important; 11 | } 12 | .container > * { 13 | border-radius: 0 !important; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/HeaderDropdown/index.tsx: -------------------------------------------------------------------------------- 1 | import { Dropdown } from 'antd' 2 | import type { DropDownProps } from 'antd/es/dropdown' 3 | import classNames from 'classnames' 4 | import React from 'react' 5 | import styles from './index.module.less' 6 | 7 | export type HeaderDropdownProps = { 8 | overlayClassName?: string 9 | overlay: React.ReactNode | (() => React.ReactNode) | any 10 | placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter' 11 | } & Omit 12 | 13 | const HeaderDropdown: React.FC = ({ overlayClassName: cls, ...restProps }) => ( 14 | 15 | ) 16 | 17 | export default HeaderDropdown 18 | -------------------------------------------------------------------------------- /src/components/Image/index.tsx: -------------------------------------------------------------------------------- 1 | import { useInViewport } from 'ahooks' 2 | import React, { useEffect, useRef, useState } from 'react' 3 | import defaultImg from '@img/default_goods.png' 4 | 5 | const Index = (props: IndexProps) => { 6 | const [url, setUrl] = useState(props.url) 7 | const { className, lazy = false, onClick = () => {} } = props 8 | 9 | let imgRef = undefined 10 | let inViewPort: readonly [boolean | undefined, number | undefined] = [false, 0] 11 | if (lazy) { 12 | imgRef = useRef(null) 13 | inViewPort = useInViewport(imgRef) 14 | } 15 | 16 | useEffect(() => { 17 | setUrl(props.url) 18 | }, [props.url]) 19 | 20 | return ( 21 | onClick()} 24 | onError={() => setUrl(defaultImg)} 25 | className={className} 26 | src={inViewPort[0] ? url : defaultImg} 27 | /> 28 | ) 29 | } 30 | 31 | export default Index 32 | 33 | type IndexProps = { 34 | url: string 35 | lazy?: boolean 36 | onClick?: Function 37 | className: string 38 | } 39 | -------------------------------------------------------------------------------- /src/components/NoticeIcon/NoticeIcon.tsx: -------------------------------------------------------------------------------- 1 | import { Badge, Spin, Tabs } from 'antd' 2 | import classNames from 'classnames' 3 | import useMergedState from 'rc-util/es/hooks/useMergedState' 4 | import React from 'react' 5 | import { BellOutlined } from '@ant-design/icons' 6 | import HeaderDropdown from '../HeaderDropdown' 7 | import type { NoticeIconTabProps } from './NoticeList' 8 | import NoticeList from './NoticeList' 9 | import styles from './index.module.less' 10 | 11 | const { TabPane } = Tabs 12 | 13 | export type NoticeIconProps = { 14 | count?: number 15 | bell?: React.ReactNode 16 | className?: string 17 | loading?: boolean 18 | onClear?: (tabName: string, tabKey: string) => void 19 | onItemClick?: (item: API.NoticeIconItem, tabProps: NoticeIconTabProps) => void 20 | onViewMore?: (tabProps: NoticeIconTabProps, e: MouseEvent) => void 21 | onTabChange?: (tabTile: string) => void 22 | style?: React.CSSProperties 23 | onPopupVisibleChange?: (visible: boolean) => void 24 | popupVisible?: boolean 25 | clearText?: string 26 | viewMoreText?: string 27 | clearClose?: boolean 28 | emptyImage?: string 29 | children?: React.ReactElement[] 30 | } 31 | 32 | const NoticeIcon: React.FC & { 33 | Tab: typeof NoticeList 34 | } = props => { 35 | const getNotificationBox = (): React.ReactNode => { 36 | const { children, loading, onClear, onTabChange, onItemClick, onViewMore, clearText, viewMoreText } = props 37 | if (!children) { 38 | return null 39 | } 40 | const panes: React.ReactNode[] = [] 41 | React.Children.forEach(children, (child: React.ReactElement): void => { 42 | if (!child) { 43 | return 44 | } 45 | const { list, title, count, tabKey, showClear, showViewMore } = child.props 46 | const len = list && list.length ? list.length : 0 47 | const msgCount = count || count === 0 ? count : len 48 | const tabTitle: string = msgCount > 0 ? `${title} (${msgCount})` : title 49 | panes.push( 50 | 51 | onClear && onClear(title, tabKey)} 57 | onClick={(item): void => onItemClick && onItemClick(item, child.props)} 58 | onViewMore={(event): void => onViewMore && onViewMore(child.props, event)} 59 | showClear={showClear} 60 | showViewMore={showViewMore} 61 | title={title} 62 | /> 63 | 64 | ) 65 | }) 66 | return ( 67 | <> 68 | 69 | 70 | {panes} 71 | 72 | 73 | 74 | ) 75 | } 76 | 77 | const { className, count, bell } = props 78 | 79 | const [visible, setVisible] = useMergedState(false, { 80 | value: props.popupVisible, 81 | onChange: props.onPopupVisibleChange 82 | }) 83 | const noticeButtonClass = classNames(className, styles.noticeButton) 84 | const notificationBox = getNotificationBox() 85 | const NoticeBellIcon = bell || 86 | const trigger = ( 87 | 88 | 89 | {NoticeBellIcon} 90 | 91 | 92 | ) 93 | if (!notificationBox) { 94 | return trigger 95 | } 96 | 97 | return ( 98 | 107 | {trigger} 108 | 109 | ) 110 | } 111 | 112 | NoticeIcon.defaultProps = { 113 | emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg' 114 | } 115 | 116 | NoticeIcon.Tab = NoticeList 117 | 118 | export default NoticeIcon 119 | -------------------------------------------------------------------------------- /src/components/NoticeIcon/NoticeList.module.less: -------------------------------------------------------------------------------- 1 | @import url('../../assets/style/default.less'); 2 | .list { 3 | max-height: 400px; 4 | overflow: auto; 5 | &::-webkit-scrollbar { 6 | display: none; 7 | } 8 | .item { 9 | padding-right: 24px; 10 | padding-left: 24px; 11 | overflow: hidden; 12 | cursor: pointer; 13 | transition: all 0.3s; 14 | .meta { 15 | width: 100%; 16 | } 17 | .avatar { 18 | margin-top: 4px; 19 | background: @component-background; 20 | } 21 | .iconElement { 22 | font-size: 32px; 23 | } 24 | &.read { 25 | opacity: 0.4; 26 | } 27 | &:last-child { 28 | border-bottom: 0; 29 | } 30 | &:hover { 31 | background: @primary-1; 32 | } 33 | .title { 34 | margin-bottom: 8px; 35 | font-weight: normal; 36 | } 37 | .description { 38 | font-size: 12px; 39 | line-height: @line-height-base; 40 | } 41 | .datetime { 42 | margin-top: 4px; 43 | font-size: 12px; 44 | line-height: @line-height-base; 45 | } 46 | .extra { 47 | float: right; 48 | margin-top: -1.5px; 49 | margin-right: 0; 50 | font-weight: normal; 51 | color: @text-color-secondary; 52 | } 53 | } 54 | .loadMore { 55 | padding: 8px 0; 56 | color: @primary-6; 57 | text-align: center; 58 | cursor: pointer; 59 | &.loadedAll { 60 | color: rgb(0 0 0 / 25%); 61 | cursor: unset; 62 | } 63 | } 64 | } 65 | .notFound { 66 | padding: 73px 0 88px; 67 | color: @text-color-secondary; 68 | text-align: center; 69 | img { 70 | display: inline-block; 71 | height: 76px; 72 | margin-bottom: 16px; 73 | } 74 | } 75 | .bottomBar { 76 | height: 46px; 77 | line-height: 46px; 78 | color: @text-color; 79 | text-align: center; 80 | border-top: 1px solid @border-color-split; 81 | border-radius: 0 0 @border-radius-base @border-radius-base; 82 | transition: all 0.3s; 83 | div { 84 | display: inline-block; 85 | width: 50%; 86 | cursor: pointer; 87 | user-select: none; 88 | transition: all 0.3s; 89 | &:only-child { 90 | width: 100%; 91 | } 92 | &:not(:only-child):last-child { 93 | border-left: 1px solid @border-color-split; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/components/NoticeIcon/NoticeList.tsx: -------------------------------------------------------------------------------- 1 | import { Avatar, List } from 'antd' 2 | import classNames from 'classnames' 3 | import React from 'react' 4 | import styles from './NoticeList.module.less' 5 | 6 | export type NoticeIconTabProps = { 7 | count?: number 8 | showClear?: boolean 9 | showViewMore?: boolean 10 | style?: React.CSSProperties 11 | title: string 12 | tabKey: API.NoticeIconItemType 13 | onClick?: (item: API.NoticeIconItem) => void 14 | onClear?: () => void 15 | emptyText?: string 16 | clearText?: string 17 | viewMoreText?: string 18 | list: API.NoticeIconItem[] 19 | onViewMore?: (e: any) => void 20 | } 21 | const NoticeList: React.FC = ({ 22 | list = [], 23 | onClick, 24 | onClear, 25 | title, 26 | onViewMore, 27 | emptyText, 28 | showClear = true, 29 | clearText, 30 | viewMoreText, 31 | showViewMore = false 32 | }) => { 33 | if (!list || list.length === 0) { 34 | return ( 35 |
36 | not found 37 |
{emptyText}
38 |
39 | ) 40 | } 41 | return ( 42 |
43 | 44 | className={styles.list} 45 | dataSource={list} 46 | renderItem={(item, i) => { 47 | const itemCls = classNames(styles.item, { 48 | [styles.read]: item.read 49 | }) 50 | // eslint-disable-next-line no-nested-ternary 51 | const leftIcon = item.avatar ? ( 52 | typeof item.avatar === 'string' ? ( 53 | 54 | ) : ( 55 | {item.avatar} 56 | ) 57 | ) : null 58 | 59 | return ( 60 | { 64 | onClick?.(item) 65 | }} 66 | > 67 | 72 | {item.title} 73 |
{item.extra}
74 |
75 | } 76 | description={ 77 |
78 |
{item.description}
79 |
{item.datetime}
80 |
81 | } 82 | /> 83 | 84 | ) 85 | }} 86 | /> 87 |
88 | {showClear ? ( 89 |
90 | {clearText} {title} 91 |
92 | ) : null} 93 | {showViewMore ? ( 94 |
{ 96 | if (onViewMore) { 97 | onViewMore(e) 98 | } 99 | }} 100 | > 101 | {viewMoreText} 102 |
103 | ) : null} 104 |
105 | 106 | ) 107 | } 108 | 109 | export default NoticeList 110 | -------------------------------------------------------------------------------- /src/components/NoticeIcon/index.module.less: -------------------------------------------------------------------------------- 1 | @import url('../../assets/style/default.less'); 2 | .popover { 3 | position: relative; 4 | top: 75px !important; 5 | width: 336px; 6 | } 7 | .noticeButton { 8 | display: inline-block; 9 | cursor: pointer; 10 | transition: all 0.3s; 11 | } 12 | .icon { 13 | padding: 4px; 14 | vertical-align: middle; 15 | } 16 | .badge { 17 | font-size: 16px; 18 | } 19 | .tabs { 20 | :global { 21 | .ant-tabs-nav-list { 22 | margin: auto; 23 | } 24 | .ant-tabs-nav-scroll { 25 | text-align: center; 26 | } 27 | .ant-tabs-bar { 28 | margin-bottom: 0; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | import Image from './Image' 2 | import NoticeIcon from './NoticeIcon/NoticeIcon' 3 | 4 | export { Image, NoticeIcon } 5 | -------------------------------------------------------------------------------- /src/components/style/bezierEasing.less: -------------------------------------------------------------------------------- 1 | /* stylelint-disable */ 2 | .bezierEasingMixin() { 3 | @functions: ~`(function() { 4 | var NEWTON_ITERATIONS = 4; 5 | var NEWTON_MIN_SLOPE = 0.001; 6 | var SUBDIVISION_PRECISION = 0.0000001; 7 | var SUBDIVISION_MAX_ITERATIONS = 10; 8 | 9 | var kSplineTableSize = 11; 10 | var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); 11 | 12 | var float32ArraySupported = typeof Float32Array === 'function'; 13 | 14 | function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } 15 | function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } 16 | function C (aA1) { return 3.0 * aA1; } 17 | 18 | // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. 19 | function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } 20 | 21 | // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. 22 | function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } 23 | 24 | function binarySubdivide (aX, aA, aB, mX1, mX2) { 25 | var currentX, currentT, i = 0; 26 | do { 27 | currentT = aA + (aB - aA) / 2.0; 28 | currentX = calcBezier(currentT, mX1, mX2) - aX; 29 | if (currentX > 0.0) { 30 | aB = currentT; 31 | } else { 32 | aA = currentT; 33 | } 34 | } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); 35 | return currentT; 36 | } 37 | 38 | function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { 39 | for (var i = 0; i < NEWTON_ITERATIONS; ++i) { 40 | var currentSlope = getSlope(aGuessT, mX1, mX2); 41 | if (currentSlope === 0.0) { 42 | return aGuessT; 43 | } 44 | var currentX = calcBezier(aGuessT, mX1, mX2) - aX; 45 | aGuessT -= currentX / currentSlope; 46 | } 47 | return aGuessT; 48 | } 49 | 50 | var BezierEasing = function (mX1, mY1, mX2, mY2) { 51 | if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { 52 | throw new Error('bezier x values must be in [0, 1] range'); 53 | } 54 | 55 | // Precompute samples table 56 | var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); 57 | if (mX1 !== mY1 || mX2 !== mY2) { 58 | for (var i = 0; i < kSplineTableSize; ++i) { 59 | sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); 60 | } 61 | } 62 | 63 | function getTForX (aX) { 64 | var intervalStart = 0.0; 65 | var currentSample = 1; 66 | var lastSample = kSplineTableSize - 1; 67 | 68 | for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { 69 | intervalStart += kSampleStepSize; 70 | } 71 | --currentSample; 72 | 73 | // Interpolate to provide an initial guess for t 74 | var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); 75 | var guessForT = intervalStart + dist * kSampleStepSize; 76 | 77 | var initialSlope = getSlope(guessForT, mX1, mX2); 78 | if (initialSlope >= NEWTON_MIN_SLOPE) { 79 | return newtonRaphsonIterate(aX, guessForT, mX1, mX2); 80 | } else if (initialSlope === 0.0) { 81 | return guessForT; 82 | } else { 83 | return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); 84 | } 85 | } 86 | 87 | return function BezierEasing (x) { 88 | if (mX1 === mY1 && mX2 === mY2) { 89 | return x; // linear 90 | } 91 | // Because JavaScript number are imprecise, we should guarantee the extremes are right. 92 | if (x === 0) { 93 | return 0; 94 | } 95 | if (x === 1) { 96 | return 1; 97 | } 98 | return calcBezier(getTForX(x), mY1, mY2); 99 | }; 100 | }; 101 | 102 | this.colorEasing = BezierEasing(0.26, 0.09, 0.37, 0.18); 103 | // less 3 requires a return 104 | return ''; 105 | })()`; 106 | } 107 | // It is hacky way to make this function will be compiled preferentially by less 108 | // resolve error: `ReferenceError: colorPalette is not defined` 109 | // https://github.com/ant-design/ant-motion/issues/44 110 | .bezierEasingMixin(); 111 | -------------------------------------------------------------------------------- /src/components/style/colorPalette.less: -------------------------------------------------------------------------------- 1 | /* stylelint-disable no-duplicate-selectors */ 2 | @import url("bezierEasing"); 3 | @import url("tinyColor"); 4 | 5 | // We create a very complex algorithm which take the place of original tint/shade color system 6 | // to make sure no one can understand it 👻 7 | // and create an entire color palette magicly by inputing just a single primary color. 8 | // We are using bezier-curve easing function and some color manipulations like tint/shade/darken/spin 9 | .colorPaletteMixin() { 10 | @functions: ~`(function() { 11 | var hueStep = 2; 12 | var saturationStep = 0.16; 13 | var saturationStep2 = 0.05; 14 | var brightnessStep1 = 0.05; 15 | var brightnessStep2 = 0.15; 16 | var lightColorCount = 5; 17 | var darkColorCount = 4; 18 | 19 | var getHue = function(hsv, i, isLight) { 20 | var hue; 21 | if (hsv.h >= 60 && hsv.h <= 240) { 22 | hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i; 23 | } else { 24 | hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i; 25 | } 26 | if (hue < 0) { 27 | hue += 360; 28 | } else if (hue >= 360) { 29 | hue -= 360; 30 | } 31 | return Math.round(hue); 32 | }; 33 | var getSaturation = function(hsv, i, isLight) { 34 | if (hsv.h === 0 && hsv.s === 0) { 35 | return hsv.s; 36 | } 37 | var saturation; 38 | if (isLight) { 39 | saturation = hsv.s - saturationStep * i; 40 | } else if (i === darkColorCount) { 41 | saturation = hsv.s + saturationStep; 42 | } else { 43 | saturation = hsv.s + saturationStep2 * i; 44 | } 45 | if (saturation > 1) { 46 | saturation = 1; 47 | } 48 | if (isLight && i === lightColorCount && saturation > 0.1) { 49 | saturation = 0.1; 50 | } 51 | if (saturation < 0.06) { 52 | saturation = 0.06; 53 | } 54 | return Number(saturation.toFixed(2)); 55 | }; 56 | var getValue = function(hsv, i, isLight) { 57 | var value; 58 | if (isLight) { 59 | value = hsv.v + brightnessStep1 * i; 60 | }else{ 61 | value = hsv.v - brightnessStep2 * i 62 | } 63 | if (value > 1) { 64 | value = 1; 65 | } 66 | return Number(value.toFixed(2)) 67 | }; 68 | 69 | this.colorPalette = function(color, index) { 70 | var isLight = index <= 6; 71 | var hsv = tinycolor(color).toHsv(); 72 | var i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1; 73 | return tinycolor({ 74 | h: getHue(hsv, i, isLight), 75 | s: getSaturation(hsv, i, isLight), 76 | v: getValue(hsv, i, isLight), 77 | }).toHexString(); 78 | }; 79 | })()`; 80 | } 81 | // It is hacky way to make this function will be compiled preferentially by less 82 | // resolve error: `ReferenceError: colorPalette is not defined` 83 | // https://github.com/ant-design/ant-motion/issues/44 84 | .colorPaletteMixin(); 85 | -------------------------------------------------------------------------------- /src/components/style/colors.less: -------------------------------------------------------------------------------- 1 | @import url('colorPalette'); 2 | 3 | // color palettes 4 | @blue-base: #1890ff; 5 | @blue-1: color(~`colorPalette('@{blue-6}', 1) `); 6 | @blue-2: color(~`colorPalette('@{blue-6}', 2) `); 7 | @blue-3: color(~`colorPalette('@{blue-6}', 3) `); 8 | @blue-4: color(~`colorPalette('@{blue-6}', 4) `); 9 | @blue-5: color(~`colorPalette('@{blue-6}', 5) `); 10 | @blue-6: @blue-base; 11 | @blue-7: color(~`colorPalette('@{blue-6}', 7) `); 12 | @blue-8: color(~`colorPalette('@{blue-6}', 8) `); 13 | @blue-9: color(~`colorPalette('@{blue-6}', 9) `); 14 | @blue-10: color(~`colorPalette('@{blue-6}', 10) `); 15 | 16 | @purple-base: #722ed1; 17 | @purple-1: color(~`colorPalette('@{purple-6}', 1) `); 18 | @purple-2: color(~`colorPalette('@{purple-6}', 2) `); 19 | @purple-3: color(~`colorPalette('@{purple-6}', 3) `); 20 | @purple-4: color(~`colorPalette('@{purple-6}', 4) `); 21 | @purple-5: color(~`colorPalette('@{purple-6}', 5) `); 22 | @purple-6: @purple-base; 23 | @purple-7: color(~`colorPalette('@{purple-6}', 7) `); 24 | @purple-8: color(~`colorPalette('@{purple-6}', 8) `); 25 | @purple-9: color(~`colorPalette('@{purple-6}', 9) `); 26 | @purple-10: color(~`colorPalette('@{purple-6}', 10) `); 27 | 28 | @cyan-base: #13c2c2; 29 | @cyan-1: color(~`colorPalette('@{cyan-6}', 1) `); 30 | @cyan-2: color(~`colorPalette('@{cyan-6}', 2) `); 31 | @cyan-3: color(~`colorPalette('@{cyan-6}', 3) `); 32 | @cyan-4: color(~`colorPalette('@{cyan-6}', 4) `); 33 | @cyan-5: color(~`colorPalette('@{cyan-6}', 5) `); 34 | @cyan-6: @cyan-base; 35 | @cyan-7: color(~`colorPalette('@{cyan-6}', 7) `); 36 | @cyan-8: color(~`colorPalette('@{cyan-6}', 8) `); 37 | @cyan-9: color(~`colorPalette('@{cyan-6}', 9) `); 38 | @cyan-10: color(~`colorPalette('@{cyan-6}', 10) `); 39 | 40 | @green-base: #52c41a; 41 | @green-1: color(~`colorPalette('@{green-6}', 1) `); 42 | @green-2: color(~`colorPalette('@{green-6}', 2) `); 43 | @green-3: color(~`colorPalette('@{green-6}', 3) `); 44 | @green-4: color(~`colorPalette('@{green-6}', 4) `); 45 | @green-5: color(~`colorPalette('@{green-6}', 5) `); 46 | @green-6: @green-base; 47 | @green-7: color(~`colorPalette('@{green-6}', 7) `); 48 | @green-8: color(~`colorPalette('@{green-6}', 8) `); 49 | @green-9: color(~`colorPalette('@{green-6}', 9) `); 50 | @green-10: color(~`colorPalette('@{green-6}', 10) `); 51 | 52 | @magenta-base: #eb2f96; 53 | @magenta-1: color(~`colorPalette('@{magenta-6}', 1) `); 54 | @magenta-2: color(~`colorPalette('@{magenta-6}', 2) `); 55 | @magenta-3: color(~`colorPalette('@{magenta-6}', 3) `); 56 | @magenta-4: color(~`colorPalette('@{magenta-6}', 4) `); 57 | @magenta-5: color(~`colorPalette('@{magenta-6}', 5) `); 58 | @magenta-6: @magenta-base; 59 | @magenta-7: color(~`colorPalette('@{magenta-6}', 7) `); 60 | @magenta-8: color(~`colorPalette('@{magenta-6}', 8) `); 61 | @magenta-9: color(~`colorPalette('@{magenta-6}', 9) `); 62 | @magenta-10: color(~`colorPalette('@{magenta-6}', 10) `); 63 | 64 | // alias of magenta 65 | @pink-base: #eb2f96; 66 | @pink-1: color(~`colorPalette('@{pink-6}', 1) `); 67 | @pink-2: color(~`colorPalette('@{pink-6}', 2) `); 68 | @pink-3: color(~`colorPalette('@{pink-6}', 3) `); 69 | @pink-4: color(~`colorPalette('@{pink-6}', 4) `); 70 | @pink-5: color(~`colorPalette('@{pink-6}', 5) `); 71 | @pink-6: @pink-base; 72 | @pink-7: color(~`colorPalette('@{pink-6}', 7) `); 73 | @pink-8: color(~`colorPalette('@{pink-6}', 8) `); 74 | @pink-9: color(~`colorPalette('@{pink-6}', 9) `); 75 | @pink-10: color(~`colorPalette('@{pink-6}', 10) `); 76 | 77 | @red-base: #f5222d; 78 | @red-1: color(~`colorPalette('@{red-6}', 1) `); 79 | @red-2: color(~`colorPalette('@{red-6}', 2) `); 80 | @red-3: color(~`colorPalette('@{red-6}', 3) `); 81 | @red-4: color(~`colorPalette('@{red-6}', 4) `); 82 | @red-5: color(~`colorPalette('@{red-6}', 5) `); 83 | @red-6: @red-base; 84 | @red-7: color(~`colorPalette('@{red-6}', 7) `); 85 | @red-8: color(~`colorPalette('@{red-6}', 8) `); 86 | @red-9: color(~`colorPalette('@{red-6}', 9) `); 87 | @red-10: color(~`colorPalette('@{red-6}', 10) `); 88 | 89 | @orange-base: #fa8c16; 90 | @orange-1: color(~`colorPalette('@{orange-6}', 1) `); 91 | @orange-2: color(~`colorPalette('@{orange-6}', 2) `); 92 | @orange-3: color(~`colorPalette('@{orange-6}', 3) `); 93 | @orange-4: color(~`colorPalette('@{orange-6}', 4) `); 94 | @orange-5: color(~`colorPalette('@{orange-6}', 5) `); 95 | @orange-6: @orange-base; 96 | @orange-7: color(~`colorPalette('@{orange-6}', 7) `); 97 | @orange-8: color(~`colorPalette('@{orange-6}', 8) `); 98 | @orange-9: color(~`colorPalette('@{orange-6}', 9) `); 99 | @orange-10: color(~`colorPalette('@{orange-6}', 10) `); 100 | 101 | @yellow-base: #fadb14; 102 | @yellow-1: color(~`colorPalette('@{yellow-6}', 1) `); 103 | @yellow-2: color(~`colorPalette('@{yellow-6}', 2) `); 104 | @yellow-3: color(~`colorPalette('@{yellow-6}', 3) `); 105 | @yellow-4: color(~`colorPalette('@{yellow-6}', 4) `); 106 | @yellow-5: color(~`colorPalette('@{yellow-6}', 5) `); 107 | @yellow-6: @yellow-base; 108 | @yellow-7: color(~`colorPalette('@{yellow-6}', 7) `); 109 | @yellow-8: color(~`colorPalette('@{yellow-6}', 8) `); 110 | @yellow-9: color(~`colorPalette('@{yellow-6}', 9) `); 111 | @yellow-10: color(~`colorPalette('@{yellow-6}', 10) `); 112 | 113 | @volcano-base: #fa541c; 114 | @volcano-1: color(~`colorPalette('@{volcano-6}', 1) `); 115 | @volcano-2: color(~`colorPalette('@{volcano-6}', 2) `); 116 | @volcano-3: color(~`colorPalette('@{volcano-6}', 3) `); 117 | @volcano-4: color(~`colorPalette('@{volcano-6}', 4) `); 118 | @volcano-5: color(~`colorPalette('@{volcano-6}', 5) `); 119 | @volcano-6: @volcano-base; 120 | @volcano-7: color(~`colorPalette('@{volcano-6}', 7) `); 121 | @volcano-8: color(~`colorPalette('@{volcano-6}', 8) `); 122 | @volcano-9: color(~`colorPalette('@{volcano-6}', 9) `); 123 | @volcano-10: color(~`colorPalette('@{volcano-6}', 10) `); 124 | 125 | @geekblue-base: #2f54eb; 126 | @geekblue-1: color(~`colorPalette('@{geekblue-6}', 1) `); 127 | @geekblue-2: color(~`colorPalette('@{geekblue-6}', 2) `); 128 | @geekblue-3: color(~`colorPalette('@{geekblue-6}', 3) `); 129 | @geekblue-4: color(~`colorPalette('@{geekblue-6}', 4) `); 130 | @geekblue-5: color(~`colorPalette('@{geekblue-6}', 5) `); 131 | @geekblue-6: @geekblue-base; 132 | @geekblue-7: color(~`colorPalette('@{geekblue-6}', 7) `); 133 | @geekblue-8: color(~`colorPalette('@{geekblue-6}', 8) `); 134 | @geekblue-9: color(~`colorPalette('@{geekblue-6}', 9) `); 135 | @geekblue-10: color(~`colorPalette('@{geekblue-6}', 10) `); 136 | 137 | @lime-base: #a0d911; 138 | @lime-1: color(~`colorPalette('@{lime-6}', 1) `); 139 | @lime-2: color(~`colorPalette('@{lime-6}', 2) `); 140 | @lime-3: color(~`colorPalette('@{lime-6}', 3) `); 141 | @lime-4: color(~`colorPalette('@{lime-6}', 4) `); 142 | @lime-5: color(~`colorPalette('@{lime-6}', 5) `); 143 | @lime-6: @lime-base; 144 | @lime-7: color(~`colorPalette('@{lime-6}', 7) `); 145 | @lime-8: color(~`colorPalette('@{lime-6}', 8) `); 146 | @lime-9: color(~`colorPalette('@{lime-6}', 9) `); 147 | @lime-10: color(~`colorPalette('@{lime-6}', 10) `); 148 | 149 | @gold-base: #faad14; 150 | @gold-1: color(~`colorPalette('@{gold-6}', 1) `); 151 | @gold-2: color(~`colorPalette('@{gold-6}', 2) `); 152 | @gold-3: color(~`colorPalette('@{gold-6}', 3) `); 153 | @gold-4: color(~`colorPalette('@{gold-6}', 4) `); 154 | @gold-5: color(~`colorPalette('@{gold-6}', 5) `); 155 | @gold-6: @gold-base; 156 | @gold-7: color(~`colorPalette('@{gold-6}', 7) `); 157 | @gold-8: color(~`colorPalette('@{gold-6}', 8) `); 158 | @gold-9: color(~`colorPalette('@{gold-6}', 9) `); 159 | @gold-10: color(~`colorPalette('@{gold-6}', 10) `); 160 | 161 | @preset-colors: pink, magenta, red, volcano, orange, yellow, gold, cyan, lime, green, blue, geekblue, purple; 162 | -------------------------------------------------------------------------------- /src/components/style/default.less: -------------------------------------------------------------------------------- 1 | /* stylelint-disable at-rule-empty-line-before,at-rule-name-space-after,at-rule-no-unknown */ 2 | @import url('colors'); 3 | 4 | @theme: default; 5 | 6 | // The prefix to use on all css classes from ant. 7 | @ant-prefix: ant; 8 | 9 | // An override for the html selector for theme prefixes 10 | @html-selector: html; 11 | 12 | // [CSS-VARIABLE-REPLACE-BEGIN: html-variables] 13 | // [CSS-VARIABLE-REPLACE-END: html-variables] 14 | 15 | // -------- Colors ----------- 16 | // >>> Primary 17 | @primary-color: @blue-6; 18 | @primary-color-hover: color(~`colorPalette('@{primary-color}', 5) `); 19 | @primary-color-active: color(~`colorPalette('@{primary-color}', 7) `); 20 | @primary-color-outline: fade(@primary-color, @outline-fade); 21 | 22 | @processing-color: @blue-6; 23 | 24 | // >>> Info 25 | @info-color: @primary-color; 26 | @info-color-deprecated-bg: color(~`colorPalette('@{info-color}', 1) `); 27 | @info-color-deprecated-border: color(~`colorPalette('@{info-color}', 3) `); 28 | 29 | // >>> Success 30 | @success-color: @green-6; 31 | @success-color-hover: color(~`colorPalette('@{success-color}', 5) `); 32 | @success-color-active: color(~`colorPalette('@{success-color}', 7) `); 33 | @success-color-outline: fade(@success-color, @outline-fade); 34 | @success-color-deprecated-bg: color(~`colorPalette('@{success-color}', 1) `); 35 | @success-color-deprecated-border: color(~`colorPalette('@{success-color}', 3) `); 36 | 37 | // >>> Warning 38 | @warning-color: @gold-6; 39 | @warning-color-hover: color(~`colorPalette('@{warning-color}', 5) `); 40 | @warning-color-active: color(~`colorPalette('@{warning-color}', 7) `); 41 | @warning-color-outline: fade(@warning-color, @outline-fade); 42 | @warning-color-deprecated-bg: color(~`colorPalette('@{warning-color}', 1) `); 43 | @warning-color-deprecated-border: color(~`colorPalette('@{warning-color}', 3) `); 44 | 45 | // >>> Error 46 | @error-color: @red-5; 47 | @error-color-hover: color(~`colorPalette('@{error-color}', 5) `); 48 | @error-color-active: color(~`colorPalette('@{error-color}', 7) `); 49 | @error-color-outline: fade(@error-color, @outline-fade); 50 | @error-color-deprecated-bg: color(~`colorPalette('@{error-color}', 1) `); 51 | @error-color-deprecated-border: color(~`colorPalette('@{error-color}', 3) `); 52 | 53 | @highlight-color: @red-5; 54 | @normal-color: #d9d9d9; 55 | @white: #fff; 56 | @black: #000; 57 | 58 | // Color used by default to control hover and active backgrounds and for 59 | // alert info backgrounds. 60 | @primary-1: color(~`colorPalette('@{primary-color}', 1) `); // replace tint(@primary-color, 90%) 61 | @primary-2: color(~`colorPalette('@{primary-color}', 2) `); // replace tint(@primary-color, 80%) 62 | @primary-3: color(~`colorPalette('@{primary-color}', 3) `); // unused 63 | @primary-4: color(~`colorPalette('@{primary-color}', 4) `); // unused 64 | @primary-5: color( 65 | ~`colorPalette('@{primary-color}', 5) ` 66 | ); // color used to control the text color in many active and hover states, replace tint(@primary-color, 20%) 67 | @primary-6: @primary-color; // color used to control the text color of active buttons, don't use, use @primary-color 68 | @primary-7: color(~`colorPalette('@{primary-color}', 7) `); // replace shade(@primary-color, 5%) 69 | @primary-8: color(~`colorPalette('@{primary-color}', 8) `); // unused 70 | @primary-9: color(~`colorPalette('@{primary-color}', 9) `); // unused 71 | @primary-10: color(~`colorPalette('@{primary-color}', 10) `); // unused 72 | 73 | // Base Scaffolding Variables 74 | // --- 75 | 76 | // Background color for `` 77 | @body-background: #fff; 78 | // Base background color for most components 79 | @component-background: #fff; 80 | // Popover background color 81 | @popover-background: @component-background; 82 | @popover-customize-border-color: @border-color-split; 83 | @font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 84 | 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; 85 | @code-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; 86 | @text-color: fade(@black, 85%); 87 | @text-color-secondary: fade(@black, 45%); 88 | @text-color-inverse: @white; 89 | @icon-color: inherit; 90 | @icon-color-hover: fade(@black, 75%); 91 | @heading-color: fade(@black, 85%); 92 | @text-color-dark: fade(@white, 85%); 93 | @text-color-secondary-dark: fade(@white, 65%); 94 | @text-selection-bg: @primary-color; 95 | @font-variant-base: tabular-nums; 96 | @font-feature-settings-base: 'tnum'; 97 | @font-size-base: 14px; 98 | @font-size-lg: @font-size-base + 2px; 99 | @font-size-sm: 12px; 100 | @heading-1-size: ceil(@font-size-base * 2.71); 101 | @heading-2-size: ceil(@font-size-base * 2.14); 102 | @heading-3-size: ceil(@font-size-base * 1.71); 103 | @heading-4-size: ceil(@font-size-base * 1.42); 104 | @heading-5-size: ceil(@font-size-base * 1.14); 105 | // https://github.com/ant-design/ant-design/issues/20210 106 | @line-height-base: 1.5715; 107 | @border-radius-base: 2px; 108 | @border-radius-sm: 2px; 109 | 110 | // control border 111 | @control-border-radius: @border-radius-base; 112 | 113 | // arrow border 114 | @arrow-border-radius: 2px; 115 | 116 | // vertical paddings 117 | @padding-lg: 24px; // containers 118 | @padding-md: 16px; // small containers and buttons 119 | @padding-sm: 12px; // Form controls and items 120 | @padding-xs: 8px; // small items 121 | @padding-xss: 4px; // more small 122 | 123 | // vertical padding for all form controls 124 | @control-padding-horizontal: @padding-sm; 125 | @control-padding-horizontal-sm: @padding-xs; 126 | 127 | // vertical margins 128 | @margin-lg: 24px; // containers 129 | @margin-md: 16px; // small containers and buttons 130 | @margin-sm: 12px; // Form controls and items 131 | @margin-xs: 8px; // small items 132 | @margin-xss: 4px; // more small 133 | 134 | // height rules 135 | @height-base: 32px; 136 | @height-lg: 40px; 137 | @height-sm: 24px; 138 | 139 | // The background colors for active and hover states for things like 140 | // list items or table cells. 141 | @item-active-bg: @primary-1; 142 | @item-hover-bg: #f5f5f5; 143 | 144 | // ICONFONT 145 | @iconfont-css-prefix: anticon; 146 | 147 | // LINK 148 | @link-color: @primary-color; 149 | @link-hover-color: color(~`colorPalette('@{link-color}', 5) `); 150 | @link-active-color: color(~`colorPalette('@{link-color}', 7) `); 151 | @link-decoration: none; 152 | @link-hover-decoration: none; 153 | @link-focus-decoration: none; 154 | @link-focus-outline: 0; 155 | 156 | // Animation 157 | @ease-base-out: cubic-bezier(0.7, 0.3, 0.1, 1); 158 | @ease-base-in: cubic-bezier(0.9, 0, 0.3, 0.7); 159 | @ease-out: cubic-bezier(0.215, 0.61, 0.355, 1); 160 | @ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19); 161 | @ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1); 162 | @ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46); 163 | @ease-in-back: cubic-bezier(0.71, -0.46, 0.88, 0.6); 164 | @ease-in-out-back: cubic-bezier(0.71, -0.46, 0.29, 1.46); 165 | @ease-out-circ: cubic-bezier(0.08, 0.82, 0.17, 1); 166 | @ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.34); 167 | @ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86); 168 | @ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1); 169 | @ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06); 170 | @ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1); 171 | 172 | // Border color 173 | @border-color-base: hsv(0, 0, 85%); // base border outline a component 174 | @border-color-split: hsv(0, 0, 94%); // split border inside a component 175 | @border-color-inverse: @white; 176 | @border-width-base: 1px; // width of the border for a component 177 | @border-style-base: solid; // style of a components border 178 | 179 | // Outline 180 | @outline-blur-size: 0; 181 | @outline-width: 2px; 182 | @outline-color: @primary-color; // No use anymore 183 | @outline-fade: 20%; 184 | 185 | @background-color-light: hsv(0, 0, 98%); // background of header and selected item 186 | @background-color-base: hsv(0, 0, 96%); // Default grey background color 187 | 188 | // Disabled states 189 | @disabled-color: fade(#000, 25%); 190 | @disabled-bg: @background-color-base; 191 | @disabled-active-bg: tint(@black, 90%); 192 | @disabled-color-dark: fade(#fff, 35%); 193 | 194 | // Shadow 195 | @shadow-color: rgba(0, 0, 0, 0.15); 196 | @shadow-color-inverse: @component-background; 197 | @box-shadow-base: @shadow-2; 198 | @shadow-1-up: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05), 199 | 0 -12px 48px 16px rgba(0, 0, 0, 0.03); 200 | @shadow-1-down: 0 6px 16px -8px rgba(0, 0, 0, 0.08), 0 9px 28px 0 rgba(0, 0, 0, 0.05), 201 | 0 12px 48px 16px rgba(0, 0, 0, 0.03); 202 | @shadow-1-left: -6px 0 16px -8px rgba(0, 0, 0, 0.08), -9px 0 28px 0 rgba(0, 0, 0, 0.05), 203 | -12px 0 48px 16px rgba(0, 0, 0, 0.03); 204 | @shadow-1-right: 6px 0 16px -8px rgba(0, 0, 0, 0.08), 9px 0 28px 0 rgba(0, 0, 0, 0.05), 205 | 12px 0 48px 16px rgba(0, 0, 0, 0.03); 206 | @shadow-2: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05); 207 | 208 | // Buttons 209 | @btn-font-weight: 400; 210 | @btn-border-radius-base: @border-radius-base; 211 | @btn-border-radius-sm: @border-radius-base; 212 | @btn-border-width: @border-width-base; 213 | @btn-border-style: @border-style-base; 214 | @btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); 215 | @btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); 216 | @btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); 217 | 218 | @btn-primary-color: #fff; 219 | @btn-primary-bg: @primary-color; 220 | 221 | @btn-default-color: @text-color; 222 | @btn-default-bg: @component-background; 223 | @btn-default-border: @border-color-base; 224 | 225 | @btn-danger-color: #fff; 226 | @btn-danger-bg: @error-color; 227 | @btn-danger-border: @error-color; 228 | 229 | @btn-disable-color: @disabled-color; 230 | @btn-disable-bg: @disabled-bg; 231 | @btn-disable-border: @border-color-base; 232 | 233 | @btn-default-ghost-color: @component-background; 234 | @btn-default-ghost-bg: transparent; 235 | @btn-default-ghost-border: @component-background; 236 | 237 | @btn-font-size-lg: @font-size-lg; 238 | @btn-font-size-sm: @font-size-base; 239 | @btn-padding-horizontal-base: @padding-md - 1px; 240 | @btn-padding-horizontal-lg: @btn-padding-horizontal-base; 241 | @btn-padding-horizontal-sm: @padding-xs - 1px; 242 | 243 | @btn-height-base: @height-base; 244 | @btn-height-lg: @height-lg; 245 | @btn-height-sm: @height-sm; 246 | 247 | @btn-line-height: @line-height-base; 248 | 249 | @btn-circle-size: @btn-height-base; 250 | @btn-circle-size-lg: @btn-height-lg; 251 | @btn-circle-size-sm: @btn-height-sm; 252 | 253 | @btn-square-size: @btn-height-base; 254 | @btn-square-size-lg: @btn-height-lg; 255 | @btn-square-size-sm: @btn-height-sm; 256 | @btn-square-only-icon-size: @font-size-base + 2px; 257 | @btn-square-only-icon-size-sm: @font-size-base; 258 | @btn-square-only-icon-size-lg: @btn-font-size-lg + 2px; 259 | 260 | @btn-group-border: @primary-5; 261 | 262 | @btn-link-hover-bg: transparent; 263 | @btn-text-hover-bg: rgba(0, 0, 0, 0.018); 264 | 265 | // Checkbox 266 | @checkbox-size: 16px; 267 | @checkbox-color: @primary-color; 268 | @checkbox-check-color: #fff; 269 | @checkbox-check-bg: @checkbox-check-color; 270 | @checkbox-border-width: @border-width-base; 271 | @checkbox-border-radius: @border-radius-base; 272 | @checkbox-group-item-margin-right: 8px; 273 | 274 | // Descriptions 275 | @descriptions-bg: #fafafa; 276 | @descriptions-title-margin-bottom: 20px; 277 | @descriptions-default-padding: @padding-md @padding-lg; 278 | @descriptions-middle-padding: @padding-sm @padding-lg; 279 | @descriptions-small-padding: @padding-xs @padding-md; 280 | @descriptions-item-padding-bottom: @padding-md; 281 | @descriptions-item-trailing-colon: true; 282 | @descriptions-item-label-colon-margin-right: 8px; 283 | @descriptions-item-label-colon-margin-left: 2px; 284 | @descriptions-extra-color: @text-color; 285 | 286 | // Divider 287 | @divider-text-padding: 1em; 288 | @divider-orientation-margin: 5%; 289 | @divider-color: rgba(0, 0, 0, 6%); 290 | @divider-vertical-gutter: 8px; 291 | 292 | // Dropdown 293 | @dropdown-selected-color: @primary-color; 294 | @dropdown-menu-submenu-disabled-bg: @component-background; 295 | @dropdown-selected-bg: @item-active-bg; 296 | 297 | // Empty 298 | @empty-font-size: @font-size-base; 299 | 300 | // Radio 301 | @radio-size: 16px; 302 | @radio-top: 0.2em; 303 | @radio-border-width: 1px; 304 | @radio-dot-size: @radio-size - 8px; 305 | @radio-dot-color: @primary-color; 306 | @radio-dot-disabled-color: fade(@black, 20%); 307 | @radio-solid-checked-color: @component-background; 308 | 309 | // Radio buttons 310 | @radio-button-bg: @btn-default-bg; 311 | @radio-button-checked-bg: @btn-default-bg; 312 | @radio-button-color: @btn-default-color; 313 | @radio-button-hover-color: @primary-5; 314 | @radio-button-active-color: @primary-7; 315 | @radio-button-padding-horizontal: @padding-md - 1px; 316 | @radio-disabled-button-checked-bg: @disabled-active-bg; 317 | @radio-disabled-button-checked-color: @disabled-color; 318 | @radio-wrapper-margin-right: 8px; 319 | 320 | // Media queries breakpoints 321 | // @screen-xs and @screen-xs-min is not used in Grid 322 | // smallest break point is @screen-md 323 | @screen-xs: 480px; 324 | @screen-xs-min: @screen-xs; 325 | // 👆 Extra small screen / phone 326 | 327 | // 👇 Small screen / tablet 328 | @screen-sm: 576px; 329 | @screen-sm-min: @screen-sm; 330 | 331 | // Medium screen / desktop 332 | @screen-md: 768px; 333 | @screen-md-min: @screen-md; 334 | 335 | // Large screen / wide desktop 336 | @screen-lg: 992px; 337 | @screen-lg-min: @screen-lg; 338 | 339 | // Extra large screen / full hd 340 | @screen-xl: 1200px; 341 | @screen-xl-min: @screen-xl; 342 | 343 | // Extra extra large screen / large desktop 344 | @screen-xxl: 1600px; 345 | @screen-xxl-min: @screen-xxl; 346 | 347 | // provide a maximum 348 | @screen-xs-max: (@screen-sm-min - 1px); 349 | @screen-sm-max: (@screen-md-min - 1px); 350 | @screen-md-max: (@screen-lg-min - 1px); 351 | @screen-lg-max: (@screen-xl-min - 1px); 352 | @screen-xl-max: (@screen-xxl-min - 1px); 353 | 354 | // Grid system 355 | @grid-columns: 24; 356 | 357 | // Layout 358 | @layout-body-background: #f0f2f5; 359 | @layout-header-background: #001529; 360 | @layout-header-height: 64px; 361 | @layout-header-padding: 0 50px; 362 | @layout-header-color: @text-color; 363 | @layout-footer-padding: 24px 50px; 364 | @layout-footer-background: @layout-body-background; 365 | @layout-sider-background: @layout-header-background; 366 | @layout-trigger-height: 48px; 367 | @layout-trigger-background: #002140; 368 | @layout-trigger-color: #fff; 369 | @layout-zero-trigger-width: 36px; 370 | @layout-zero-trigger-height: 42px; 371 | // Layout light theme 372 | @layout-sider-background-light: #fff; 373 | @layout-trigger-background-light: #fff; 374 | @layout-trigger-color-light: @text-color; 375 | 376 | // z-index list, order by `z-index` 377 | @zindex-badge: auto; 378 | @zindex-table-fixed: 2; 379 | @zindex-affix: 10; 380 | @zindex-back-top: 10; 381 | @zindex-picker-panel: 10; 382 | @zindex-popup-close: 10; 383 | @zindex-modal: 1000; 384 | @zindex-modal-mask: 1000; 385 | @zindex-message: 1010; 386 | @zindex-notification: 1010; 387 | @zindex-popover: 1030; 388 | @zindex-dropdown: 1050; 389 | @zindex-picker: 1050; 390 | @zindex-popoconfirm: 1060; 391 | @zindex-tooltip: 1070; 392 | @zindex-image: 1080; 393 | 394 | // Animation 395 | @animation-duration-slow: 0.3s; // Modal 396 | @animation-duration-base: 0.2s; 397 | @animation-duration-fast: 0.1s; // Tooltip 398 | 399 | //CollapsePanel 400 | @collapse-panel-border-radius: @border-radius-base; 401 | 402 | //Dropdown 403 | @dropdown-menu-bg: @component-background; 404 | @dropdown-vertical-padding: 5px; 405 | @dropdown-edge-child-vertical-padding: 4px; 406 | @dropdown-font-size: @font-size-base; 407 | @dropdown-line-height: 22px; 408 | 409 | // Form 410 | // --- 411 | @label-required-color: @highlight-color; 412 | @label-color: @heading-color; 413 | @form-warning-input-bg: @input-bg; 414 | @form-item-margin-bottom: 24px; 415 | @form-item-trailing-colon: true; 416 | @form-vertical-label-padding: 0 0 8px; 417 | @form-vertical-label-margin: 0; 418 | @form-item-label-font-size: @font-size-base; 419 | @form-item-label-height: @input-height-base; 420 | @form-item-label-colon-margin-right: 8px; 421 | @form-item-label-colon-margin-left: 2px; 422 | @form-error-input-bg: @input-bg; 423 | 424 | // Input 425 | // --- 426 | @input-height-base: @height-base; 427 | @input-height-lg: @height-lg; 428 | @input-height-sm: @height-sm; 429 | @input-padding-horizontal: @control-padding-horizontal - 1px; 430 | @input-padding-horizontal-base: @input-padding-horizontal; 431 | @input-padding-horizontal-sm: @control-padding-horizontal-sm - 1px; 432 | @input-padding-horizontal-lg: @input-padding-horizontal; 433 | @input-padding-vertical-base: max( 434 | (round(((@input-height-base - @font-size-base * @line-height-base) / 2) * 10) / 10) - @border-width-base, 435 | 3px 436 | ); 437 | @input-padding-vertical-sm: max( 438 | (round(((@input-height-sm - @font-size-base * @line-height-base) / 2) * 10) / 10) - @border-width-base, 439 | 0 440 | ); 441 | @input-padding-vertical-lg: (ceil(((@input-height-lg - @font-size-lg * @line-height-base) / 2) * 10) / 10) - 442 | @border-width-base; 443 | @input-placeholder-color: hsv(0, 0, 75%); 444 | @input-color: @text-color; 445 | @input-icon-color: @input-color; 446 | @input-border-color: @border-color-base; 447 | @input-bg: @component-background; 448 | @input-number-hover-border-color: @input-hover-border-color; 449 | @input-number-handler-active-bg: #f4f4f4; 450 | @input-number-handler-hover-bg: @primary-5; 451 | @input-number-handler-bg: @component-background; 452 | @input-number-handler-border-color: @border-color-base; 453 | @input-addon-bg: @background-color-light; 454 | @input-hover-border-color: @primary-5; 455 | @input-disabled-bg: @disabled-bg; 456 | @input-outline-offset: 0 0; 457 | @input-icon-hover-color: fade(@black, 85%); 458 | @input-disabled-color: @disabled-color; 459 | 460 | // Mentions 461 | // --- 462 | @mentions-dropdown-bg: @component-background; 463 | @mentions-dropdown-menu-item-hover-bg: @mentions-dropdown-bg; 464 | 465 | // Select 466 | // --- 467 | @select-border-color: @border-color-base; 468 | @select-item-selected-color: @text-color; 469 | @select-item-selected-font-weight: 600; 470 | @select-dropdown-bg: @component-background; 471 | @select-item-selected-bg: @primary-1; 472 | @select-item-active-bg: @item-hover-bg; 473 | @select-dropdown-vertical-padding: @dropdown-vertical-padding; 474 | @select-dropdown-font-size: @dropdown-font-size; 475 | @select-dropdown-line-height: @dropdown-line-height; 476 | @select-dropdown-height: 32px; 477 | @select-background: @component-background; 478 | @select-clear-background: @select-background; 479 | @select-selection-item-bg: @background-color-base; 480 | @select-selection-item-border-color: @border-color-split; 481 | @select-single-item-height-lg: 40px; 482 | @select-multiple-item-height: @input-height-base - @input-padding-vertical-base * 2; // Normal 24px 483 | @select-multiple-item-height-lg: 32px; 484 | @select-multiple-item-spacing-half: ceil((@input-padding-vertical-base / 2)); 485 | @select-multiple-disabled-background: @input-disabled-bg; 486 | @select-multiple-item-disabled-color: #bfbfbf; 487 | @select-multiple-item-disabled-border-color: @select-border-color; 488 | 489 | // Cascader 490 | // --- 491 | @cascader-bg: @component-background; 492 | @cascader-item-selected-bg: @primary-1; 493 | @cascader-menu-bg: @component-background; 494 | @cascader-menu-border-color-split: @border-color-split; 495 | 496 | // Cascader 497 | // ---- 498 | @cascader-dropdown-vertical-padding: @dropdown-vertical-padding; 499 | @cascader-dropdown-edge-child-vertical-padding: @dropdown-edge-child-vertical-padding; 500 | @cascader-dropdown-font-size: @dropdown-font-size; 501 | @cascader-dropdown-line-height: @dropdown-line-height; 502 | 503 | // Anchor 504 | // --- 505 | @anchor-bg: transparent; 506 | @anchor-border-color: @border-color-split; 507 | @anchor-link-top: 4px; 508 | @anchor-link-left: 16px; 509 | @anchor-link-padding: @anchor-link-top 0 @anchor-link-top @anchor-link-left; 510 | 511 | // Tooltip 512 | // --- 513 | // Tooltip max width 514 | @tooltip-max-width: 250px; 515 | // Tooltip text color 516 | @tooltip-color: #fff; 517 | // Tooltip background color 518 | @tooltip-bg: rgba(0, 0, 0, 0.75); 519 | // Tooltip arrow width 520 | @tooltip-arrow-width: 8px * sqrt(2); 521 | // Tooltip distance with trigger 522 | @tooltip-distance: @tooltip-arrow-width - 1px + 4px; 523 | // Tooltip arrow color 524 | @tooltip-arrow-color: @tooltip-bg; 525 | @tooltip-border-radius: @border-radius-base; 526 | 527 | // Popover 528 | // --- 529 | // Popover body background color 530 | @popover-bg: @component-background; 531 | // Popover text color 532 | @popover-color: @text-color; 533 | // Popover maximum width 534 | @popover-min-width: 177px; 535 | @popover-min-height: 32px; 536 | // Popover arrow width 537 | @popover-arrow-width: @tooltip-arrow-width; 538 | // Popover arrow color 539 | @popover-arrow-color: @popover-bg; 540 | // Popover outer arrow width 541 | // Popover outer arrow color 542 | @popover-arrow-outer-color: @popover-bg; 543 | // Popover distance with trigger 544 | @popover-distance: @popover-arrow-width + 4px; 545 | @popover-padding-horizontal: @padding-md; 546 | 547 | // Modal 548 | // -- 549 | @modal-header-padding-vertical: @padding-md; 550 | @modal-header-padding-horizontal: @padding-lg; 551 | @modal-body-padding: @padding-lg; 552 | @modal-header-bg: @component-background; 553 | @modal-header-padding: @modal-header-padding-vertical @modal-header-padding-horizontal; 554 | @modal-header-border-width: @border-width-base; 555 | @modal-header-border-style: @border-style-base; 556 | @modal-header-title-line-height: 22px; 557 | @modal-header-title-font-size: @font-size-lg; 558 | @modal-header-border-color-split: @border-color-split; 559 | @modal-header-close-size: @modal-header-title-line-height + 2 * @modal-header-padding-vertical; 560 | @modal-content-bg: @component-background; 561 | @modal-heading-color: @heading-color; 562 | @modal-close-color: @text-color-secondary; 563 | @modal-footer-bg: transparent; 564 | @modal-footer-border-color-split: @border-color-split; 565 | @modal-footer-border-style: @border-style-base; 566 | @modal-footer-padding-vertical: 10px; 567 | @modal-footer-padding-horizontal: 16px; 568 | @modal-footer-border-width: @border-width-base; 569 | @modal-mask-bg: fade(@black, 45%); 570 | @modal-confirm-body-padding: 32px 32px 24px; 571 | @modal-confirm-title-font-size: @font-size-lg; 572 | @modal-border-radius: @border-radius-base; 573 | 574 | // Progress 575 | // -- 576 | @progress-default-color: @processing-color; 577 | @progress-remaining-color: @background-color-base; 578 | @progress-info-text-color: @progress-text-color; 579 | @progress-radius: 100px; 580 | @progress-steps-item-bg: #f3f3f3; 581 | @progress-text-font-size: 1em; 582 | @progress-text-color: @text-color; // This is for circle text color, should be renamed better 583 | @progress-circle-text-font-size: 1em; 584 | // Menu 585 | // --- 586 | @menu-inline-toplevel-item-height: 40px; 587 | @menu-item-height: 40px; 588 | @menu-item-group-height: @line-height-base; 589 | @menu-collapsed-width: 80px; 590 | @menu-bg: @component-background; 591 | @menu-popup-bg: @component-background; 592 | @menu-item-color: @text-color; 593 | @menu-inline-submenu-bg: @background-color-light; 594 | @menu-highlight-color: @primary-color; 595 | @menu-highlight-danger-color: @error-color; 596 | @menu-item-active-bg: @primary-1; 597 | @menu-item-active-danger-bg: @red-1; 598 | @menu-item-active-border-width: 3px; 599 | @menu-item-group-title-color: @text-color-secondary; 600 | @menu-item-vertical-margin: 4px; 601 | @menu-item-font-size: @font-size-base; 602 | @menu-item-boundary-margin: 8px; 603 | @menu-item-padding-horizontal: 20px; 604 | @menu-item-padding: 0 @menu-item-padding-horizontal; 605 | @menu-horizontal-line-height: 46px; 606 | @menu-icon-margin-right: 10px; 607 | @menu-icon-size: @menu-item-font-size; 608 | @menu-icon-size-lg: @font-size-lg; 609 | @menu-item-group-title-font-size: @menu-item-font-size; 610 | 611 | // dark theme 612 | @menu-dark-color: @text-color-secondary-dark; 613 | @menu-dark-danger-color: @error-color; 614 | @menu-dark-bg: @layout-header-background; 615 | @menu-dark-arrow-color: #fff; 616 | @menu-dark-inline-submenu-bg: #000c17; 617 | @menu-dark-highlight-color: #fff; 618 | @menu-dark-item-active-bg: @primary-color; 619 | @menu-dark-item-active-danger-bg: @error-color; 620 | @menu-dark-selected-item-icon-color: @white; 621 | @menu-dark-selected-item-text-color: @white; 622 | @menu-dark-item-hover-bg: transparent; 623 | // Spin 624 | // --- 625 | @spin-dot-size-sm: 14px; 626 | @spin-dot-size: 20px; 627 | @spin-dot-size-lg: 32px; 628 | 629 | // Table 630 | // -- 631 | @table-bg: @component-background; 632 | @table-header-bg: @background-color-light; 633 | @table-header-color: @heading-color; 634 | @table-header-sort-bg: @background-color-base; 635 | @table-body-sort-bg: #fafafa; 636 | @table-row-hover-bg: @background-color-light; 637 | @table-selected-row-color: inherit; 638 | @table-selected-row-bg: @primary-1; 639 | @table-body-selected-sort-bg: @table-selected-row-bg; 640 | @table-selected-row-hover-bg: darken(@table-selected-row-bg, 2%); 641 | @table-expanded-row-bg: #fbfbfb; 642 | @table-padding-vertical: 16px; 643 | @table-padding-horizontal: 16px; 644 | @table-padding-vertical-md: (@table-padding-vertical * 3 / 4); 645 | @table-padding-horizontal-md: (@table-padding-horizontal / 2); 646 | @table-padding-vertical-sm: (@table-padding-vertical / 2); 647 | @table-padding-horizontal-sm: (@table-padding-horizontal / 2); 648 | @table-border-color: @border-color-split; 649 | @table-border-radius-base: @border-radius-base; 650 | @table-footer-bg: @background-color-light; 651 | @table-footer-color: @heading-color; 652 | @table-header-bg-sm: @table-header-bg; 653 | @table-font-size: @font-size-base; 654 | @table-font-size-md: @table-font-size; 655 | @table-font-size-sm: @table-font-size; 656 | @table-header-cell-split-color: rgba(0, 0, 0, 0.06); 657 | // Sorter 658 | // Legacy: `table-header-sort-active-bg` is used for hover not real active 659 | @table-header-sort-active-bg: rgba(0, 0, 0, 0.04); 660 | @table-fixed-header-sort-active-bg: hsv(0, 0, 96%); 661 | 662 | // Filter 663 | @table-header-filter-active-bg: rgba(0, 0, 0, 0.04); 664 | @table-filter-btns-bg: inherit; 665 | @table-filter-dropdown-bg: @component-background; 666 | @table-expand-icon-bg: @component-background; 667 | @table-selection-column-width: 32px; 668 | // Sticky 669 | @table-sticky-scroll-bar-bg: fade(#000, 35%); 670 | @table-sticky-scroll-bar-radius: 4px; 671 | 672 | // Tag 673 | // -- 674 | @tag-border-radius: @border-radius-base; 675 | @tag-default-bg: @background-color-light; 676 | @tag-default-color: @text-color; 677 | @tag-font-size: @font-size-sm; 678 | @tag-line-height: 20px; 679 | 680 | // TimePicker 681 | // --- 682 | @picker-bg: @component-background; 683 | @picker-basic-cell-hover-color: @item-hover-bg; 684 | @picker-basic-cell-active-with-range-color: @primary-1; 685 | @picker-basic-cell-hover-with-range-color: lighten(@primary-color, 35%); 686 | @picker-basic-cell-disabled-bg: rgba(0, 0, 0, 0.04); 687 | @picker-border-color: @border-color-split; 688 | @picker-date-hover-range-border-color: lighten(@primary-color, 20%); 689 | @picker-date-hover-range-color: @picker-basic-cell-hover-with-range-color; 690 | @picker-time-panel-column-width: 56px; 691 | @picker-time-panel-column-height: 224px; 692 | @picker-time-panel-cell-height: 28px; 693 | @picker-panel-cell-height: 24px; 694 | @picker-panel-cell-width: 36px; 695 | @picker-text-height: 40px; 696 | @picker-panel-without-time-cell-height: 66px; 697 | 698 | // Calendar 699 | // --- 700 | @calendar-bg: @component-background; 701 | @calendar-input-bg: @input-bg; 702 | @calendar-border-color: @border-color-inverse; 703 | @calendar-item-active-bg: @item-active-bg; 704 | @calendar-column-active-bg: fade(@calendar-item-active-bg, 20%); 705 | @calendar-full-bg: @calendar-bg; 706 | @calendar-full-panel-bg: @calendar-full-bg; 707 | 708 | // Carousel 709 | // --- 710 | @carousel-dot-width: 16px; 711 | @carousel-dot-height: 3px; 712 | @carousel-dot-active-width: 24px; 713 | 714 | // Badge 715 | // --- 716 | @badge-height: 20px; 717 | @badge-height-sm: 14px; 718 | @badge-dot-size: 6px; 719 | @badge-font-size: @font-size-sm; 720 | @badge-font-size-sm: @font-size-sm; 721 | @badge-font-weight: normal; 722 | @badge-status-size: 6px; 723 | @badge-text-color: @component-background; 724 | @badge-color: @highlight-color; 725 | 726 | // Rate 727 | // --- 728 | @rate-star-color: @yellow-6; 729 | @rate-star-bg: @border-color-split; 730 | @rate-star-size: 20px; 731 | @rate-star-hover-scale: scale(1.1); 732 | 733 | // Card 734 | // --- 735 | @card-head-color: @heading-color; 736 | @card-head-background: transparent; 737 | @card-head-font-size: @font-size-lg; 738 | @card-head-font-size-sm: @font-size-base; 739 | @card-head-padding: 16px; 740 | @card-head-padding-sm: (@card-head-padding / 2); 741 | @card-head-height: 48px; 742 | @card-head-height-sm: 36px; 743 | @card-inner-head-padding: 12px; 744 | @card-padding-base: 24px; 745 | @card-padding-base-sm: (@card-padding-base / 2); 746 | @card-actions-background: @component-background; 747 | @card-actions-li-margin: 12px 0; 748 | @card-skeleton-bg: #cfd8dc; 749 | @card-background: @component-background; 750 | @card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09); 751 | @card-radius: @border-radius-base; 752 | @card-head-tabs-margin-bottom: -17px; 753 | @card-head-extra-color: @text-color; 754 | 755 | // Comment 756 | // --- 757 | @comment-bg: inherit; 758 | @comment-padding-base: @padding-md 0; 759 | @comment-nest-indent: 44px; 760 | @comment-font-size-base: @font-size-base; 761 | @comment-font-size-sm: @font-size-sm; 762 | @comment-author-name-color: @text-color-secondary; 763 | @comment-author-time-color: #ccc; 764 | @comment-action-color: @text-color-secondary; 765 | @comment-action-hover-color: #595959; 766 | @comment-actions-margin-bottom: inherit; 767 | @comment-actions-margin-top: @margin-sm; 768 | @comment-content-detail-p-margin-bottom: inherit; 769 | 770 | // Tabs 771 | // --- 772 | @tabs-card-head-background: @background-color-light; 773 | @tabs-card-height: 40px; 774 | @tabs-card-active-color: @primary-color; 775 | @tabs-card-horizontal-padding: ((@tabs-card-height - floor(@font-size-base * @line-height-base)) / 2) - 776 | @border-width-base @padding-md; 777 | @tabs-card-horizontal-padding-sm: 6px @padding-md; 778 | @tabs-card-horizontal-padding-lg: 7px @padding-md 6px; 779 | @tabs-title-font-size: @font-size-base; 780 | @tabs-title-font-size-lg: @font-size-lg; 781 | @tabs-title-font-size-sm: @font-size-base; 782 | @tabs-ink-bar-color: @primary-color; 783 | @tabs-bar-margin: 0 0 @margin-md 0; 784 | @tabs-horizontal-gutter: 32px; 785 | @tabs-horizontal-margin: 0 0 0 @tabs-horizontal-gutter; 786 | @tabs-horizontal-margin-rtl: 0 0 0 32px; 787 | @tabs-horizontal-padding: @padding-sm 0; 788 | @tabs-horizontal-padding-lg: @padding-md 0; 789 | @tabs-horizontal-padding-sm: @padding-xs 0; 790 | @tabs-vertical-padding: @padding-xs @padding-lg; 791 | @tabs-vertical-margin: @margin-md 0 0 0; 792 | @tabs-scrolling-size: 32px; 793 | @tabs-highlight-color: @primary-color; 794 | @tabs-hover-color: @primary-5; 795 | @tabs-active-color: @primary-7; 796 | @tabs-card-gutter: 2px; 797 | @tabs-card-tab-active-border-top: 2px solid transparent; 798 | 799 | // BackTop 800 | // --- 801 | @back-top-color: #fff; 802 | @back-top-bg: @text-color-secondary; 803 | @back-top-hover-bg: @text-color; 804 | 805 | // Avatar 806 | // --- 807 | @avatar-size-base: 32px; 808 | @avatar-size-lg: 40px; 809 | @avatar-size-sm: 24px; 810 | @avatar-font-size-base: 18px; 811 | @avatar-font-size-lg: 24px; 812 | @avatar-font-size-sm: 14px; 813 | @avatar-bg: #ccc; 814 | @avatar-color: #fff; 815 | @avatar-border-radius: @border-radius-base; 816 | @avatar-group-overlapping: -8px; 817 | @avatar-group-space: 3px; 818 | @avatar-group-border-color: #fff; 819 | 820 | // Switch 821 | // --- 822 | @switch-height: 22px; 823 | @switch-sm-height: 16px; 824 | @switch-min-width: 44px; 825 | @switch-sm-min-width: 28px; 826 | @switch-disabled-opacity: 0.4; 827 | @switch-color: @primary-color; 828 | @switch-bg: @component-background; 829 | @switch-shadow-color: fade(#00230b, 20%); 830 | @switch-padding: 2px; 831 | @switch-inner-margin-min: ceil(@switch-height * 0.3); 832 | @switch-inner-margin-max: ceil(@switch-height * 1.1); 833 | @switch-sm-inner-margin-min: ceil(@switch-sm-height * 0.3); 834 | @switch-sm-inner-margin-max: ceil(@switch-sm-height * 1.1); 835 | 836 | // Pagination 837 | // --- 838 | @pagination-item-bg: @component-background; 839 | @pagination-item-size: @height-base; 840 | @pagination-item-size-sm: 24px; 841 | @pagination-font-family: @font-family; 842 | @pagination-font-weight-active: 500; 843 | @pagination-item-bg-active: @component-background; 844 | @pagination-item-link-bg: @component-background; 845 | @pagination-item-disabled-color-active: @disabled-color; 846 | @pagination-item-disabled-bg-active: @disabled-active-bg; 847 | @pagination-item-input-bg: @component-background; 848 | @pagination-mini-options-size-changer-top: 0px; 849 | 850 | // PageHeader 851 | // --- 852 | @page-header-padding: @padding-lg; 853 | @page-header-padding-vertical: @padding-md; 854 | @page-header-padding-breadcrumb: @padding-sm; 855 | @page-header-content-padding-vertical: @padding-sm; 856 | @page-header-back-color: #000; 857 | @page-header-ghost-bg: inherit; 858 | @page-header-heading-title: @heading-4-size; 859 | @page-header-heading-sub-title: 14px; 860 | @page-header-tabs-tab-font-size: 16px; 861 | 862 | // Breadcrumb 863 | // --- 864 | @breadcrumb-base-color: @text-color-secondary; 865 | @breadcrumb-last-item-color: @text-color; 866 | @breadcrumb-font-size: @font-size-base; 867 | @breadcrumb-icon-font-size: @font-size-base; 868 | @breadcrumb-link-color: @text-color-secondary; 869 | @breadcrumb-link-color-hover: @text-color; 870 | @breadcrumb-separator-color: @text-color-secondary; 871 | @breadcrumb-separator-margin: 0 @padding-xs; 872 | 873 | // Slider 874 | // --- 875 | @slider-margin: 10px 6px 10px; 876 | @slider-rail-background-color: @background-color-base; 877 | @slider-rail-background-color-hover: #e1e1e1; 878 | @slider-track-background-color: @primary-3; 879 | @slider-track-background-color-hover: @primary-4; 880 | @slider-handle-border-width: 2px; 881 | @slider-handle-background-color: @component-background; 882 | @slider-handle-color: @primary-3; 883 | @slider-handle-color-hover: @primary-4; 884 | @slider-handle-color-focus: tint(@primary-color, 20%); 885 | @slider-handle-color-focus-shadow: fade(@primary-color, 12%); 886 | @slider-handle-color-tooltip-open: @primary-color; 887 | @slider-handle-size: 14px; 888 | @slider-handle-margin-top: -5px; 889 | @slider-handle-margin-left: -5px; 890 | @slider-handle-shadow: 0; 891 | @slider-dot-border-color: @border-color-split; 892 | @slider-dot-border-color-active: tint(@primary-color, 50%); 893 | @slider-disabled-color: @disabled-color; 894 | @slider-disabled-background-color: @component-background; 895 | 896 | // Tree 897 | // --- 898 | @tree-bg: @component-background; 899 | @tree-title-height: 24px; 900 | @tree-child-padding: 18px; 901 | @tree-directory-selected-color: #fff; 902 | @tree-directory-selected-bg: @primary-color; 903 | @tree-node-hover-bg: @item-hover-bg; 904 | @tree-node-selected-bg: @primary-2; 905 | 906 | // Collapse 907 | // --- 908 | @collapse-header-padding: @padding-sm @padding-md; 909 | @collapse-header-padding-extra: 40px; 910 | @collapse-header-bg: @background-color-light; 911 | @collapse-content-padding: @padding-md; 912 | @collapse-content-bg: @component-background; 913 | @collapse-header-arrow-left: 16px; 914 | 915 | // Skeleton 916 | // --- 917 | @skeleton-color: rgba(190, 190, 190, 0.2); 918 | @skeleton-to-color: shade(@skeleton-color, 5%); 919 | @skeleton-paragraph-margin-top: 28px; 920 | @skeleton-paragraph-li-margin-top: @margin-md; 921 | @skeleton-paragraph-li-height: 16px; 922 | @skeleton-title-height: 16px; 923 | @skeleton-title-paragraph-margin-top: @margin-lg; 924 | 925 | // Transfer 926 | // --- 927 | @transfer-header-height: 40px; 928 | @transfer-item-height: @height-base; 929 | @transfer-disabled-bg: @disabled-bg; 930 | @transfer-list-height: 200px; 931 | @transfer-item-hover-bg: @item-hover-bg; 932 | @transfer-item-selected-hover-bg: darken(@item-active-bg, 2%); 933 | @transfer-item-padding-vertical: 6px; 934 | @transfer-list-search-icon-top: 12px; 935 | 936 | // Message 937 | // --- 938 | @message-notice-content-padding: 10px 16px; 939 | @message-notice-content-bg: @component-background; 940 | // Motion 941 | // --- 942 | @wave-animation-width: 6px; 943 | 944 | // Alert 945 | // --- 946 | @alert-success-border-color: ~`colorPalette('@{success-color}', 3) `; 947 | @alert-success-bg-color: ~`colorPalette('@{success-color}', 1) `; 948 | @alert-success-icon-color: @success-color; 949 | @alert-info-border-color: ~`colorPalette('@{info-color}', 3) `; 950 | @alert-info-bg-color: ~`colorPalette('@{info-color}', 1) `; 951 | @alert-info-icon-color: @info-color; 952 | @alert-warning-border-color: ~`colorPalette('@{warning-color}', 3) `; 953 | @alert-warning-bg-color: ~`colorPalette('@{warning-color}', 1) `; 954 | @alert-warning-icon-color: @warning-color; 955 | @alert-error-border-color: ~`colorPalette('@{error-color}', 3) `; 956 | @alert-error-bg-color: ~`colorPalette('@{error-color}', 1) `; 957 | @alert-error-icon-color: @error-color; 958 | @alert-message-color: @heading-color; 959 | @alert-text-color: @text-color; 960 | @alert-close-color: @text-color-secondary; 961 | @alert-close-hover-color: @icon-color-hover; 962 | @alert-no-icon-padding-vertical: @padding-xs; 963 | @alert-with-description-no-icon-padding-vertical: @padding-md - 1px; 964 | @alert-with-description-padding-vertical: @padding-md - 1px; 965 | @alert-with-description-padding: @alert-with-description-padding-vertical 15px 966 | @alert-with-description-no-icon-padding-vertical @alert-with-description-icon-size; 967 | @alert-icon-top: 8px + @font-size-base * (@line-height-base / 2) - (@font-size-base / 2); 968 | @alert-with-description-icon-size: 24px; 969 | 970 | // List 971 | // --- 972 | @list-header-background: transparent; 973 | @list-footer-background: transparent; 974 | @list-empty-text-padding: @padding-md; 975 | @list-item-padding: @padding-sm 0; 976 | @list-item-padding-sm: @padding-xs @padding-md; 977 | @list-item-padding-lg: 16px 24px; 978 | @list-item-meta-margin-bottom: @padding-md; 979 | @list-item-meta-avatar-margin-right: @padding-md; 980 | @list-item-meta-title-margin-bottom: @padding-sm; 981 | @list-customize-card-bg: @component-background; 982 | @list-item-meta-description-font-size: @font-size-base; 983 | 984 | // Statistic 985 | // --- 986 | @statistic-title-font-size: @font-size-base; 987 | @statistic-content-font-size: 24px; 988 | @statistic-unit-font-size: 24px; 989 | @statistic-font-family: @font-family; 990 | 991 | // Drawer 992 | // --- 993 | @drawer-header-padding: @padding-md @padding-lg; 994 | @drawer-body-padding: @padding-lg; 995 | @drawer-bg: @component-background; 996 | @drawer-footer-padding-vertical: @modal-footer-padding-vertical; 997 | @drawer-footer-padding-horizontal: @modal-footer-padding-horizontal; 998 | @drawer-header-close-size: 56px; 999 | @drawer-title-font-size: @font-size-lg; 1000 | @drawer-title-line-height: 22px; 1001 | 1002 | // Timeline 1003 | // --- 1004 | @timeline-width: 2px; 1005 | @timeline-color: @border-color-split; 1006 | @timeline-dot-border-width: 2px; 1007 | @timeline-dot-color: @primary-color; 1008 | @timeline-dot-bg: @component-background; 1009 | @timeline-item-padding-bottom: 20px; 1010 | 1011 | // Typography 1012 | // --- 1013 | @typography-title-font-weight: 600; 1014 | @typography-title-margin-top: 1.2em; 1015 | @typography-title-margin-bottom: 0.5em; 1016 | 1017 | // Upload 1018 | // --- 1019 | @upload-actions-color: @text-color-secondary; 1020 | 1021 | // Steps 1022 | // --- 1023 | @process-tail-color: @border-color-split; 1024 | @steps-nav-arrow-color: fade(@black, 25%); 1025 | @steps-background: @component-background; 1026 | @steps-icon-size: 32px; 1027 | @steps-icon-custom-size: @steps-icon-size; 1028 | @steps-icon-custom-top: 0px; 1029 | @steps-icon-custom-font-size: 24px; 1030 | @steps-icon-top: -0.5px; 1031 | @steps-icon-font-size: @font-size-lg; 1032 | @steps-icon-margin: 0 8px 0 0; 1033 | @steps-title-line-height: @height-base; 1034 | @steps-small-icon-size: 24px; 1035 | @steps-small-icon-margin: 0 8px 0 0; 1036 | @steps-dot-size: 8px; 1037 | @steps-dot-top: 2px; 1038 | @steps-current-dot-size: 10px; 1039 | @steps-description-max-width: 140px; 1040 | @steps-nav-content-max-width: auto; 1041 | @steps-vertical-icon-width: 16px; 1042 | @steps-vertical-tail-width: 16px; 1043 | @steps-vertical-tail-width-sm: 12px; 1044 | 1045 | // Notification 1046 | // --- 1047 | @notification-bg: @component-background; 1048 | @notification-padding-vertical: 16px; 1049 | @notification-padding-horizontal: 24px; 1050 | 1051 | // Result 1052 | // --- 1053 | @result-title-font-size: 24px; 1054 | @result-subtitle-font-size: @font-size-base; 1055 | @result-icon-font-size: 72px; 1056 | @result-extra-margin: 24px 0 0 0; 1057 | 1058 | // Image 1059 | // --- 1060 | @image-size-base: 48px; 1061 | @image-font-size-base: 24px; 1062 | @image-bg: #f5f5f5; 1063 | @image-color: #fff; 1064 | @image-mask-font-size: 16px; 1065 | @image-preview-operation-size: 18px; 1066 | @image-preview-operation-color: @text-color-dark; 1067 | @image-preview-operation-disabled-color: fade(@image-preview-operation-color, 25%); 1068 | 1069 | // Segmented 1070 | // --- 1071 | @segmented-bg: fade(@black, 4%); 1072 | @segmented-hover-bg: fade(@black, 6%); 1073 | @segmented-selected-bg: @white; 1074 | @segmented-label-color: fade(@black, 65%); 1075 | @segmented-label-hover-color: #262626; 1076 | -------------------------------------------------------------------------------- /src/http/fetch.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig } from 'axios' 2 | import { AuthInterceptor } from './interceptors/auth-intereceptor' 3 | import { ErrorResponseInterceptor } from './interceptors/error-response-interceptor' 4 | import { ResponseLogInterceptor } from './interceptors/response-log-interceptor' 5 | import { TokenInjectRequestInterceptor } from './interceptors/token-interceptor' 6 | import { BaseResponse } from './response' 7 | 8 | const instance = axios.create() 9 | // @ts-ignore 10 | instance.defaults.headers['Content-Type'] = 'application/json' 11 | // @ts-ignore 12 | instance.defaults.headers.Accept = 'application/json' 13 | 14 | if (process.env.NODE_ENV === 'development') { 15 | instance.interceptors.response.use(...ResponseLogInterceptor) 16 | } 17 | instance.interceptors.response.use(...AuthInterceptor) 18 | instance.interceptors.response.use(...ErrorResponseInterceptor) 19 | // @ts-ignore 20 | instance.interceptors.request.use(...TokenInjectRequestInterceptor) 21 | 22 | const Fetch = { 23 | get: (url: string, config?: AxiosRequestConfig) => { 24 | return instance.get>(url, { ...config }).then(res => res.data) 25 | }, 26 | post: (url: string, data: any = {}, config?: AxiosRequestConfig) => { 27 | return instance.post>(url, data, { ...config }).then(res => res.data) 28 | }, 29 | request: (config: AxiosRequestConfig) => instance.request>({ ...config }) 30 | } 31 | 32 | export default Fetch 33 | -------------------------------------------------------------------------------- /src/http/interceptors/auth-intereceptor.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { BaseResponse } from '../response' 3 | import { AxiosInterceptor } from './types' 4 | 5 | export const AuthInterceptor: AxiosInterceptor>> = [ 6 | // @ts-ignore 7 | async res => { 8 | // console.log('res data redirect', res?.data?.redirect); 9 | // if (res?.data?.redirect) { 10 | // // captureMessage('token expired'); 11 | // // eslint-disable-next-line prefer-promise-reject-errors 12 | // return Promise.reject('token expired'); 13 | // } 14 | return res 15 | }, 16 | error => { 17 | // @ts-ignore 18 | console.warn('axios auth error', error) 19 | return Promise.reject(error) 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /src/http/interceptors/error-response-interceptor.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { BaseResponse } from '../response' 3 | import { AxiosInterceptor } from './types' 4 | 5 | export const ErrorResponseInterceptor: AxiosInterceptor>> = [ 6 | // @ts-ignore 7 | res => { 8 | // eslint-disable-next-line eqeqeq 9 | if (res.data?.code != 0) { 10 | if (process.env.NODE_ENV === 'development') { 11 | console.warn('___________request error_______________') 12 | console.warn(`${res.config.url} request error ${res.data.msg}`) 13 | console.warn('___________request error_______________') 14 | } 15 | console.log(res?.data?.msg ?? '服务异常,请稍后重试!') 16 | 17 | return Promise.reject(res.data.msg) 18 | } 19 | return res 20 | }, 21 | err => { 22 | if (err?.message && err.message === 'Network Error') { 23 | console.log('网络异常') 24 | } 25 | return Promise.reject(err) 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /src/http/interceptors/response-log-interceptor.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { BaseResponse } from '../response' 3 | import { AxiosInterceptor } from './types' 4 | 5 | export const ResponseLogInterceptor: AxiosInterceptor>> = [ 6 | // @ts-ignore 7 | res => { 8 | console.log(`axios request url is ${res.config.url}`) 9 | console.log('axios request Data is', res.config.data) 10 | console.log('axios response', res) 11 | 12 | return res 13 | }, 14 | error => { 15 | if (error.request) { 16 | console.warn('.....................................................') 17 | console.warn('axios response interceptor error and url is', error.request.responseURL) 18 | console.warn('axios response interceptor error and error is', error) 19 | console.warn('.....................................................') 20 | } 21 | return Promise.reject(error) 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /src/http/interceptors/token-interceptor.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequsetInterceptor } from './types' 2 | 3 | export const TokenInjectRequestInterceptor: AxiosRequsetInterceptor = [ 4 | async req => { 5 | // const token = await UserInfoStorage.getParsedData(); 6 | 7 | // if (token) { 8 | // // eslint-disable-next-line @typescript-eslint/camelcase 9 | // const { _u_id, _h_id, _aj_token, jwt } = token; 10 | // // eslint-disable-next-line @typescript-eslint/camelcase 11 | // req.headers['x-bifrost-u-id'] = _u_id; 12 | // // eslint-disable-next-line @typescript-eslint/camelcase 13 | // req.headers['x-bifrost-h-id'] = _h_id; 14 | // // eslint-disable-next-line @typescript-eslint/camelcase 15 | // req.headers['x-bifrost-aj-token'] = _aj_token; 16 | // req.headers['x-bifrost-jwt'] = jwt; 17 | // } 18 | return req 19 | }, 20 | error => { 21 | // @ts-ignore 22 | // eslint-disable-next-line no-console 23 | console.warn('axios token error', error) 24 | return Promise.reject(error) 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /src/http/interceptors/types.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig, AxiosResponse } from 'axios' 2 | 3 | export type AxiosInterceptor = [OnFulfilled, OnRejected] 4 | export type AxiosRequsetInterceptor = [RequestOnFulfilled, OnRejected] 5 | 6 | export type OnFulfilled = (value: V) => AxiosResponse | Promise> 7 | export type OnRejected = (error: any) => any 8 | 9 | export type RequestOnFulfilled = (value: AxiosRequestConfig) => Promise | AxiosRequestConfig 10 | -------------------------------------------------------------------------------- /src/http/response.ts: -------------------------------------------------------------------------------- 1 | export interface BaseResponse { 2 | code: number 3 | data: T 4 | msg: string 5 | } 6 | export interface SuccessResponse extends BaseResponse { 7 | success: true 8 | data: T 9 | } 10 | // @ts-ignore 11 | export interface FailedResponse extends BaseResponse { 12 | success: false 13 | obj: null 14 | data: null 15 | } 16 | 17 | export type PageData = { 18 | pageSize: number 19 | 20 | totalPageCount: number 21 | 22 | totalCount: number 23 | 24 | dataList: T[] 25 | } 26 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import { RouterProvider } from 'react-router-dom' 4 | import { Provider } from 'react-redux' 5 | import { store } from '@store' 6 | import { router } from '@router' 7 | import dayjs from 'dayjs' 8 | import relativeTime from 'dayjs/plugin/relativeTime' 9 | 10 | import './assets/style/index.less' 11 | 12 | const container = document.getElementById('root') 13 | const root = createRoot(container!) 14 | 15 | function App() { 16 | dayjs.extend(relativeTime) 17 | dayjs.locale('zh-cn') 18 | 19 | return ( 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | root.render(App()) 27 | -------------------------------------------------------------------------------- /src/kits/checkLogin.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Navigate } from 'react-router-dom' 3 | 4 | const CheckLogin: React.FC<{ children: React.ReactNode }> = props => { 5 | const isLogin = true 6 | return <>{isLogin ? props.children : } 7 | } 8 | 9 | export default CheckLogin 10 | -------------------------------------------------------------------------------- /src/kits/checkPermission.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Spin } from 'antd' 3 | import { useLocation } from 'react-router-dom' 4 | import { useHasPermission } from './hooks' 5 | import Error403 from '@pages/error403' 6 | 7 | const CheckPermission: React.FC<{ children: React.ReactNode }> = props => { 8 | const location = useLocation() 9 | const { isLoading, hasPermission } = useHasPermission(location.pathname) 10 | 11 | return ( 12 | <> 13 | {isLoading ? ( 14 | 23 | ) : hasPermission() ? ( 24 | props.children 25 | ) : ( 26 | 27 | )} 28 | 29 | ) 30 | } 31 | 32 | export default CheckPermission 33 | -------------------------------------------------------------------------------- /src/kits/hooks.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 权限检测当前用户是否有某个权限 3 | * @param permissionKey 权限 key 4 | */ 5 | export const useHasPermission = (permissionKey: string | string[]) => { 6 | // const { data, isLoading } = useGetAccountPermissionsQuery() 7 | // 8 | const isLoading = false 9 | const hasPermission = (): boolean => { 10 | if (isLoading) { 11 | return false 12 | } 13 | // if (Array.isArray(permissionKey)) { 14 | // for (const key of permissionKey) { 15 | // if (data!.permissions.includes(key)) { 16 | // return false 17 | // } 18 | // } 19 | // return true 20 | // } 21 | // return data!.permissions.includes(permissionKey) 22 | return true 23 | } 24 | return { isLoading: false, hasPermission } 25 | } 26 | -------------------------------------------------------------------------------- /src/kits/index.ts: -------------------------------------------------------------------------------- 1 | import Msg from './msg' 2 | 3 | export { Msg } 4 | -------------------------------------------------------------------------------- /src/kits/lazyLoad.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react' 2 | import { Spin } from 'antd' 3 | 4 | const lazyLoad = (LazyComponent: React.LazyExoticComponent): React.ReactNode => { 5 | return ( 6 | 17 | } 18 | > 19 | <> 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | export default lazyLoad 27 | -------------------------------------------------------------------------------- /src/kits/msg/index.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | 3 | const Msg = new EventEmitter() 4 | export default Msg 5 | -------------------------------------------------------------------------------- /src/kits/util/getEnv.ts: -------------------------------------------------------------------------------- 1 | export function wrapperEnv(envConf: Recordable): GlobalEnv { 2 | const ret: any = {} 3 | 4 | for (const envName of Object.keys(envConf)) { 5 | let realName = envConf[envName].replace(/\\n/g, '\n') 6 | realName = realName === 'true' ? true : realName === 'false' ? false : realName 7 | 8 | if (envName === 'APP_HOST_PORT') { 9 | realName = Number(realName) 10 | } 11 | 12 | ret[envName] = realName 13 | process.env[envName] = realName 14 | } 15 | return ret 16 | } 17 | -------------------------------------------------------------------------------- /src/kits/util/regexUtil.ts: -------------------------------------------------------------------------------- 1 | /** 手机号码 */ 2 | export const mobile = /^1[345789]\d{9}/ 3 | 4 | /** 5 | * 小数 6 | */ 7 | export const deci = /(? { 7 | return ( 8 |
9 | ChartLine 10 | 11 | 12 |
13 | ) 14 | } 15 | 16 | export default ChartLine 17 | -------------------------------------------------------------------------------- /src/pages/chart/chartLine.module.less: -------------------------------------------------------------------------------- 1 | .img { 2 | width: 200px; 3 | height: 200px; 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/chart/index.less: -------------------------------------------------------------------------------- 1 | .img2 { 2 | width: 400px; 3 | height: 400px; 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/error403.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Error404: React.FC = () => { 4 | return ( 5 |
6 |

诶呀,您没有权限,请联系管理员...

7 |
8 | ) 9 | } 10 | 11 | export default Error404 12 | -------------------------------------------------------------------------------- /src/pages/error404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Error404: React.FC = () => { 4 | return ( 5 |
6 |

诶呀,找不到当前页面了...

7 |
8 | ) 9 | } 10 | 11 | export default Error404 12 | -------------------------------------------------------------------------------- /src/pages/login/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { useDispatch } from 'react-redux' 3 | import { useNavigate } from 'react-router-dom' 4 | import { App as AntdApp, Button, Checkbox, Form, Input } from 'antd' 5 | import { LockOutlined, UserOutlined } from '@ant-design/icons' 6 | import styles from './login.module.less' 7 | 8 | const Login: React.FC = () => { 9 | const dispatch = useDispatch() 10 | const navigate = useNavigate() 11 | 12 | const [isLoading, setIsLoading] = useState(false) 13 | 14 | const [form] = Form.useForm() 15 | const { message, notification, modal } = AntdApp.useApp() 16 | const handlerSubmit = async (values: any) => { 17 | // loginFn({ 18 | // account: values.username, 19 | // password: values.password 20 | // }) 21 | // .unwrap() 22 | // .then(data => { 23 | // if (data.errorCode == 0) { 24 | // dispatch( 25 | // login({ 26 | // jwt: data.jwt 27 | // }) 28 | // ) 29 | // message.success('登录成功') 30 | // navigate('/index') 31 | // } else { 32 | // notification.error({ 33 | // description: data.message, 34 | // message: '出错了' 35 | // }) 36 | // 37 | // form.resetFields() 38 | // } 39 | // }) 40 | } 41 | 42 | const showModal = () => { 43 | modal.success({ 44 | title: '诶呀', 45 | content: '这个只是一个示例而已,需自己实现一下...' 46 | }) 47 | } 48 | 49 | return ( 50 |
51 |
66 |

React-Better-Admin

67 | 68 | 73 | } placeholder="账户" /> 74 | 75 | 76 | 77 | } type="password" placeholder="密码" /> 78 | 79 | 80 | 81 | 记住我 82 | 83 | 84 | 87 | 88 | 89 | 90 | 93 | 或者{' '} 94 | 97 | 98 |
99 |
100 | ) 101 | } 102 | export default Login 103 | -------------------------------------------------------------------------------- /src/pages/login/login.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | justify-content: center; 4 | //align-items: center; 5 | height: 100vh; 6 | width: 100%; 7 | background-size: 300% 300%; 8 | background-image: linear-gradient( 9 | -45deg, 10 | rgba(59,173,227,1) 0%, 11 | rgba(87,111,230,1) 25%, 12 | rgba(152,68,183,1) 51%, 13 | rgba(255,53,127,1) 100% 14 | ); 15 | animation: BGAnimate 20s ease infinite; 16 | } 17 | 18 | @keyframes BGAnimate { 19 | 0%{background-position:0% 50%} 20 | 50%{background-position:100% 50%} 21 | 100%{background-position:0% 50%} 22 | } 23 | 24 | 25 | //.centered-div { 26 | // //position: absolute; 27 | // //top: 0; 28 | // //bottom: 0; 29 | // //left: 0; 30 | // //right: 0; 31 | // //margin: auto; 32 | // height: 1500px; 33 | // width: 500px; 34 | // background-color: red; 35 | // /* 在此处设置要居中的div的宽度、高度、背景色等样式 */ 36 | //} 37 | 38 | -------------------------------------------------------------------------------- /src/pages/user/type.ts: -------------------------------------------------------------------------------- 1 | export type UserDto = { 2 | id: string 3 | name: string 4 | deptName: string 5 | } 6 | 7 | export interface UserState { 8 | data: Array 9 | selectedRowKeys: Array 10 | loading: boolean 11 | total: number 12 | currentPage: number 13 | pageSize: number 14 | showAddForm: boolean 15 | } 16 | -------------------------------------------------------------------------------- /src/pages/user/user-add.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Form, Input, Select } from 'antd' 3 | import styles from './user.module.less' 4 | import { formItemLayout, tailFormItemLayout } from './util' 5 | 6 | const { Option } = Select 7 | 8 | const UserAdd = () => { 9 | const [form] = Form.useForm() 10 | 11 | const onFinish = (values: any) => { 12 | console.log('Received values of form: ', values) 13 | } 14 | 15 | return ( 16 |
17 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 39 | 42 | 43 |
44 | ) 45 | } 46 | 47 | export default UserAdd 48 | -------------------------------------------------------------------------------- /src/pages/user/user-list.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { useSetState } from 'ahooks' 3 | import { Button, Divider, Drawer, Layout, Pagination, Popconfirm, Table } from 'antd' 4 | import { UserState } from './type' 5 | import UserAdd from './user-add' 6 | import { queryUserList } from './web-api' 7 | import styles from './user.module.less' 8 | 9 | const UserList = () => { 10 | const [state, setState] = useSetState({ 11 | data: [], 12 | selectedRowKeys: [], 13 | loading: true, 14 | total: 0, 15 | currentPage: 1, 16 | pageSize: 10, 17 | showAddForm: false 18 | }) 19 | 20 | useEffect(() => { 21 | queryList(state.currentPage) 22 | }, []) 23 | 24 | return ( 25 | <> 26 | 27 |
28 | 29 | {state.selectedRowKeys.length > 0 ? `已选中 ${state.selectedRowKeys.length} 条数据` : ''} 30 | 31 | 34 |
35 | record.id} 38 | locale={{ emptyText: '暂无数据' }} 39 | dataSource={state.data} 40 | loading={state.loading} 41 | pagination={false} 42 | rowSelection={{ 43 | selectedRowKeys: state.selectedRowKeys, 44 | fixed: true, 45 | onChange: selectedRowKeys => { 46 | setState({ selectedRowKeys }) 47 | } 48 | }} 49 | columns={[ 50 | { 51 | className: 'table-column', 52 | title: '姓名', 53 | dataIndex: 'name' 54 | }, 55 | { 56 | className: 'table-column', 57 | title: '手机号', 58 | dataIndex: 'phone' 59 | }, 60 | { 61 | className: 'table-column', 62 | title: '部门', 63 | dataIndex: 'deptName' 64 | }, 65 | { 66 | className: 'table-column', 67 | title: '操作', 68 | dataIndex: '', 69 | width: 200, 70 | render: record => ( 71 | 72 | 修改 73 | 74 | 75 | 删除 76 | 77 | 78 | ) 79 | } 80 | ]} 81 | /> 82 |
83 | { 88 | queryList(page) 89 | }} 90 | showTotal={(total: number, range: any) => { 91 | return total > 0 ? `当前第 ${range[0]} - ${range[1]} 条 共计 ${total} 条` : '没有符合条件的记录' 92 | }} 93 | defaultCurrent={1} 94 | defaultPageSize={10} 95 | total={state.total} 96 | showQuickJumper 97 | /> 98 |
99 | 100 | setState({ showAddForm: false })} 107 | open={state.showAddForm} 108 | getContainer={false} 109 | style={{ position: 'absolute' }} 110 | > 111 | 112 | 113 | 114 | ) 115 | 116 | function queryList(currentPage: number) { 117 | queryUserList().then(res => { 118 | setState({ data: res.dataList, total: res.totalCount, loading: false, currentPage }) 119 | }) 120 | } 121 | } 122 | 123 | export default UserList 124 | -------------------------------------------------------------------------------- /src/pages/user/user.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | //height: 100%; 3 | overflow-y: auto; 4 | background-color: white; 5 | padding:20px; 6 | box-sizing: border-box; 7 | } 8 | 9 | .toolbar { 10 | display: flex; 11 | flex-direction: row; 12 | align-items: center; 13 | justify-content: flex-start; 14 | padding-right: 15px; 15 | margin-bottom: 10px; 16 | } 17 | .selectedText { 18 | margin-left: 8px; 19 | font-size: 12px; 20 | } 21 | .addForm { 22 | width: 80%; 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/user/util.ts: -------------------------------------------------------------------------------- 1 | export const formItemLayout = { 2 | labelCol: { 3 | xs: { span: 24 }, 4 | sm: { span: 8 } 5 | }, 6 | wrapperCol: { 7 | xs: { span: 24 }, 8 | sm: { span: 16 } 9 | } 10 | } 11 | 12 | export const tailFormItemLayout = { 13 | wrapperCol: { 14 | xs: { 15 | span: 24, 16 | offset: 0 17 | }, 18 | sm: { 19 | span: 16, 20 | offset: 8 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/user/web-api.ts: -------------------------------------------------------------------------------- 1 | import Fetch from '@http' 2 | import { PageData } from '../../http/response' 3 | import { UserDto } from './type' 4 | 5 | export const queryUserList = async () => { 6 | const { data } = await Fetch.get>( 7 | `${import.meta.env.APP_HOST_API}:${import.meta.env.APP_HOST_PORT}/user/list` 8 | ) 9 | return data 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/workspace/components/breadcrumbs.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react' 2 | import { useLocation } from 'react-router-dom' 3 | import { Breadcrumb } from 'antd' 4 | import { getBreadcrumbs } from '@router' 5 | 6 | /** 7 | * 面包屑组件 8 | * @constructor 9 | */ 10 | const Breadcrumbs: React.FC = () => { 11 | const { pathname } = useLocation() 12 | 13 | const breadcrumbs = useMemo(() => getBreadcrumbs(pathname), [pathname]) 14 | 15 | return ( 16 | 17 | {breadcrumbs.map(value => { 18 | return {value} 19 | })} 20 | 21 | ) 22 | } 23 | 24 | export default Breadcrumbs 25 | -------------------------------------------------------------------------------- /src/pages/workspace/components/header.less: -------------------------------------------------------------------------------- 1 | @import url('../../../assets/style/default.less'); 2 | .notice-header { 3 | padding: 0 12px 0 0; 4 | background: #ffffff; 5 | box-shadow: 0 1px 4px rgb(0 21 41 / 8%); 6 | } 7 | :global { 8 | .ant-layout { 9 | overflow-x: hidden; 10 | } 11 | } 12 | .menu { 13 | :global(.anticon) { 14 | margin-right: 8px; 15 | } 16 | :global(.ant-dropdown-menu-item) { 17 | width: 160px; 18 | } 19 | } 20 | .trigger { 21 | padding: 0 24px; 22 | font-size: 20px; 23 | line-height: 64px; 24 | cursor: pointer; 25 | transition: all 0.3s; 26 | &:hover { 27 | color: #1890ff; 28 | background: @primary-1; 29 | } 30 | } 31 | 32 | @media screen and (max-width: @screen-xs) { 33 | .trigger { 34 | display: none; 35 | } 36 | } 37 | .header-right { 38 | float: right; 39 | height: 100%; 40 | .notice-action { 41 | display: inline-block; 42 | padding: 20px 12px; 43 | cursor: pointer; 44 | transition: all 0.3s; 45 | > i { 46 | font-size: 12px; 47 | vertical-align: middle; 48 | } 49 | &:global(.ant-popover-open), 50 | &:hover { 51 | background: @primary-1; 52 | } 53 | } 54 | .action { 55 | display: inline-block; 56 | height: 100%; 57 | padding: 0 12px; 58 | cursor: pointer; 59 | transition: all 0.3s; 60 | > i { 61 | font-size: 12px; 62 | vertical-align: middle; 63 | } 64 | &:global(.ant-popover-open), 65 | &:hover { 66 | background: @primary-1; 67 | } 68 | } 69 | .account { 70 | .avatar { 71 | margin: 20px 1px 25px 0; 72 | color: @primary-color; 73 | vertical-align: middle; 74 | background: rgb(255 255 255 / 85%); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/pages/workspace/components/header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import { Avatar, Dropdown, Layout, Menu, Tag } from 'antd' 4 | import dayjs from 'dayjs' 5 | import groupBy from 'lodash/groupBy' 6 | import { MenuInfo } from 'rc-menu/lib/interface' 7 | import { NoticeIcon } from '@comps' 8 | import { AppDispatch, RootState } from '@store' 9 | import { LogoutOutlined, MenuFoldOutlined, MenuUnfoldOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons' 10 | import { bachSetState } from '../slice' 11 | import { getColor, tempData } from '../util' 12 | import './header.less' 13 | 14 | const { Header } = Layout 15 | 16 | const WorkSpaceHeader = () => { 17 | const dispatch: AppDispatch = useDispatch() 18 | const { collapsed } = useSelector((state: RootState) => state.workspace) 19 | 20 | function getNoticeData(notices: any) { 21 | if (notices.length === 0) { 22 | return {} 23 | } 24 | const newNotices = notices.map((notice: any) => { 25 | const newNotice = { ...notice } 26 | if (newNotice.datetime) { 27 | newNotice.datetime = dayjs(notice.datetime).fromNow() 28 | } 29 | if (newNotice.id) { 30 | newNotice.key = newNotice.id 31 | } 32 | if (newNotice.extra && newNotice.status) { 33 | const color = getColor(newNotice.status) 34 | newNotice.extra = ( 35 | 36 | {newNotice.extra} 37 | 38 | ) 39 | } 40 | return newNotice 41 | }) 42 | return groupBy(newNotices, 'type') 43 | } 44 | 45 | const noticeData = getNoticeData(tempData) 46 | return ( 47 |
48 | {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, { 49 | className: 'trigger', 50 | onClick: () => { 51 | dispatch(bachSetState({ collapsed: !collapsed })) 52 | } 53 | })} 54 |
55 | { 59 | console.log(item, tabProps) 60 | }} 61 | loading={false} 62 | clearText="清空" 63 | viewMoreText="查看更多" 64 | onPopupVisibleChange={() => {}} 65 | onClear={() => {}} 66 | > 67 | 74 | 81 | 88 | 89 | { 95 | console.log('======info:', info) 96 | }} 97 | > 98 | }> 99 | 个人中心 100 | 101 | }> 102 | 设置 103 | 104 | 105 | }> 106 | 退出登录 107 | 108 | 109 | } 110 | > 111 | 112 | 117 | {'199******06'} 118 | 119 | 120 |
121 |
122 | ) 123 | } 124 | 125 | export default WorkSpaceHeader 126 | -------------------------------------------------------------------------------- /src/pages/workspace/components/menu.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import { useLocation, useNavigate } from 'react-router-dom' 4 | import { Layout, Menu } from 'antd' 5 | import { AppDispatch, RootState } from '@store' 6 | import { createMenuItems, routerMaps, getDefaultOpenKeys } from '@router' 7 | 8 | const { Sider } = Layout 9 | 10 | const LeftMenu = () => { 11 | const navigate = useNavigate() 12 | const { pathname } = useLocation() 13 | const { collapsed } = useSelector((state: RootState) => state.workspace) 14 | 15 | const childMenuItems = createMenuItems(routerMaps[0].children, '', false) 16 | 17 | const { subMenuKey, menuKey } = getDefaultOpenKeys(pathname) 18 | 19 | return ( 20 | 21 |
22 |

23 |
24 | navigate(item.key)} 32 | /> 33 | 34 | ) 35 | } 36 | 37 | export default LeftMenu 38 | -------------------------------------------------------------------------------- /src/pages/workspace/index.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | height: 100%; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | .content { 8 | height: 100%; 9 | background: #F3F3F3; 10 | padding:0 24px 24px 24px; 11 | box-sizing: border-box; 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/workspace/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import { Layout } from 'antd' 3 | import { Outlet } from 'react-router-dom' 4 | import CheckPermission from '@kits/checkPermission' 5 | 6 | import Header from './components/header' 7 | import LeftMenu from './components/menu' 8 | import Breadcrumbs from './components/breadcrumbs' 9 | import styles from './index.module.less' 10 | 11 | const { Content } = Layout 12 | 13 | const WorkSpace = (props: WorkSpaceProps) => { 14 | return ( 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ) 28 | } 29 | 30 | export default WorkSpace 31 | 32 | type WorkSpaceProps = PropsWithChildren<{}> 33 | -------------------------------------------------------------------------------- /src/pages/workspace/slice.ts: -------------------------------------------------------------------------------- 1 | import { PayloadAction, createSlice } from '@reduxjs/toolkit' 2 | import { WorkSpaceState } from './type' 3 | 4 | const initialState: WorkSpaceState = { 5 | menuName: '用户管理', 6 | subMenuKey: 'user', 7 | menuKey: 'userlist', 8 | collapsed: false 9 | } 10 | 11 | export const WorkSpaceSlice = createSlice({ 12 | name: 'workspace', 13 | initialState, 14 | reducers: { 15 | bachSetState: (state: WorkSpaceState, action: PayloadAction<{ [key: string]: unknown }>) => { 16 | const valObj = action.payload 17 | Object.keys(valObj).forEach(key => (state[key] = valObj[key])) 18 | } 19 | } 20 | }) 21 | 22 | export const { bachSetState } = WorkSpaceSlice.actions 23 | export default WorkSpaceSlice.reducer 24 | -------------------------------------------------------------------------------- /src/pages/workspace/type.ts: -------------------------------------------------------------------------------- 1 | export interface WorkSpaceState { 2 | menuName: string 3 | menuKey: string 4 | subMenuKey: string 5 | collapsed: boolean 6 | [key: string]: unknown 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/workspace/util.ts: -------------------------------------------------------------------------------- 1 | export const tempData = [ 2 | { 3 | id: '000000001', 4 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', 5 | title: '你收到了 14 份新周报', 6 | datetime: '2017-08-09', 7 | type: '通知' 8 | }, 9 | { 10 | id: '000000002', 11 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png', 12 | title: '你推荐的 曲妮妮 已通过第三轮面试', 13 | datetime: '2017-08-08', 14 | type: '通知' 15 | }, 16 | { 17 | id: '000000003', 18 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png', 19 | title: '这种模板可以区分多种通知类型', 20 | datetime: '2017-08-07', 21 | read: true, 22 | type: '通知' 23 | }, 24 | { 25 | id: '000000004', 26 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png', 27 | title: '左侧图标用于区分不同的类型', 28 | datetime: '2017-08-07', 29 | type: '通知' 30 | }, 31 | { 32 | id: '000000005', 33 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', 34 | title: '内容不要超过两行字,超出时自动截断', 35 | datetime: '2017-08-07', 36 | type: '通知' 37 | }, 38 | { 39 | id: '000000006', 40 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', 41 | title: '曲丽丽 评论了你', 42 | description: '描述信息描述信息描述信息', 43 | datetime: '2017-08-07', 44 | type: '消息' 45 | }, 46 | { 47 | id: '000000007', 48 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', 49 | title: '朱偏右 回复了你', 50 | description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', 51 | datetime: '2017-08-07', 52 | type: '消息' 53 | }, 54 | { 55 | id: '000000008', 56 | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg', 57 | title: '标题', 58 | description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像', 59 | datetime: '2017-08-07', 60 | type: '消息' 61 | }, 62 | { 63 | id: '000000009', 64 | title: '任务名称', 65 | description: '任务需要在 2017-01-12 20:00 前启动', 66 | extra: '未开始', 67 | status: 'todo', 68 | type: '待办' 69 | }, 70 | { 71 | id: '000000010', 72 | title: '第三方紧急代码变更', 73 | description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', 74 | extra: '马上到期', 75 | status: 'urgent', 76 | type: '待办' 77 | }, 78 | { 79 | id: '000000011', 80 | title: '信息安全考试', 81 | description: '指派竹尔于 2017-01-09 前完成更新并发布', 82 | extra: '已耗时 8 天', 83 | status: 'doing', 84 | type: '待办' 85 | }, 86 | { 87 | id: '000000012', 88 | title: 'ABCD 版本发布', 89 | description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务', 90 | extra: '进行中', 91 | status: 'processing', 92 | type: '待办' 93 | } 94 | ] 95 | 96 | export const getColor = (status: any) => { 97 | let color = '' 98 | switch (status) { 99 | case 'processing': 100 | color = 'blue' 101 | break 102 | case 'urgent': 103 | color = 'red' 104 | break 105 | case 'doing': 106 | color = 'gold' 107 | break 108 | } 109 | return color 110 | } 111 | -------------------------------------------------------------------------------- /src/router/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { RouteObject, createBrowserRouter } from 'react-router-dom' 3 | import type { ItemType } from 'antd/es/menu/hooks/useItems' 4 | import { routerMaps } from './routeMap' 5 | 6 | export declare type MenuRouteObject = { 7 | icon?: React.ReactNode 8 | label?: string 9 | children?: MenuRouteObject[] | null 10 | } & RouteObject 11 | 12 | export const createMenuItems = ( 13 | routers: MenuRouteObject[] | undefined, 14 | key: string, 15 | topMenuOnly?: boolean 16 | ): ItemType[] => { 17 | return routers 18 | ? routers.map(item => { 19 | return { 20 | key: `${key}/${item.path}`, 21 | icon: item.icon, 22 | children: !topMenuOnly && item.children ? createMenuItems(item.children, `${key}/${item.path}`, false) : null, 23 | label: item.label 24 | } as ItemType 25 | }) 26 | : [] 27 | } 28 | 29 | const getBreadcrumbs = (pathname: string): string[] => { 30 | const breadcrumbs: string[] = [] 31 | let tempRouters: MenuRouteObject[] = routerMaps[0].children! 32 | let matchedPath = '' 33 | while (tempRouters) { 34 | let isMatchedRouters = false 35 | for (const router of tempRouters) { 36 | const routerPath = `${matchedPath}/${router.path}` 37 | if (routerPath === pathname || pathname.includes(routerPath)) { 38 | breadcrumbs.push(router.label!) 39 | matchedPath = routerPath 40 | isMatchedRouters = true 41 | tempRouters = router.children as MenuRouteObject[] 42 | break 43 | } 44 | } 45 | if (!isMatchedRouters) { 46 | break 47 | } 48 | } 49 | return breadcrumbs 50 | } 51 | 52 | const getDefaultOpenKeys = (pathname: string) => { 53 | const pageRoutes = routerMaps[0].children! 54 | let subMenuKey = '', 55 | menuKey = '' 56 | for (const routeItem of pageRoutes) { 57 | if (pathname.includes(routeItem.path!)) { 58 | subMenuKey = `/${routeItem.path!}` 59 | 60 | for (const childMenuItem of routeItem.children || []) { 61 | const childPath = `/${routeItem.path!}/${childMenuItem.path!}` 62 | if (pathname === childPath) { 63 | menuKey = childPath 64 | } 65 | } 66 | if (!menuKey) break 67 | } 68 | } 69 | return { subMenuKey, menuKey } 70 | } 71 | 72 | const router = createBrowserRouter(routerMaps) 73 | export { router, getBreadcrumbs, routerMaps, getDefaultOpenKeys } 74 | -------------------------------------------------------------------------------- /src/router/routeMap.tsx: -------------------------------------------------------------------------------- 1 | import CheckLogin from '@kits/checkLogin' 2 | import Workspace from '@pages/workspace' 3 | import Error404 from '@pages/error404' 4 | import { DesktopOutlined, UserOutlined } from '@ant-design/icons' 5 | import lazyLoad from '@kits/lazyLoad' 6 | import React, { lazy } from 'react' 7 | import LoginPage from '@pages/login' 8 | import { MenuRouteObject } from './index' 9 | 10 | export const routerMaps: MenuRouteObject[] = [ 11 | { 12 | path: '/', 13 | element: ( 14 | 15 | 16 | 17 | ), 18 | errorElement: , 19 | children: [ 20 | { 21 | path: 'system', 22 | label: '系统管理', 23 | icon: , 24 | children: [ 25 | { 26 | path: 'userList', 27 | label: '用户管理', 28 | element: lazyLoad(lazy(() => import('@pages/user/user-list'))) 29 | } 30 | ] as MenuRouteObject[] 31 | }, 32 | { 33 | path: 'chart', 34 | label: '图表管理', 35 | icon: , 36 | children: [ 37 | { 38 | path: 'line', 39 | label: '折线图', 40 | element: lazyLoad(lazy(() => import('@pages/chart/chart-line'))) 41 | }, 42 | { 43 | path: 'pie', 44 | label: '折线图', 45 | element: lazyLoad(lazy(() => import('@pages/chart/chart-line'))) 46 | } 47 | ] as MenuRouteObject[] 48 | } 49 | ] as MenuRouteObject[] 50 | }, 51 | { 52 | path: '/login', 53 | element: 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import logger from 'redux-logger' 2 | import { configureStore } from '@reduxjs/toolkit' 3 | import rootReducer from './rootReducer' 4 | 5 | export const store = configureStore({ 6 | reducer: rootReducer, 7 | devTools: process.env.NODE_ENV !== 'production' 8 | }) 9 | 10 | export type AppDispatch = typeof store.dispatch 11 | 12 | export type RootState = ReturnType 13 | -------------------------------------------------------------------------------- /src/store/rootReducer.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from '@reduxjs/toolkit' 2 | import WorkSPaceSlice from '@pages/workspace/slice' 3 | 4 | const rootReducer = combineReducers({ 5 | workspace: WorkSPaceSlice 6 | }) 7 | 8 | export default rootReducer 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "baseUrl": ".", 15 | "paths": { 16 | "@pages/*" : ["src/pages/*"], 17 | "@comps" : ["src/components"], 18 | "@http" : ["src/http/fetch"], 19 | "@img/*" : ["src/assets/images/*"], 20 | "@router" : ["src/router"], 21 | "@kits": ["src/kits"], 22 | "@kits/*": ["src/kits/*"], 23 | "@store": ["src/store"] 24 | }, 25 | "resolveJsonModule": true, 26 | "isolatedModules": true, 27 | "noEmit": true, 28 | "jsx": "react-jsx" 29 | }, 30 | "include": ["./src/**/*", "./typings/*"] 31 | } 32 | -------------------------------------------------------------------------------- /typings/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly APP_MODE: string; 5 | readonly APP_DOCUMENT_TITLE: string; 6 | readonly APP_HOST_PORT: number; 7 | readonly APP_HOST_API: string; 8 | } 9 | 10 | interface ImportMeta { 11 | readonly env: ImportMetaEnv 12 | } 13 | -------------------------------------------------------------------------------- /typings/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.less' { 2 | const classes: { readonly [key: string]: string } 3 | export default classes 4 | } 5 | declare module '*.png' 6 | declare module 'ant-design-pro/lib/*' 7 | 8 | 9 | // @ts-ignore 10 | /* eslint-disable */ 11 | 12 | declare namespace API { 13 | type CurrentUser = { 14 | name?: string; 15 | avatar?: string; 16 | userid?: string; 17 | email?: string; 18 | signature?: string; 19 | title?: string; 20 | group?: string; 21 | tags?: { key?: string; label?: string }[]; 22 | notifyCount?: number; 23 | unreadCount?: number; 24 | country?: string; 25 | access?: string; 26 | geographic?: { 27 | province?: { label?: string; key?: string }; 28 | city?: { label?: string; key?: string }; 29 | }; 30 | address?: string; 31 | phone?: string; 32 | }; 33 | 34 | type LoginResult = { 35 | status?: string; 36 | type?: string; 37 | currentAuthority?: string; 38 | }; 39 | 40 | type PageParams = { 41 | current?: number; 42 | pageSize?: number; 43 | }; 44 | 45 | type RuleListItem = { 46 | key?: number; 47 | disabled?: boolean; 48 | href?: string; 49 | avatar?: string; 50 | name?: string; 51 | owner?: string; 52 | desc?: string; 53 | callNo?: number; 54 | status?: number; 55 | updatedAt?: string; 56 | createdAt?: string; 57 | progress?: number; 58 | }; 59 | 60 | type RuleList = { 61 | data?: RuleListItem[]; 62 | /** 列表的内容总数 */ 63 | total?: number; 64 | success?: boolean; 65 | }; 66 | 67 | type FakeCaptcha = { 68 | code?: number; 69 | status?: string; 70 | }; 71 | 72 | type LoginParams = { 73 | username?: string; 74 | password?: string; 75 | autoLogin?: boolean; 76 | type?: string; 77 | }; 78 | 79 | type ErrorResponse = { 80 | /** 业务约定的错误码 */ 81 | errorCode: string; 82 | /** 业务上的错误信息 */ 83 | errorMessage?: string; 84 | /** 业务上的请求是否成功 */ 85 | success?: boolean; 86 | }; 87 | 88 | type NoticeIconList = { 89 | data?: NoticeIconItem[]; 90 | /** 列表的内容总数 */ 91 | total?: number; 92 | success?: boolean; 93 | }; 94 | 95 | type NoticeIconItemType = 'notification' | 'message' | 'event'; 96 | 97 | type NoticeIconItem = { 98 | id?: string; 99 | extra?: string; 100 | key?: string; 101 | read?: boolean; 102 | avatar?: string; 103 | title?: string; 104 | status?: string; 105 | datetime?: string; 106 | description?: string; 107 | type?: NoticeIconItemType; 108 | }; 109 | } 110 | 111 | declare type Recordable = Record; 112 | 113 | declare interface GlobalEnv { 114 | APP_MODE: string; 115 | APP_DOCUMENT_TITLE: string; 116 | APP_HOST_PORT: number; 117 | APP_HOST_API: string; 118 | } 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv, UserConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import checker from 'vite-plugin-checker' 4 | import legacy from '@vitejs/plugin-legacy' 5 | import eslintPlugin from 'vite-plugin-eslint' 6 | import { viteMockServe } from 'vite-plugin-mock' 7 | import { createHtmlPlugin } from 'vite-plugin-html' 8 | import { createStyleImportPlugin, AntdResolve } from 'vite-plugin-style-import' 9 | import progress from 'vite-plugin-progress' 10 | import colors from 'picocolors' 11 | import * as path from 'path' 12 | import { wrapperEnv } from './src/kits/util/getEnv' 13 | 14 | // https://vitejs.dev/config/ 15 | export default defineConfig((): UserConfig => { 16 | const mockEnabled = (process.env.useMock as unknown as boolean) || false 17 | const env = loadEnv(process.env.appEnv!, process.cwd(), 'APP_') 18 | const viteEnv = wrapperEnv(env) 19 | 20 | return { 21 | plugins: [ 22 | react(), 23 | legacy({ 24 | targets: ['defaults', 'not IE 11'] 25 | }), 26 | checker({ 27 | typescript: true 28 | }), 29 | createHtmlPlugin({ 30 | entry: './src/index.tsx', 31 | inject: { 32 | data: { 33 | title: viteEnv.APP_DOCUMENT_TITLE 34 | } 35 | } 36 | }), 37 | eslintPlugin(), 38 | createStyleImportPlugin({ 39 | resolves: [AntdResolve()] 40 | }), 41 | viteMockServe({ 42 | mockPath: 'mock', 43 | enable: mockEnabled, 44 | watchFiles: true, 45 | logger: true 46 | }), 47 | progress({ 48 | format: `${colors.green(colors.bold('Building'))} ${colors.cyan('[:bar]')} :percent` 49 | }) 50 | ], 51 | resolve: { 52 | alias: { 53 | '@pages': path.resolve(__dirname, './src/pages'), 54 | '@comps': path.resolve(__dirname, './src/components'), 55 | '@http': path.resolve(__dirname, './src/http/fetch'), 56 | '@img': path.resolve(__dirname, './src/assets/images'), 57 | '@kits': path.resolve(__dirname, './src/kits'), 58 | '@router': path.resolve(__dirname, './src/router'), 59 | '@store': path.resolve(__dirname, './src/store') 60 | } 61 | }, 62 | css: { 63 | preprocessorOptions: { 64 | less: { 65 | javascriptEnabled: true, 66 | modifyVars: { 67 | '@primary-color': '#4377FE' //设置antd主题色 68 | } 69 | } 70 | } 71 | }, 72 | envPrefix: 'APP_', 73 | server: { 74 | port: 3000, 75 | open: 'http://localhost:3000/system/userList', 76 | cors: true, 77 | proxy: { 78 | // string shorthand: http://localhost:5173/foo -> http://localhost:4567/foo 79 | '/foo': 'http://localhost:4567', 80 | // with RegExp: http://localhost:5173/fallback/ -> http://jsonplaceholder.typicode.com/ 81 | '^/fallback/.*': { 82 | target: 'http://jsonplaceholder.typicode.com', 83 | changeOrigin: true, 84 | rewrite: path => path.replace(/^\/fallback/, '') 85 | } 86 | } 87 | }, 88 | build: { 89 | outDir: 'dist', 90 | rollupOptions: { 91 | output: { 92 | chunkFileNames: 'assets/js/[name]-[hash].js', 93 | entryFileNames: 'assets/js/[name]-[hash].js', 94 | assetFileNames: 'assets/[ext]/[name]-[hash].[ext]' 95 | } 96 | } 97 | } 98 | } 99 | }) 100 | --------------------------------------------------------------------------------