> | string;
19 | export default content;
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/Exception/locales/zh-cn.ts:
--------------------------------------------------------------------------------
1 | const zh: {
2 | [k: string]: string;
3 | } = {
4 | 'menu.exception': '异常页',
5 | 'menu.exception.403': '403',
6 | 'exception.result.403.description': '对不起,您没有访问该资源的权限',
7 | 'exception.result.403.back': '返回',
8 |
9 | 'menu.exception.404': '404',
10 | 'exception.result.404.description': '抱歉,页面不见了~',
11 | 'exception.result.404.retry': '重试',
12 | 'exception.result.404.back': '返回',
13 |
14 | 'menu.exception.500': '500',
15 | 'exception.result.500.retry': '重试',
16 | 'exception.result.500.description': '抱歉,服务器出了点问题~',
17 | 'exception.result.500.back': '返回',
18 | };
19 |
20 | export default zh;
21 |
--------------------------------------------------------------------------------
/src/Layout/Header/index.module.less:
--------------------------------------------------------------------------------
1 | .header {
2 | display: flex;
3 | justify-content: space-between;
4 | width: 100%;
5 | }
6 |
7 | .logo {
8 | display: flex;
9 | align-items: center;
10 | width: auto;
11 | cursor: pointer;
12 | color: var(--color-text-1);
13 | }
14 |
15 | .ul {
16 | display: flex;
17 | list-style: none;
18 |
19 | & > li {
20 | padding-right: 20px;
21 | }
22 | }
23 |
24 | .fullscreen {
25 | display: flex;
26 | align-items: center;
27 | cursor: pointer;
28 | }
29 |
30 | .avatar {
31 | cursor: pointer;
32 | }
33 |
34 | .themeStyle {
35 | background: var(--theme-color);
36 | color: #fff;
37 | }
38 |
--------------------------------------------------------------------------------
/src/app/Result/locales/en-us.ts:
--------------------------------------------------------------------------------
1 | const en: {
2 | [k: string]: string
3 | } = {
4 | 'menu.result': 'Result page',
5 |
6 | 'menu.result.success': 'success',
7 | 'menu.result.success.title': 'Success message',
8 | 'menu.result.success.subTitle': 'This is a success description.',
9 | 'menu.result.success.again': 'Again',
10 | 'menu.result.success.back': 'Back',
11 |
12 | 'menu.result.error': 'fail',
13 | 'menu.result.error.title': 'Error message',
14 | 'menu.result.error.subTitle': 'Something went wrong. Please try again.',
15 | 'menu.result.error.again': 'Again',
16 | 'menu.result.error.back': 'Back',
17 | }
18 |
19 | export default en
20 |
--------------------------------------------------------------------------------
/src/app/Exception/403.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from '@arco-design/web-react';
2 | import React from 'react';
3 | import useI18n from 'src/ahooks/useI18n';
4 | import locale from './locales';
5 |
6 | const Result403 = () => {
7 | const { i18n, lang } = useI18n(locale);
8 | return (
9 |
10 |
15 | {i18n[lang]['exception.result.403.back']}
16 |
17 | }
18 | />
19 |
20 | );
21 | };
22 | export default Result403;
23 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | const Webpack = require('webpack')
2 | const WebpackDevServer = require('webpack-dev-server')
3 | // webpack开发 配置文件
4 | const webpackConfig = require('../build/webpack.dev')
5 | // 自定义日志输出
6 | const logger = require('./logger')
7 | // 服务配置
8 | const { port, host } = webpackConfig.devServer // 监听的端口号
9 | // 编译器
10 | const compiler = Webpack(webpackConfig)
11 | // devServer 参数
12 | const devServerOptions = Object.assign({}, webpackConfig.devServer) // devServer配置
13 | const server = new WebpackDevServer(compiler, devServerOptions)
14 |
15 | server.listen(port, host, async (err) => {
16 | if (err) {
17 | return logger.error(err.message)
18 | }
19 | logger.appStarted(port, host)
20 | })
21 |
--------------------------------------------------------------------------------
/src/app/Exception/locales/en-us.ts:
--------------------------------------------------------------------------------
1 | const en: {
2 | [k: string]: string;
3 | } = {
4 | 'menu.exception': 'Exception page',
5 | 'menu.exception.403': '403',
6 | 'exception.result.403.description': 'Access to this resource on the server is denied.',
7 | 'exception.result.403.back': 'Back',
8 |
9 | 'menu.exception.404': '404',
10 | 'exception.result.404.description': 'Whoops, this page is gone.',
11 | 'exception.result.404.retry': 'Retry',
12 | 'exception.result.404.back': 'Back',
13 |
14 | 'menu.exception.500': '500',
15 | 'exception.result.500.retry': 'Retry',
16 | 'exception.result.500.description': 'Internal server error',
17 | 'exception.result.500.back': 'Back',
18 | };
19 |
20 | export default en;
21 |
--------------------------------------------------------------------------------
/src/app/Login/modules/Banner.tsx:
--------------------------------------------------------------------------------
1 | import { Carousel } from '@arco-design/web-react';
2 | import React from 'react';
3 |
4 | const Banner = () => {
5 | const list = ['人生在勤,不索何获.', '三十以前,不要怕;三十以后,不要悔。', '真理是永远蒙蔽不了的。'];
6 | return (
7 |
8 |
14 | {list.map((item, index) => (
15 |
16 |
{item}
17 |
18 | ))}
19 |
20 |
21 | );
22 | };
23 |
24 | export default Banner;
25 |
--------------------------------------------------------------------------------
/src/index.less:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 16px;
3 | }
4 | .flex {
5 | display: flex;
6 | }
7 |
8 | .align-items-center {
9 | align-items: center;
10 | }
11 |
12 | .justify-content-center {
13 | justify-content: center;
14 | }
15 |
16 | .justify-content-between {
17 | justify-content: between;
18 | }
19 |
20 | .justify-content-center {
21 | justify-content: center;
22 | }
23 |
24 | .for-loop(@index) when (@index > 0) {
25 | .ml-@{index} {
26 | margin-left: (1px * @index);
27 | }
28 | .mr-@{index} {
29 | margin-right: (1px * @index);
30 | }
31 | .mt-@{index} {
32 | margin-top: (1px * @index);
33 | }
34 | .mb-@{index} {
35 | margin-bottom: (1px * @index);
36 | }
37 | .for-loop(@index - 1);
38 | }
39 |
40 | .for-loop(20);
41 |
--------------------------------------------------------------------------------
/template/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | React Arco Admin中台管理系统
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/app/Exception/500.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from '@arco-design/web-react';
2 | import React from 'react';
3 | import useI18n from 'src/ahooks/useI18n';
4 | import locale from './locales';
5 |
6 | const Result500 = () => {
7 | const { lang, i18n } = useI18n(locale);
8 | return (
9 |
10 | {i18n[lang]['exception.result.500.retry']},
15 | ,
18 | ]}
19 | />
20 |
21 | );
22 | };
23 | export default Result500;
24 |
--------------------------------------------------------------------------------
/src/app/Exception/404.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Result } from '@arco-design/web-react';
2 | import React from 'react';
3 | import useI18n from 'src/ahooks/useI18n';
4 | import locale from './locales';
5 |
6 | const Result404 = () => {
7 | const { lang, i18n } = useI18n(locale);
8 |
9 | return (
10 |
11 | {i18n[lang]['exception.result.404.retry']},
16 | ,
19 | ]}
20 | />
21 |
22 | );
23 | };
24 | export default Result404;
25 |
--------------------------------------------------------------------------------
/src/assets/images/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/Layout/Bread/Bread.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { Breadcrumb } from '@arco-design/web-react';
3 | import { IconHome } from '@arco-design/web-react/icon';
4 | const BreadcrumbItem = Breadcrumb.Item;
5 |
6 | const Bread = () => {
7 | useEffect(() => {
8 | getBreadListByHostName()
9 | }, [])
10 |
11 | const getBreadListByHostName = () => {
12 | // const hostname = window.location.hostname
13 | // console.log(hostname, '??')
14 | }
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | Channel
22 | News
23 |
24 |
25 | )
26 | }
27 |
28 | export default Bread
29 |
--------------------------------------------------------------------------------
/src/ahooks/index.ts:
--------------------------------------------------------------------------------
1 | import { useLocalStorageState } from 'ahooks';
2 | import { useEffect } from 'react';
3 | import { changePageColor } from 'src/utils'
4 |
5 | export const useTheme = () => {
6 | const [arcoThem] = useLocalStorageState('arco-theme');
7 | useEffect(() => {
8 | if (arcoThem) {
9 | document.body.setAttribute('arco-theme', 'dark');
10 | } else {
11 | document.body.setAttribute('arco-theme', '');
12 | }
13 | }, []);
14 | };
15 |
16 | export const useColor = () => {
17 | const [arcoThemColor, setArcoThemColor] = useLocalStorageState('arco-theme-color');
18 | useEffect(() => {
19 | if (!arcoThemColor) {
20 | setArcoThemColor('#873bf4')
21 | }
22 |
23 | changePageColor(arcoThemColor || '#873bf4')
24 | }, [arcoThemColor]);
25 | return [arcoThemColor, setArcoThemColor];
26 | };
27 |
--------------------------------------------------------------------------------
/src/app/Result/Error.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Result, Button } from '@arco-design/web-react'
3 | import useI18n from 'src/ahooks/useI18n'
4 | import locale from './locales'
5 |
6 | const Error = () => {
7 | const { lang, i18n } = useI18n(locale)
8 | return (
9 |
10 |
16 | {i18n[lang]['menu.result.error.again']}
17 | ,
18 | ,
21 | ]}
22 | />
23 |
24 | )
25 | }
26 |
27 | export default Error
28 |
--------------------------------------------------------------------------------
/src/app/Result/Success.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Result, Button } from '@arco-design/web-react'
3 | import useI18n from 'src/ahooks/useI18n'
4 | import locale from './locales'
5 |
6 | const Success = () => {
7 | const { lang, i18n } = useI18n(locale)
8 | return (
9 |
10 |
16 | {i18n[lang]['menu.result.success.again']}
17 | ,
18 | ,
21 | ]}
22 | />
23 |
24 | )
25 | }
26 |
27 | export default Success
28 |
--------------------------------------------------------------------------------
/template/js/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: yaogeng.zhu
3 | * @Date: 2021-10-08 10:17:01
4 | * @Last Modified by: yaogeng.zhu
5 | * @Last Modified time: 2022-08-12 13:17:24
6 | * 存放一些其他的js脚本
7 | */
8 | console.log('%c%s', 'color: red; background: yellow; font-size: 24px;', '数据埋点!')
9 |
10 | function buriedPoint() {
11 | const timingInfo = window.performance.timing
12 | const { domLoading, fetchStart } = timingInfo
13 | const time = domLoading - fetchStart
14 | console.log(time + 'ms', '白屏时间')
15 | console.log(timingInfo, '时间统计数据')
16 | }
17 |
18 | window.onload = function () {
19 | buriedPoint()
20 |
21 | window.addEventListener('hashchange', buriedPoint)
22 | history.pushState = new Proxy(history.pushState, {
23 | apply: function (target, thisBinding, args) {
24 | buriedPoint()
25 | return target.apply(thisBinding, args)
26 | },
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/src/app/Welcome/index.module.less:
--------------------------------------------------------------------------------
1 | .container {
2 | background: lightblue;
3 | cursor: pointer;
4 | flex: 1;
5 | display: flex;
6 | align-items: center;
7 | height: 100%;
8 | justify-content: center;
9 | overflow: hidden;
10 | }
11 | .deck {
12 | position: absolute;
13 | width: 300px;
14 | height: 200px;
15 | will-change: transform;
16 | display: flex;
17 | align-items: center;
18 | justify-content: center;
19 | touch-action: none;
20 | }
21 |
22 | .deck > div {
23 | background-color: white;
24 | background-size: auto 85%;
25 | background-repeat: no-repeat;
26 | background-position: center center;
27 | width: 45vh;
28 | max-width: 150px;
29 | height: 85vh;
30 | max-height: 285px;
31 | will-change: transform;
32 | border-radius: 10px;
33 | box-shadow: 0 12.5px 100px -10px rgba(50, 50, 73, 0.4), 0 10px 10px -10px rgba(50, 50, 73, 0.3);
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/WorkPlace.tsx:
--------------------------------------------------------------------------------
1 | import { Grid } from '@arco-design/web-react';
2 | import React from 'react';
3 | import styles from './index.module.less';
4 | import Banner from './modules/slide/banner';
5 | import ContentView from './modules/slide/contentView'
6 | import Overview from './modules/slide/overview';
7 | import ShortCuts from './modules/slide/shortcuts';
8 |
9 | const { Row, Col } = Grid;
10 |
11 | const Workplace = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default Workplace;
29 |
--------------------------------------------------------------------------------
/src/app/Login/mock/user.ts:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs';
2 | import { uuid } from 'src/utils'
3 | import setupMock from 'src/utils/setupMock';
4 |
5 | setupMock({
6 | setup: () => {
7 | Mock.mock(new RegExp('/api/user/login'), (params: { body: string }) => {
8 | const { username, password } = JSON.parse(params.body);
9 | if (!username) {
10 | return {
11 | status: 'error',
12 | msg: '用户名不能为空',
13 | };
14 | }
15 | if (!password) {
16 | return {
17 | status: 'error',
18 | msg: '密码不能为空',
19 | };
20 | }
21 | if (username === 'admin' && password === 'admin') {
22 | return {
23 | status: 'ok',
24 | data: {
25 | token: uuid(),
26 | },
27 | };
28 | }
29 | return {
30 | status: 'error',
31 | msg: '账号或者密码错误',
32 | };
33 | });
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/server/logger.js:
--------------------------------------------------------------------------------
1 | const ip = require('ip')
2 | const chalk = require('chalk')
3 | const divider = chalk.gray('\n-----------------------------------')
4 | const logger = {
5 | error: (err) => {
6 | console.error(chalk.red(err))
7 | },
8 | appStarted: (port, host, tunnelStarted) => {
9 | console.log(`Server started ! ${chalk.green('✓')}`)
10 | if (tunnelStarted) {
11 | console.log(`Tunnel initialised ${chalk.green('✓')}`)
12 | }
13 | console.log(`
14 | ${chalk.bold('Access URLs:')}${divider}
15 | Localhost: ${chalk.magenta(`${process.env.REACT_APP_SERVER_DOMAIN}:${port}`)}
16 | 本地IP地址: ${chalk.magenta(`http://${ip.address()}:${port}`) +
17 | (tunnelStarted ? `\n Proxy: ${chalk.magenta(tunnelStarted)}` : '')}
18 | ${divider}
19 | ${chalk.blue(`Press ${chalk.italic('CTRL-C')} to stop`)}
20 | `)
21 | },
22 | }
23 |
24 | module.exports = logger
25 |
--------------------------------------------------------------------------------
/src/app/Login/index.module.less:
--------------------------------------------------------------------------------
1 | .login {
2 | width: 100%;
3 | display: flex;
4 | height: 100vh;
5 | overflow: hidden;
6 | }
7 |
8 | .logo {
9 | position: absolute;
10 | left: 20px;
11 | top: 10px;
12 | z-index: 2;
13 | color: #fff;
14 | }
15 |
16 | .left {
17 | display: flex;
18 | align-items: center;
19 | justify-content: center;
20 | background: linear-gradient(163.85deg, #1d2129, #00308f);
21 | width: 550px;
22 | }
23 |
24 | .item {
25 | display: flex;
26 | align-items: center;
27 | justify-content: center;
28 | }
29 |
30 | .right {
31 | flex: 1;
32 | display: flex;
33 | align-items: center;
34 | justify-content: center;
35 | padding-bottom: 40px;
36 | background: var(--color-bg-3);
37 | }
38 |
39 | .title {
40 | margin-bottom: 28px;
41 | font-size: 24px;
42 | font-weight: bold;
43 | color: var(--color-text-1);
44 | }
45 |
46 | .footer {
47 | position: absolute;
48 | bottom: 10px;
49 | text-align: center;
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/style/shortcuts.less:
--------------------------------------------------------------------------------
1 | @class-prefix-shortcuts: ~'shortcuts';
2 |
3 | .@{class-prefix-shortcuts} {
4 | &-header {
5 | display: flex;
6 | justify-content: space-between;
7 | }
8 |
9 | &-list {
10 | display: grid;
11 | grid-template-columns: 33.33% 33.33% 33.33%;
12 | }
13 |
14 | &-item {
15 | align-items: center;
16 | box-sizing: border-box;
17 | cursor: pointer;
18 | display: flex;
19 | flex-direction: column;
20 | justify-content: center;
21 | padding: 12px;
22 |
23 | &:hover {
24 | background-color: rgba(0, 0, 0, 0.05);
25 | }
26 | }
27 |
28 | &-title {
29 | font-size: 12px;
30 | line-height: 20px;
31 | color: var(--color-text-1);
32 | }
33 |
34 | &-icon {
35 | align-items: center;
36 | background-color: var(--color-fill-2);
37 | border-radius: 6px;
38 | display: flex;
39 | height: 32px;
40 | justify-content: center;
41 | margin-bottom: 4px;
42 | width: 32px;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Layout/MainRoute.tsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense, useEffect } from 'react';
2 | import { Route, Routes } from 'react-router-dom';
3 | import Loading from 'src/components/Loading/Loading';
4 | import RouteConfig, { IRouterConfig } from '../conifg/routerConfig';
5 | import nprogress from 'nprogress';
6 | import '@/assets/css/nprogress.less';
7 | import Welcome from 'src/app/Welcome'
8 |
9 | const LazyLoad = () => {
10 | useEffect(() => {
11 | nprogress.configure({ showSpinner: false });
12 | nprogress.start();
13 | return () => {
14 | nprogress.done();
15 | };
16 | }, []);
17 |
18 | return ;
19 | };
20 |
21 | const getRouter = () => {
22 | return RouteConfig.map((item: IRouterConfig) => {
23 | return ;
24 | });
25 | };
26 |
27 | const MainRoute = () => {
28 | return (
29 | }>
30 |
31 | } />
32 | {getRouter()}
33 |
34 |
35 | );
36 | };
37 | export default MainRoute;
38 |
--------------------------------------------------------------------------------
/.github/workflows/prview-build.yml:
--------------------------------------------------------------------------------
1 | name: Preview Build
2 | on:
3 | pull_request:
4 |
5 | jobs:
6 | build-preview:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - name: Check
11 | uses: actions/checkout@v3
12 | with:
13 | ref: ${{ github.event.pull_request.head.sha }}
14 |
15 | - name: Install Node.js
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: '16'
19 |
20 | - name: Install dependencies
21 | run: npm install --force
22 |
23 | - name: Build project
24 | run: npm run build
25 |
26 | # 产物压缩
27 | - run: |
28 | zip -r dist.zip dist
29 |
30 | - name: Archive artifact
31 | uses: actions/upload-artifact@v2
32 | with:
33 | name: dist
34 | path: dist.zip
35 | retention-days: 5
36 |
37 | - name: Save PR number
38 | if: ${{ always() }}
39 | run: echo ${{ github.event.number }} > ./pr-id.txt
40 |
41 | - name: Upload PR number
42 | if: ${{ always() }}
43 | uses: actions/upload-artifact@v2
44 | with:
45 | name: pr
46 | path: ./pr-id.txt
47 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/banner.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Carousel } from '@arco-design/web-react';
3 |
4 | const imageSrc = [
5 | '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/94e8dd2d6dc4efb2c8cfd82c0ff02a2c.jpg~tplv-uwbnlip3yd-webp.webp',
6 | '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/94e8dd2d6dc4efb2c8cfd82c0ff02a2c.jpg~tplv-uwbnlip3yd-webp.webp',
7 | '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/94e8dd2d6dc4efb2c8cfd82c0ff02a2c.jpg~tplv-uwbnlip3yd-webp.webp',
8 | '//p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/94e8dd2d6dc4efb2c8cfd82c0ff02a2c.jpg~tplv-uwbnlip3yd-webp.webp',
9 | ];
10 | function Banner() {
11 | return (
12 |
22 | {imageSrc.map((src, index) => (
23 |
24 |

31 |
32 | ))}
33 |
34 | );
35 | }
36 |
37 | export default Banner;
38 |
--------------------------------------------------------------------------------
/src/locales/zh-cn.ts:
--------------------------------------------------------------------------------
1 | const zh: {
2 | [k: string]: string
3 | } = {
4 | 'header.toggletoLight': '点击切换为亮色模式',
5 | 'header.toggletoDark': '点击进入暗黑模式',
6 | 'header.enterFullScreenMode': '进入全屏模式',
7 | 'header.leaveFullScreenMode': '退出全屏模式',
8 | 'header.userSetting': '用户设置',
9 | 'header.logout': '退出登录',
10 |
11 | 'menu.dashboard': '仪表盘',
12 | 'menu.dashboard.workplace': '工作台',
13 | 'menu.dashboard.monitor': '实时监控',
14 | 'menu.visualization': '数据可视化',
15 | 'menu.visualization.dataAnalysis': '分析页',
16 | 'menu.visualization.multiDimensionDataAnalysis': '多维数据分析',
17 | 'menu.list': '列表页',
18 | 'menu.list.searchTable': '查询表格',
19 | 'menu.list.cardList': '卡片列表',
20 | 'menu.profile': '详情页',
21 | 'menu.profile.basic': '基础详情页',
22 | 'menu.result': '结果页',
23 | 'menu.result.success': '成功页',
24 | 'menu.result.error': '失败页',
25 | 'menu.exception': '异常页',
26 | 'menu.exception.403': '403',
27 | 'menu.exception.404': '404',
28 | 'menu.exception.500': '500',
29 | 'menu.user': '个人中心',
30 | 'menu.user.info': '用户信息',
31 | 'menu.user.setting': '用户设置',
32 |
33 |
34 | 'system.tip.ok': '确定',
35 | 'system.tip.cancel': '取消',
36 | 'system.tip.config': '页面基本配置',
37 | 'system.tip.themColor': 'theme color'
38 | };
39 |
40 | export default zh;
41 |
--------------------------------------------------------------------------------
/src/Layout/index.less:
--------------------------------------------------------------------------------
1 | .layout-collapse {
2 | height: 100vh;
3 | border: 1px solid var(--color-border);
4 | overflow: hidden;
5 | }
6 |
7 | .layout-collapse .layout-main {
8 | background: var(--color-bg-1);
9 | min-width: 1100px;
10 | }
11 |
12 | .layout-collapse .header {
13 | width: 100%;
14 | height: 60px;
15 | position: fixed;
16 | top: 0;
17 | z-index: 2;
18 | background: var(--color-bg-2);
19 | border-bottom: 1px solid var(--color-border);
20 | box-sizing: border-box;
21 | }
22 |
23 | .layout-collapse .layout-content {
24 | background: var(--color-fill-2);
25 | }
26 |
27 | .layout-collapse .arco-layout-content {
28 | display: flex;
29 | align-items: center;
30 | justify-content: center;
31 | background: var(--color-bg-1);
32 | font-stretch: condensed;
33 | font-size: 16px;
34 | }
35 |
36 | .layout-collapse .arco-layout-footer {
37 | color: var(--color-text-2);
38 | text-align: center;
39 | height: 48px;
40 | line-height: 48px;
41 | font-weight: 400;
42 | font-size: 14px;
43 | }
44 |
45 | // .layout-collapse .arco-layout-content {
46 | // color: var(--color-text-2);
47 | // font-weight: 400;
48 | // font-size: 14px;
49 | // }
50 |
51 | .layout-collapse .arco-layout-header .trigger {
52 | margin-left: 20px;
53 | }
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React-arco-admin
2 |
3 | node 版本需要在v14之上
4 |
5 | ```bash
6 | pnpm i
7 |
8 | pnpm start
9 |
10 | ```
11 |
12 |
13 | ## 该系统技术栈特点
14 | - 基于webpack5.x配置
15 | - eslint严格代码规范
16 | - prettier统一代码风格
17 | - husky代码提交检测
18 | - 环境变量配置规范化
19 |
20 | ## 技术框架
21 | - [x] react18
22 | - [x] react-dom
23 | - [x] react-router-dom 6.x
24 | - [x] react-hook-form
25 | - [x] hooks
26 | - [x] arco-design
27 |
28 | ## 代码规范
29 | - [x] eslint
30 | - [x] prettier
31 | - [x] husky
32 |
33 | 代码x层面需要对自己严格要去的规范
34 |
35 |
36 | ## 菜单结构
37 |
38 |
39 |
40 | ## 基础工程 TodoList
41 |
42 | - [x] 脚手架
43 | - [x] 国际化
44 | - [x] 主题配置
45 | - [x] CI/CD
46 | - [x] jenkins自动构建
47 | - [ ] 待补充
48 | - ...
49 |
50 | ## 页面 TodoList
51 | - [ ] 仪表盘
52 | - [x] 工作台
53 | - [ ] 实时监控
54 | - [ ] 数据可视化
55 | - [ ] 分析页
56 | - [ ] 多位数据分析
57 | - [ ] 列表页
58 | - [ ] 查询表格
59 | - [ ] 卡片列表
60 | - [ ] 表单页
61 | - [ ] 分组表单
62 | - [ ] 分布表单
63 | - [x] 结果页
64 | - [x] 成功页
65 | - [x] 失败页
66 | - [x] 异常页
67 | - [x] 403
68 | - [x] 404
69 | - [x] 500
70 | - [ ] 个人中心
71 | - [ ] 用户信息
72 | - [ ] 用户设置
73 |
74 |
75 | ## 支持打包产物上传至COS
76 |
77 | 在腾讯云网在购买COS服务,然后在项目根目录下创建.env文件,配置如下
78 |
79 | ```bash
80 | SecretId=xxxx
81 | SecretKey=xxxx
82 | Bucket=xxxx
83 | Region=xxx
84 | ```
85 |
86 | 安装腾讯云cos-nodejs-sdk-v5
87 |
88 | ```bash
89 | pnpm add cos-nodejs-sdk-v5
90 | ```
91 |
92 | 具体上传代码可参考 build/upload.js
--------------------------------------------------------------------------------
/src/locales/en-us.ts:
--------------------------------------------------------------------------------
1 | const en: {
2 | [k: string]: string;
3 | } = {
4 | 'header.toggletoLight': 'Click to switch to light mode',
5 | 'header.toggletoDark': 'Click to switch to dark mode',
6 | 'header.enterFullScreenMode': 'Full screen',
7 | 'header.leaveFullScreenMode': 'Mini screen',
8 | 'header.userSetting': 'User Setting',
9 | 'header.logout': 'Logout',
10 | 'menu.dashboard': 'Dashboard',
11 | 'menu.dashboard.workplace': 'Workplace',
12 | 'menu.dashboard.monitor': 'Monitor',
13 | 'menu.visualization': 'Data Visualization',
14 | 'menu.visualization.dataAnalysis': 'Analysis',
15 | 'menu.visualization.multiDimensionDataAnalysis': 'Multi-D Analysis',
16 | 'menu.list': 'List',
17 | 'menu.list.searchTable': 'Search Table',
18 | 'menu.list.cardList': 'Card List',
19 | 'menu.profile': 'Profile',
20 | 'menu.profile.basic': 'Basic Profile',
21 | 'menu.result': 'Result',
22 | 'menu.result.success': 'Success',
23 | 'menu.result.error': 'Error',
24 | 'menu.exception': 'Exception',
25 | 'menu.exception.403': '403',
26 | 'menu.exception.404': '404',
27 | 'menu.exception.500': '500',
28 | 'menu.user': 'User Center',
29 | 'menu.user.info': 'User Info',
30 | 'menu.user.setting': 'User Setting',
31 |
32 | 'system.tip.ok': 'ok',
33 | 'system.tip.cancel': 'cancel',
34 | 'system.tip.config': 'page basic config',
35 | 'system.tip.themColor': 'theme color'
36 | };
37 |
38 | export default en;
39 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/style/overview.module.less:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 20px;
3 |
4 | :global(.arco-divider-horizontal) {
5 | border-bottom: 1px solid var(--color-border-1);
6 | }
7 |
8 | :global(.arco-divider-vertical) {
9 | border-left: 1px solid var(--color-border-1);
10 | }
11 | }
12 |
13 | .item {
14 | display: flex;
15 | align-items: center;
16 | padding-left: 20px;
17 | color: var(--color-text-1);
18 | }
19 |
20 | .icon {
21 | display: flex;
22 | align-items: center;
23 | justify-content: center;
24 | width: 54px;
25 | height: 54px;
26 | background-color: var(--color-fill-2);
27 | border-radius: 50%;
28 | margin-right: 12px;
29 | }
30 |
31 | .title {
32 | font-size: 12px;
33 | color: var(--color-text-1);
34 | }
35 |
36 | .count {
37 | font-size: 22px;
38 | font-weight: 600;
39 | color: var(--color-text-1);
40 |
41 | .unit {
42 | font-size: 12px;
43 | font-weight: 400;
44 | color: var(--color-text-2);
45 | margin-left: 8px;
46 | }
47 | }
48 |
49 | .divider {
50 | height: 60px;
51 | }
52 |
53 | .ctw {
54 | display: flex;
55 | justify-content: space-between;
56 | align-items: center;
57 | margin-bottom: 16px;
58 | }
59 |
60 | .chart-title {
61 | font-size: 16px;
62 | font-weight: 500;
63 | }
64 |
65 | .chart-sub-title {
66 | font-size: 12px;
67 | font-weight: 400;
68 | margin-left: 4px;
69 | color: var(--color-text-3);
70 | }
71 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
4 | "diagnostics": true, // 打印诊断信息
5 | "removeComments": true, // 移除代码注释
6 | "sourceMap": true,
7 | "types": ["react"],
8 | // "types": ["react/next", "react-dom/next"],
9 | // 基本配置
10 | "target": "ES5", // 编译成哪个版本的 es
11 | "module": "ESNext", // 指定生成哪个模块系统代码
12 | "lib": ["dom", "dom.iterable", "esnext"], // 编译过程中需要引入的库文件的列表
13 | "allowJs": true, // 允许编译 js 文件
14 | "jsx": "react", // 在 .tsx 文件里支持 JSX
15 | "isolatedModules": true, // 提供额外的一些语法检查,如文件没有模块导出会报错
16 | "strict": true, // 启用所有严格类型检查选项
17 | "noImplicitAny": true, // 不允许隐式的 any 类型
18 | // 模块解析选项
19 | "moduleResolution": "node", // 指定模块解析策略
20 | "esModuleInterop": true, // 支持 CommonJS 和 ES 模块之间的互操作性
21 | "resolveJsonModule": true, // 支持导入 json 模块
22 | "baseUrl": "./", // 根路径
23 | "paths": {
24 | // 路径映射,与 baseUrl 关联
25 | "src/*": ["src/*"],
26 | "components/*": ["src/components/*"],
27 | "utils/*": ["src/utils/*"]
28 | },
29 |
30 | // 实验性选项
31 | "experimentalDecorators": true, // 启用实验性的ES装饰器
32 | "emitDecoratorMetadata": true, // 给源码里的装饰器声明加上设计类型元数据
33 |
34 | // 其他选项
35 | "forceConsistentCasingInFileNames": true, // 禁止对同一个文件的不一致的引用
36 | "skipLibCheck": true, // 忽略所有的声明文件( *.d.ts)的类型检查
37 | "allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入
38 | "noEmit": true // 只想使用tsc的类型检查作为函数时(当其他工具(例如Babel实际编译)时)使用它
39 | },
40 | "exclude": ["node_modules"]
41 | }
42 |
--------------------------------------------------------------------------------
/Develop-problem-change.md:
--------------------------------------------------------------------------------
1 | ### css Module 处理
2 |
3 | > 问题:
4 |
5 | 解决css冲突
6 |
7 | 原有解决方案:
8 | BEM --- JS
9 |
10 | ```less
11 | @class-prefix-header: ~'header';
12 |
13 | .@{class-prefix-header} {
14 | display: flex;
15 | justify-content: space-between;
16 | width: 100%;
17 | &-ul {
18 | display: flex;
19 | list-style: none;
20 |
21 | & > li {
22 | padding-right: 20px;
23 | }
24 | }
25 |
26 | &-logo {
27 | display: flex;
28 | align-items: center;
29 | width: auto;
30 | cursor: pointer;
31 | color: var(--color-text-1);
32 | }
33 |
34 | &-fullscreen {
35 | display: flex;
36 | align-items: center;
37 | cursor: pointer;
38 | }
39 |
40 | &-avatar {
41 | cursor: pointer;
42 | }
43 | }
44 | ```
45 |
46 | 新的解决方案。webpack 配置css Module。同时兼容之前的处理方案, 配置如下
47 |
48 | ```js
49 | {
50 | test: /\.less$/i,
51 | use: [
52 | {
53 | loader: 'style-loader',
54 | },
55 | {
56 | loader: 'css-loader',
57 | options: {
58 | modules: {
59 | auto: (resourcePath) => resourcePath.endsWith('.module.less'), // 兼容之前的处理。只针对 .module.less 处理
60 | localIdentName: '[local]___[hash:base64:5]'
61 | }
62 | }
63 | },
64 | {
65 | loader: 'less-loader',
66 | options: {
67 | lessOptions: {
68 | modifyVars: {
69 | 'arcoblue-6': '#3491FA',
70 | },
71 | javascriptEnabled: true
72 | },
73 | },
74 | },
75 | ],
76 | },
77 | ```
--------------------------------------------------------------------------------
/src/Layout/Layout.tsx:
--------------------------------------------------------------------------------
1 | import { Layout } from '@arco-design/web-react';
2 | import { IconCaretRight, IconCaretLeft } from '@arco-design/web-react/icon';
3 | import React, { useState } from 'react';
4 | import Header from './Header/Header';
5 | import './index.less';
6 | import MainRoute from './MainRoute';
7 | import { MenuComponent } from './Menu';
8 |
9 | const Sider = Layout.Sider;
10 | const Footer = Layout.Footer;
11 | const Content = Layout.Content;
12 |
13 | const LayoutMain: React.FC = () => {
14 | const [collapsed, setCollapsed] = useState(false);
15 |
16 | const handleCollapsed = () => {
17 | setCollapsed(!collapsed);
18 | };
19 | return (
20 |
21 |
22 |
23 |
24 | : }
30 | breakpoint="xl"
31 | >
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | );
44 | };
45 | export default LayoutMain;
46 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense, useMemo, useState } from 'react';
2 | import { BrowserRouter, Routes, Route } from 'react-router-dom';
3 | import { ConfigProvider } from '@arco-design/web-react';
4 | import enUS from '@arco-design/web-react/es/locale/en-US';
5 | import zhCN from '@arco-design/web-react/es/locale/zh-CN';
6 | import '@arco-design/web-react/dist/css/arco.css';
7 | import { useColor, useTheme } from 'src/ahooks';
8 | import { useLocalStorageState } from 'ahooks';
9 | import { Login } from './app/Login';
10 | import { Home } from './app/Home';
11 | import Loading from './components/Loading/Loading';
12 | import { GlobalContext, ILang } from './utils/GlobalContext';
13 | import './index.less';
14 |
15 | const App = () => {
16 | useTheme();
17 | useColor();
18 | const [language] = useLocalStorageState('language');
19 | const [lang, setLang] = useState(language ?? 'zh-CN');
20 | const contextValue = {
21 | lang,
22 | setLang,
23 | };
24 |
25 | const locale = useMemo(() => {
26 | switch (lang) {
27 | case 'zh-CN':
28 | return zhCN;
29 | case 'en-US':
30 | return enUS;
31 | default:
32 | return zhCN;
33 | }
34 | }, [lang]);
35 |
36 | return (
37 |
38 |
39 |
40 | }>
41 |
42 | } />
43 | } />
44 |
45 |
46 |
47 |
48 |
49 | );
50 | };
51 | export default App;
52 |
--------------------------------------------------------------------------------
/src/assets/css/nprogress.less:
--------------------------------------------------------------------------------
1 | /* Make clicks pass-through */
2 | @main-color: var(--theme-color);
3 |
4 | #nprogress {
5 | pointer-events: none;
6 | }
7 |
8 | #nprogress .bar {
9 | background: @main-color;
10 |
11 | position: fixed;
12 | z-index: 1031;
13 | top: 0;
14 | left: 0;
15 |
16 | width: 100%;
17 | height: 2px;
18 | }
19 |
20 | /* Fancy blur effect */
21 | #nprogress .peg {
22 | display: block;
23 | position: absolute;
24 | right: 0px;
25 | width: 100px;
26 | height: 100%;
27 | box-shadow: 0 0 10px @main-color, 0 0 5px @main-color;
28 | opacity: 1;
29 |
30 | -webkit-transform: rotate(3deg) translate(0px, -4px);
31 | -ms-transform: rotate(3deg) translate(0px, -4px);
32 | transform: rotate(3deg) translate(0px, -4px);
33 | }
34 |
35 | /* Remove these to get rid of the spinner */
36 | #nprogress .spinner {
37 | display: block;
38 | position: fixed;
39 | z-index: 1031;
40 | top: 15px;
41 | right: 15px;
42 | }
43 |
44 | #nprogress .spinner-icon {
45 | width: 18px;
46 | height: 18px;
47 | box-sizing: border-box;
48 |
49 | border: solid 2px transparent;
50 | border-top-color: @main-color;
51 | border-left-color: @main-color;
52 | border-radius: 50%;
53 |
54 | -webkit-animation: nprogress-spinner 400ms linear infinite;
55 | animation: nprogress-spinner 400ms linear infinite;
56 | }
57 |
58 | .nprogress-custom-parent {
59 | overflow: hidden;
60 | position: relative;
61 | }
62 |
63 | .nprogress-custom-parent #nprogress .spinner,
64 | .nprogress-custom-parent #nprogress .bar {
65 | position: absolute;
66 | }
67 |
68 | @-webkit-keyframes nprogress-spinner {
69 | 0% {
70 | -webkit-transform: rotate(0deg);
71 | }
72 | 100% {
73 | -webkit-transform: rotate(360deg);
74 | }
75 | }
76 | @keyframes nprogress-spinner {
77 | 0% {
78 | transform: rotate(0deg);
79 | }
80 | 100% {
81 | transform: rotate(360deg);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/conifg/routerConfig.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Result403 from 'src/app/Exception/403';
3 | import Result404 from 'src/app/Exception/404';
4 | import Result500 from 'src/app/Exception/500';
5 |
6 | const Welcome = React.lazy(() => import('../app/Welcome'));
7 | const Workplace = React.lazy(() => import('../app/Dashboard/WorkPlace/WorkPlace'));
8 | const Monitor = React.lazy(() => import('../app/Dashboard/Monitor/Monitor'));
9 | const DataAnalysis = React.lazy(() => import('../app/Visualization/DataAnalysis'));
10 | const MultiDimensionDataAnalysis = React.lazy(() => import('../app/Visualization/MultiDimensionDataAnalysis'));
11 | const Setting = React.lazy(() => import('../app/User/setting'));
12 |
13 | const Success = React.lazy(() => import('../app/Result/Success'));
14 | const Error = React.lazy(() => import('../app/Result/Error'));
15 |
16 | export interface IRouterConfig {
17 | path: string;
18 | text: string;
19 | page: React.ReactElement;
20 | }
21 |
22 | const RouterConfig: IRouterConfig[] = [
23 | {
24 | path: '/welcome',
25 | text: '欢迎页',
26 | page: ,
27 | },
28 | {
29 | path: '/dashboard/workplace',
30 | text: '工作台',
31 | page: ,
32 | },
33 | {
34 | path: '/dashboard/monitor',
35 | text: '实时监控',
36 | page: ,
37 | },
38 | {
39 | path: '/visualization/data-analysis',
40 | text: '分析页',
41 | page: ,
42 | },
43 | {
44 | path: '/visualization/multi-dimension-data-analysis',
45 | text: '多位数据分析',
46 | page: ,
47 | },
48 | {
49 | path: '/exception/403',
50 | text: '403',
51 | page: ,
52 | },
53 | {
54 | path: '/exception/404',
55 | text: '404',
56 | page: ,
57 | },
58 | {
59 | path: '/exception/500',
60 | text: '500',
61 | page: ,
62 | },
63 | {
64 | path: '/result/success',
65 | text: 'success',
66 | page: ,
67 | },
68 | {
69 | path: '/result/error',
70 | text: 'error',
71 | page: ,
72 | },
73 | {
74 | path: '/user/setting',
75 | text: '用户设置',
76 | page: ,
77 | },
78 | ];
79 |
80 | export default RouterConfig;
81 |
--------------------------------------------------------------------------------
/src/app/User/setting/index.tsx:
--------------------------------------------------------------------------------
1 | import { Avatar, Button, Card, Descriptions } from '@arco-design/web-react'
2 | import React, { useContext, useReducer } from 'react';
3 | import styles from './index.module.less'
4 |
5 | const initialState = { data: [{
6 | label: 'Name',
7 | value: 'Socrates',
8 | }, {
9 | label: 'Mobile',
10 | value: '123-1234-1234',
11 | }, {
12 | label: 'Residence',
13 | value: 'Beijing'
14 | }, {
15 | label: 'Hometown',
16 | value: 'Beijing',
17 | }, {
18 | label: 'Address',
19 | value: 'Yingdu Building, Zhichun Road, Beijing'
20 | }]
21 | };
22 |
23 | type Action = { type: "update" }
24 | function reducer(state: typeof initialState, action: Action) {
25 | switch (action.type) {
26 | case 'update':
27 | return {
28 | ...state,
29 | data: state.data.splice(0, 1)
30 | }
31 | }
32 | }
33 |
34 | interface ContextState {
35 | data?: typeof initialState,
36 | dispatch?: React.Dispatch
37 | }
38 | const MyContenxt = React.createContext({})
39 |
40 | const Setting = () => {
41 | const [state, dispatch] = useReducer(reducer, initialState)
42 | return (
43 |
44 |
useReducer 和 useContext 的最佳实践
45 |
49 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | )
64 | }
65 | export default Setting
66 |
67 | const UserInfo = () => {
68 | const {data, dispatch} = useContext(MyContenxt)
69 | const change = () => {
70 | dispatch && dispatch({
71 | type: 'update'
72 | })
73 | }
74 | return (
75 |
76 |
83 |
84 |
85 |
86 | )
87 | }
88 |
--------------------------------------------------------------------------------
/.github/workflows/privew-deloy.yml:
--------------------------------------------------------------------------------
1 | name: Preview Deploy
2 |
3 | on:
4 | workflow_run:
5 | workflows: ['Preview Build']
6 | types:
7 | - completed
8 |
9 | jobs:
10 | success:
11 | runs-on: ubuntu-latest
12 | if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
13 | steps:
14 | - name: download pr artifact
15 | uses: dawidd6/action-download-artifact@v2
16 | with:
17 | workflow: ${{ github.event.workflow_run.workflow_id }}
18 | name: pr
19 | - name: save PR id
20 | id: pr
21 | run: echo "::set-output name=id::$(
44 |
45 | failed:
46 | runs-on: ubuntu-latest
47 | if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'failure'
48 | steps:
49 | - name: download pr artifact
50 | uses: dawidd6/action-download-artifact@v2
51 | with:
52 | workflow: ${{ github.event.workflow_run.workflow_id }}
53 | name: pr
54 | - name: save PR id
55 | id: pr
56 | run: echo "::set-output name=id::$(
21 | {icon}
22 |
23 |
24 | {title}
25 |
26 | {count}
27 | {unit}
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
35 | const Overview = () => {
36 | return (
37 |
38 | 欢迎回来,龙风
39 |
40 |
41 |
42 | } title={'线上总数据'} count={1} unit={'个'} loading={false} />
43 |
44 |
45 |
46 | } title={'投放中的内容'} count={1} unit={'个'} loading={false} />
47 |
48 |
49 |
50 | } title={'投放中的内容'} count={1} unit={'个'} loading={false} />
51 |
52 |
53 |
54 | } title={'投放中的内容'} count={1} unit={'个'} loading={false} />
55 |
56 |
57 |
58 |
59 |
60 | 内容数据
61 | 查看更多
62 |
63 |
64 |
65 |
66 | );
67 | };
68 |
69 | export default Overview;
70 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/Shortcuts.tsx:
--------------------------------------------------------------------------------
1 | import { Card, Divider, Link, Typography } from '@arco-design/web-react';
2 | import { IconFile, IconFire, IconMobile, IconSettings, IconStorage } from '@arco-design/web-react/icon';
3 | import React from 'react';
4 | import './style/shortcuts.less';
5 |
6 | const classPrefix = 'shortcuts';
7 | const ShortCuts = () => {
8 | const shortcutList = [
9 | {
10 | title: '内容管理',
11 | key: 'Content Management',
12 | icon: ,
13 | },
14 | {
15 | title: '内容数据',
16 | key: 'Content Statistic',
17 | icon: ,
18 | },
19 | {
20 | title: '高级管理',
21 | key: 'Advanced Management',
22 | icon: ,
23 | },
24 | {
25 | title: '线上推广',
26 | key: 'Online Promotion',
27 | icon: ,
28 | },
29 | {
30 | title: '内容投放',
31 | key: 'Marketing',
32 | icon: ,
33 | },
34 | ];
35 |
36 | const recentShortcuts = [
37 | {
38 | title: '内容数据',
39 | key: 'Content Statistic',
40 | icon: ,
41 | },
42 | {
43 | title: '内容管理',
44 | key: 'Content Management',
45 | icon: ,
46 | },
47 | {
48 | title: '高级管理',
49 | key: 'Advanced Management',
50 | icon: ,
51 | },
52 | ];
53 |
54 | return (
55 |
56 |
57 | 快捷入口
58 | 查看
59 |
60 |
61 |
62 | {shortcutList.map((item) => (
63 |
64 |
{item.icon}
65 |
{item.title}
66 |
67 | ))}
68 |
69 |
70 |
71 | 最近访问
72 |
73 |
74 | {recentShortcuts.map((item) => (
75 |
76 |
{item.icon}
77 |
{item.title}
78 |
79 | ))}
80 |
81 |
82 | );
83 | };
84 |
85 | export default ShortCuts;
86 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/shortcuts.tsx:
--------------------------------------------------------------------------------
1 | import { Card, Divider, Link, Typography } from '@arco-design/web-react';
2 | import { IconFile, IconFire, IconMobile, IconSettings, IconStorage } from '@arco-design/web-react/icon';
3 | import React from 'react';
4 | import './style/shortcuts.less';
5 |
6 | const classPrefix = 'shortcuts';
7 | const ShortCuts = () => {
8 | const shortcutList = [
9 | {
10 | title: '内容管理',
11 | key: 'Content Management',
12 | icon: ,
13 | },
14 | {
15 | title: '内容数据',
16 | key: 'Content Statistic',
17 | icon: ,
18 | },
19 | {
20 | title: '高级管理',
21 | key: 'Advanced Management',
22 | icon: ,
23 | },
24 | {
25 | title: '线上推广',
26 | key: 'Online Promotion',
27 | icon: ,
28 | },
29 | {
30 | title: '内容投放',
31 | key: 'Marketing',
32 | icon: ,
33 | },
34 | ];
35 |
36 | const recentShortcuts = [
37 | {
38 | title: '内容数据',
39 | key: 'Content Statistic',
40 | icon: ,
41 | },
42 | {
43 | title: '内容管理',
44 | key: 'Content Management',
45 | icon: ,
46 | },
47 | {
48 | title: '高级管理',
49 | key: 'Advanced Management',
50 | icon: ,
51 | },
52 | ];
53 |
54 | return (
55 |
56 |
57 | 快捷入口
58 | 查看
59 |
60 |
61 |
62 | {shortcutList.map((item) => (
63 |
64 |
{item.icon}
65 |
{item.title}
66 |
67 | ))}
68 |
69 |
70 |
71 | 最近访问
72 |
73 |
74 | {recentShortcuts.map((item) => (
75 |
76 |
{item.icon}
77 |
{item.title}
78 |
79 | ))}
80 |
81 |
82 | );
83 | };
84 |
85 | export default ShortCuts;
86 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/ContentData.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Chart, LineAdvance } from 'bizcharts';
3 |
4 | const data = [
5 | {
6 | month: 'Jan',
7 | city: 'Tokyo',
8 | temperature: 7,
9 | },
10 | {
11 | month: 'Jan',
12 | city: 'London',
13 | temperature: 3.9,
14 | },
15 | {
16 | month: 'Feb',
17 | city: 'Tokyo',
18 | temperature: 13,
19 | },
20 | {
21 | month: 'Feb',
22 | city: 'London',
23 | temperature: 4.2,
24 | },
25 | {
26 | month: 'Mar',
27 | city: 'Tokyo',
28 | temperature: 16.5,
29 | },
30 | {
31 | month: 'Mar',
32 | city: 'London',
33 | temperature: 5.7,
34 | },
35 | {
36 | month: 'Apr',
37 | city: 'Tokyo',
38 | temperature: 14.5,
39 | },
40 | {
41 | month: 'Apr',
42 | city: 'London',
43 | temperature: 8.5,
44 | },
45 | {
46 | month: 'May',
47 | city: 'Tokyo',
48 | temperature: 10,
49 | },
50 | {
51 | month: 'May',
52 | city: 'London',
53 | temperature: 11.9,
54 | },
55 | {
56 | month: 'Jun',
57 | city: 'Tokyo',
58 | temperature: 7.5,
59 | },
60 | {
61 | month: 'Jun',
62 | city: 'London',
63 | temperature: 15.2,
64 | },
65 | {
66 | month: 'Jul',
67 | city: 'Tokyo',
68 | temperature: 9.2,
69 | },
70 | {
71 | month: 'Jul',
72 | city: 'London',
73 | temperature: 17,
74 | },
75 | {
76 | month: 'Aug',
77 | city: 'Tokyo',
78 | temperature: 14.5,
79 | },
80 | {
81 | month: 'Aug',
82 | city: 'London',
83 | temperature: 16.6,
84 | },
85 | {
86 | month: 'Sep',
87 | city: 'Tokyo',
88 | temperature: 9.3,
89 | },
90 | {
91 | month: 'Sep',
92 | city: 'London',
93 | temperature: 14.2,
94 | },
95 | {
96 | month: 'Oct',
97 | city: 'Tokyo',
98 | temperature: 8.3,
99 | },
100 | {
101 | month: 'Oct',
102 | city: 'London',
103 | temperature: 10.3,
104 | },
105 | {
106 | month: 'Nov',
107 | city: 'Tokyo',
108 | temperature: 8.9,
109 | },
110 | {
111 | month: 'Nov',
112 | city: 'London',
113 | temperature: 5.6,
114 | },
115 | {
116 | month: 'Dec',
117 | city: 'Tokyo',
118 | temperature: 5.6,
119 | },
120 | {
121 | month: 'Dec',
122 | city: 'London',
123 | temperature: 9.8,
124 | },
125 | ];
126 |
127 | const ContentData = () => {
128 | return (
129 |
130 |
131 |
132 |
133 |
134 | );
135 | };
136 |
137 | export default ContentData;
138 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/contentData.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Chart, LineAdvance } from 'bizcharts';
3 |
4 | const data = [
5 | {
6 | month: 'Jan',
7 | city: 'Tokyo',
8 | temperature: 7,
9 | },
10 | {
11 | month: 'Jan',
12 | city: 'London',
13 | temperature: 3.9,
14 | },
15 | {
16 | month: 'Feb',
17 | city: 'Tokyo',
18 | temperature: 13,
19 | },
20 | {
21 | month: 'Feb',
22 | city: 'London',
23 | temperature: 4.2,
24 | },
25 | {
26 | month: 'Mar',
27 | city: 'Tokyo',
28 | temperature: 16.5,
29 | },
30 | {
31 | month: 'Mar',
32 | city: 'London',
33 | temperature: 5.7,
34 | },
35 | {
36 | month: 'Apr',
37 | city: 'Tokyo',
38 | temperature: 14.5,
39 | },
40 | {
41 | month: 'Apr',
42 | city: 'London',
43 | temperature: 8.5,
44 | },
45 | {
46 | month: 'May',
47 | city: 'Tokyo',
48 | temperature: 10,
49 | },
50 | {
51 | month: 'May',
52 | city: 'London',
53 | temperature: 11.9,
54 | },
55 | {
56 | month: 'Jun',
57 | city: 'Tokyo',
58 | temperature: 7.5,
59 | },
60 | {
61 | month: 'Jun',
62 | city: 'London',
63 | temperature: 15.2,
64 | },
65 | {
66 | month: 'Jul',
67 | city: 'Tokyo',
68 | temperature: 9.2,
69 | },
70 | {
71 | month: 'Jul',
72 | city: 'London',
73 | temperature: 17,
74 | },
75 | {
76 | month: 'Aug',
77 | city: 'Tokyo',
78 | temperature: 14.5,
79 | },
80 | {
81 | month: 'Aug',
82 | city: 'London',
83 | temperature: 16.6,
84 | },
85 | {
86 | month: 'Sep',
87 | city: 'Tokyo',
88 | temperature: 9.3,
89 | },
90 | {
91 | month: 'Sep',
92 | city: 'London',
93 | temperature: 14.2,
94 | },
95 | {
96 | month: 'Oct',
97 | city: 'Tokyo',
98 | temperature: 8.3,
99 | },
100 | {
101 | month: 'Oct',
102 | city: 'London',
103 | temperature: 10.3,
104 | },
105 | {
106 | month: 'Nov',
107 | city: 'Tokyo',
108 | temperature: 8.9,
109 | },
110 | {
111 | month: 'Nov',
112 | city: 'London',
113 | temperature: 5.6,
114 | },
115 | {
116 | month: 'Dec',
117 | city: 'Tokyo',
118 | temperature: 5.6,
119 | },
120 | {
121 | month: 'Dec',
122 | city: 'London',
123 | temperature: 9.8,
124 | },
125 | ];
126 |
127 | const ContentData = () => {
128 | return (
129 |
130 |
131 |
132 |
133 |
134 | );
135 | };
136 |
137 | export default ContentData;
138 |
--------------------------------------------------------------------------------
/src/Layout/Menu.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { Menu } from '@arco-design/web-react';
3 | import {
4 | IconHome,
5 | IconCalendar,
6 | IconDashboard,
7 | IconDice,
8 | IconApps,
9 | IconList,
10 | IconFile,
11 | IconCheckCircle,
12 | IconUser,
13 | IconExclamationCircle,
14 | } from '@arco-design/web-react/icon';
15 | import { IMenusItem, menuConfig } from '../conifg/menuConfig';
16 | import { useLocation, Link } from 'react-router-dom';
17 | import useI18n from 'src/ahooks/useI18n';
18 | const MenuItem = Menu.Item;
19 | const SubMenu = Menu.SubMenu;
20 |
21 | interface IconsPros {
22 | [key: string]: React.ReactElement;
23 | }
24 | const icons: IconsPros = {
25 | IconHome: ,
26 | IconCalendar: ,
27 | IconDashboard: ,
28 | IconDice: ,
29 | IconApps: ,
30 | IconList: ,
31 | IconFile: ,
32 | IconCheckCircle: ,
33 | IconUser: ,
34 | IconExclamationCircle: ,
35 | };
36 | const menu: IMenusItem[] = menuConfig.menu;
37 |
38 | const getMenu = (menus: IMenusItem[]) => {
39 | const { lang, i18n } = useI18n();
40 | const list = menus.map((item) => {
41 | if (item.children && item.children.length > 0) {
42 | return (
43 |
47 | {item.icon && icons[item.icon]}
48 | {i18n[lang][item.name]}
49 |
50 | }
51 | key={item.key}
52 | >
53 | {getMenu(item.children)}
54 |
55 | );
56 | }
57 | return ;
62 | });
63 | return list;
64 | };
65 |
66 | export const MenuComponent = () => {
67 | const [selectedKey, setSelectedKey] = useState([]);
68 | const [openKeys, setOpenKeys] = useState([]);
69 | const location = useLocation();
70 | useEffect(() => {
71 | console.log(location.pathname)
72 | initMenus();
73 | }, [location.pathname]);
74 |
75 | function initMenus() {
76 | const key = location.pathname.split('/') ? '/' + location.pathname.split('/')[1] : ''
77 | setOpenKeys([key]);
78 | setSelectedKey([location.pathname]);
79 | }
80 | const onClickMenuItem = (key: string) => {
81 | setSelectedKey([key]);
82 | };
83 |
84 | return (
85 |
96 | );
97 | };
98 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/modules/slide/contentView.tsx:
--------------------------------------------------------------------------------
1 | import { Card, Grid, Link, Radio, Table } from '@arco-design/web-react'
2 | import { useRequest } from 'ahooks'
3 | import axios from 'axios'
4 | import React, { useState } from 'react'
5 | import ConentChart from '../chart/conentChart'
6 | import { haderList } from '../config'
7 | import '../../mock/index'
8 | import '../../mock'
9 | import styles from './style/content.module.less'
10 |
11 | const { Row, Col } = Grid
12 |
13 | const Index = () => {
14 | const [type, setType] = useState(1)
15 | const [page, setPage] = useState(1)
16 | const [total, setTotal] = useState(0)
17 | const [data, setData] = useState>([])
18 |
19 | const getList = ({ page = 0 }) => {
20 | return axios.get(`/api/workplace/popular-contents?page=${page}&pageSize=5&category=${type}`)
21 | }
22 |
23 | const { loading } = useRequest((params) => getList(params), {
24 | refreshDeps: [page, type],
25 | defaultParams: [
26 | {
27 | page: page,
28 | },
29 | ],
30 | onSuccess: (res) => {
31 | const {
32 | data: { list, total },
33 | } = res
34 | setTotal(total)
35 | if (Array.isArray(list)) {
36 | setData([...list])
37 | }
38 | },
39 | })
40 |
41 | const columns = [
42 | {
43 | title: '排名',
44 | dataIndex: 'rank',
45 | width: 65,
46 | },
47 | {
48 | title: '点击量',
49 | dataIndex: 'pv',
50 | width: 100,
51 | render: (text: number) => {
52 | return `${text / 1000}k`
53 | },
54 | },
55 | {
56 | title: '日涨幅',
57 | dataIndex: 'increase',
58 | sorter: (a: { increase: number }, b: { increase: number }) => a.increase - b.increase,
59 | width: 110,
60 | },
61 | ]
62 |
63 | return (
64 |
65 |
66 | 查看更多}>
67 |
68 | {haderList.map((item) => (
69 |
70 | {item.title}
71 |
72 | ))}
73 |
74 | {
82 | setPage(pagination.current)
83 | }}
84 | pagination={{ total, current: page, pageSize: 5, simple: true }}
85 | />
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | )
95 | }
96 |
97 | export default Index
98 |
--------------------------------------------------------------------------------
/src/app/Dashboard/WorkPlace/mock/index.ts:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs';
2 | // import qs from 'query-string';
3 | import setupMock from 'src/utils/setupMock';
4 |
5 | setupMock({
6 | setup: () => {
7 | Mock.mock(new RegExp('/api/workplace/overview-content'), () => {
8 | const year = new Date().getFullYear();
9 | const getLineData = () => {
10 | return new Array(12).fill(0)
11 | .map((_item, index) => ({
12 | date: `${year}-${index + 1}`,
13 | count: Mock.Random.natural(20000, 75000),
14 | }));
15 | };
16 | return {
17 | allContents: '373.5w+',
18 | liveContents: '368',
19 | increaseComments: '8874',
20 | growthRate: '2.8%',
21 | chartData: getLineData(),
22 | };
23 | });
24 |
25 | const getList = () => {
26 | const { list } = Mock.mock({
27 | 'list|100': [
28 | {
29 | 'rank|+1': 1,
30 | title: () =>
31 | Mock.Random.pick([
32 | '经济日报:财政政策要精准提升效能',
33 | '“双12”遇冷消费者厌倦了电商平台的促销“套路”',
34 | '致敬坚守战“疫”一线的社区工作者',
35 | '普高还是职高?家长们陷入选校难题',
36 | ]),
37 | pv: function () {
38 | return 500000 - 3200;
39 | },
40 | increase: '@float(-1, 1)',
41 | },
42 | ],
43 | });
44 | return list;
45 | };
46 | const listText = getList();
47 | const listPic = getList();
48 | const listVideo = getList();
49 |
50 | Mock.mock(new RegExp('/api/workplace/popular-contents'), () => {
51 | const list = [listText, listPic, listVideo][Number()];
52 | return {
53 | list: list.slice(1, 6),
54 | total: 100,
55 | };
56 | });
57 |
58 | Mock.mock(new RegExp('/api/workplace/content-percentage'), () => {
59 | return [
60 | {
61 | type: '纯文本',
62 | count: 148564,
63 | percent: 0.16,
64 | },
65 | {
66 | type: '图文类',
67 | count: 334271,
68 | percent: 0.36,
69 | },
70 | {
71 | type: '视频类',
72 | count: 445695,
73 | percent: 0.48,
74 | },
75 | ];
76 | });
77 |
78 | Mock.mock(new RegExp('/api/workplace/announcement'), () => {
79 | return [
80 | {
81 | type: 'activity',
82 | key: '1',
83 | content: '内容最新优惠活动',
84 | },
85 | {
86 | type: 'info',
87 | key: '2',
88 | content: '新增内容尚未通过审核,详情请点击查看。',
89 | },
90 | {
91 | type: 'notice',
92 | key: '3',
93 | content: '当前产品试用期即将结束,如需续费请点击查看。',
94 | },
95 | {
96 | type: 'notice',
97 | key: '4',
98 | content: '1 月新系统升级计划通知',
99 | },
100 | {
101 | type: 'info',
102 | key: '5',
103 | content: '新增内容已经通过审核,详情请点击查看。',
104 | },
105 | ];
106 | });
107 | },
108 | });
109 |
--------------------------------------------------------------------------------
/src/components/PageConifg/PageConfig.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { Drawer, Trigger, Select } from '@arco-design/web-react';
3 | import { SketchPicker } from 'react-color';
4 | import { changePageColor } from 'src/utils';
5 | import classnames from 'classnames';
6 | import { useColor } from 'src/ahooks';
7 | import { IconLanguage } from '@arco-design/web-react/icon';
8 | import useI18n from 'src/ahooks/useI18n';
9 | import { ILang } from 'src/utils/GlobalContext';
10 |
11 | type IProps = {
12 | children: React.ReactNode;
13 | };
14 |
15 | const { Option } = Select;
16 |
17 | const PageConfig: React.FC = (props) => {
18 | const { children } = props;
19 | const [color, setColor] = useState('');
20 | const [visible, setVisible] = useState(false);
21 | const [language, setLanguage] = useState('zh-CN');
22 | const [themeColor, setThemeColor] = useColor();
23 | const { lang, setLang, i18n } = useI18n();
24 | useEffect(() => {
25 | setColor(themeColor);
26 | setLanguage(lang);
27 | }, [visible]);
28 |
29 | const onOk = () => {
30 | setVisible(false);
31 | setThemeColor(color);
32 | changePageColor(color);
33 | setLang(language);
34 | };
35 |
36 | return (
37 |
38 |
{i18n[lang]['system.tip.config']}}
43 | visible={visible}
44 | onOk={onOk}
45 | onCancel={() => {
46 | setVisible(false);
47 | }}
48 | >
49 | (
51 | {
54 | setColor(value.hex);
55 | }}
56 | />
57 | )}
58 | mouseEnterDelay={400}
59 | mouseLeaveDelay={400}
60 | position="bottom"
61 | >
62 |
63 |
{i18n[lang]['system.tip.themColor']}
64 |
67 |
{color}
68 |
69 |
70 |
71 |
72 |
73 |
74 | Languages
75 |
76 |
85 |
86 |
87 |
setVisible(true)}>{children}
88 |
89 | );
90 | };
91 |
92 | export default PageConfig;
93 |
--------------------------------------------------------------------------------
/src/conifg/menuConfig.tsx:
--------------------------------------------------------------------------------
1 | export interface IMenusItem {
2 | name: string;
3 | key: string;
4 | path: string;
5 | icon?: string;
6 | children?: {
7 | name: string;
8 | key: string;
9 | path: string;
10 | }[];
11 | }
12 | interface IMenu {
13 | menu: IMenusItem[];
14 | }
15 |
16 | export const menuConfig: IMenu = {
17 | menu: [
18 | {
19 | name: 'menu.dashboard',
20 | key: '/dashboard',
21 | path: '/dashboard',
22 | icon: 'IconDashboard',
23 | children: [
24 | {
25 | name: 'menu.dashboard.workplace',
26 | key: '/dashboard/workplace',
27 | path: '/dashboard/workplace',
28 | },
29 | {
30 | name: 'menu.dashboard.monitor',
31 | key: '/dashboard/monitor',
32 | path: '/dashboard/monitor',
33 | },
34 | ],
35 | },
36 | {
37 | name: 'menu.visualization',
38 | key: '/visualization',
39 | path: '/visualization',
40 | icon: 'IconHome',
41 | children: [
42 | {
43 | name: 'menu.visualization.dataAnalysis',
44 | key: '/visualization/data-analysis',
45 | path: '/visualization/data-analysis',
46 | },
47 | {
48 | name: 'menu.visualization.multiDimensionDataAnalysis',
49 | key: '/visualization/multi-dimension-data-analysis',
50 | path: '/visualization/multi-dimension-data-analysis',
51 | },
52 | ],
53 | },
54 | {
55 | name: 'menu.list',
56 | key: '/list',
57 | path: '/list',
58 | icon: 'IconList',
59 | children: [
60 | {
61 | name: 'menu.list.searchTable',
62 | key: '/list/search-table',
63 | path: '/list/search-table',
64 | },
65 | {
66 | name: 'menu.list.cardList',
67 | key: '/list/card',
68 | path: '/list/card',
69 | },
70 | ],
71 | },
72 | {
73 | name: 'menu.profile',
74 | key: '/profile',
75 | path: '/profile',
76 | icon: 'IconFile',
77 | children: [
78 | {
79 | name: 'menu.profile.basic',
80 | key: '/profile/basic',
81 | path: '/profile/basic',
82 | },
83 | ],
84 | },
85 | {
86 | name: 'menu.result',
87 | key: '/result',
88 | path: '/result',
89 | icon: 'IconCheckCircle',
90 | children: [
91 | {
92 | name: 'menu.result.success',
93 | key: '/result/success',
94 | path: '/result/success',
95 | },
96 | {
97 | name: 'menu.result.error',
98 | key: '/result/error',
99 | path: '/result/error',
100 | },
101 | ],
102 | },
103 | {
104 | name: 'menu.exception',
105 | key: '/exception',
106 | path: '/exception',
107 | icon: 'IconExclamationCircle',
108 | children: [
109 | {
110 | name: 'menu.exception.403',
111 | key: '/exception/403',
112 | path: '/exception/403',
113 | },
114 | {
115 | name: 'menu.exception.404',
116 | key: '/exception/404',
117 | path: '/exception/404',
118 | },
119 | {
120 | name: 'menu.exception.500',
121 | key: '/exception/500',
122 | path: '/exception/500',
123 | },
124 | ],
125 | },
126 | {
127 | name: 'menu.user',
128 | key: '/user',
129 | path: '/user',
130 | icon: 'IconUser',
131 | children: [
132 | {
133 | name: 'menu.user.info',
134 | key: '/user/info',
135 | path: '/user/info',
136 | },
137 | {
138 | name: 'menu.user.setting',
139 | key: '/user/setting',
140 | path: '/user/setting',
141 | },
142 | ],
143 | },
144 | ],
145 | };
146 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-arco-admin",
3 | "version": "1.0.0",
4 | "description": "react、arco、admin",
5 | "main": "index.js",
6 | "author": "yaogengzhu",
7 | "license": "ISC",
8 | "scripts": {
9 | "prepare": "husky install",
10 | "start": "env-cmd -f .env.development node ./server/index",
11 | "build": "env-cmd -f .env.production webpack --config ./build/webpack.prod.js",
12 | "deploy": "env-cmd -f .env.production webpack --config ./build/webpack.prod.js & . deploy.sh",
13 | "deploy:cos": "node ./build/upload.js",
14 | "lint": "eslint --ext .ts --ext .tsx src --fix"
15 | },
16 | "lint-staged": {
17 | "*.{js,jsx,tsx, ts}": [
18 | "eslint --fix",
19 | "git add"
20 | ]
21 | },
22 | "devDependencies": {
23 | "@arco-design/webpack-plugin": "^1.7.0",
24 | "@babel/core": "^7.14.6",
25 | "@babel/parser": "^7.18.0",
26 | "@babel/plugin-proposal-class-properties": "^7.14.5",
27 | "@babel/plugin-proposal-decorators": "^7.15.8",
28 | "@babel/plugin-proposal-private-methods": "^7.14.5",
29 | "@babel/plugin-transform-runtime": "^7.14.5",
30 | "@babel/preset-env": "^7.14.7",
31 | "@babel/preset-react": "^7.14.5",
32 | "@commitlint/cli": "^16.2.1",
33 | "@commitlint/config-conventional": "^16.2.1",
34 | "@types/react": "^17.0.27",
35 | "@types/react-color": "^3.0.6",
36 | "@types/react-dom": "^17.0.17",
37 | "@typescript-eslint/eslint-plugin": "^5.7.0",
38 | "autoprefixer": "^10.3.0",
39 | "babel-eslint": "^10.1.0",
40 | "babel-loader": "^8.2.2",
41 | "copy-webpack-plugin": "^9.0.1",
42 | "css-minimizer-webpack-plugin": "^3.0.2",
43 | "eslint": "^7.32.0",
44 | "eslint-config-prettier": "^8.3.0",
45 | "eslint-plugin-import": "^2.23.4",
46 | "eslint-plugin-prettier": "^3.4.0",
47 | "eslint-plugin-react": "^7.24.0",
48 | "husky": "^8.0.1",
49 | "lint-staged": "^11.1.2",
50 | "npm": "^8.0.0",
51 | "optimize-css-assets-webpack-plugin": "^6.0.1",
52 | "postcss": "^8.3.5",
53 | "postcss-loader": "^6.1.1",
54 | "pre-commit": "^1.2.2",
55 | "prettier": "^2.3.2",
56 | "thread-loader": "^3.0.4",
57 | "ts-loader": "^9.2.6",
58 | "typescript": "^4.5.5",
59 | "webpack-cli": "^4.7.2"
60 | },
61 | "dependencies": {
62 | "@arco-design/color": "^0.4.0",
63 | "@arco-design/web-react": "^2.33.0",
64 | "@babel/preset-typescript": "^7.15.0",
65 | "@react-spring/web": "^9.4.5",
66 | "@types/mockjs": "^1.0.6",
67 | "@types/uuid": "^8.3.4",
68 | "@use-gesture/react": "^10.2.12",
69 | "ahooks": "^3.1.9",
70 | "async": "^3.2.4",
71 | "axios": "^0.26.0",
72 | "babel-plugin-import": "^1.13.3",
73 | "bizcharts": "^4.1.16",
74 | "browserslist": "^4.16.6",
75 | "cache-loader": "^4.1.0",
76 | "chalk": "^4.1.2",
77 | "classnames": "^2.3.1",
78 | "cos-nodejs-sdk-v5": "^2.11.15",
79 | "css-loader": "^5.2.6",
80 | "dotenv": "^16.0.3",
81 | "env-cmd": "^10.1.0",
82 | "eslint-loader": "^4.0.2",
83 | "file-loader": "^6.2.0",
84 | "friendly-errors-webpack-plugin": "^1.7.0",
85 | "history": "^5.2.0",
86 | "html-webpack-plugin": "^5.3.2",
87 | "ip": "^1.1.5",
88 | "less": "^4.1.1",
89 | "less-loader": "^10.0.1",
90 | "mini-css-extract-plugin": "^2.1.0",
91 | "mockjs": "^1.1.0",
92 | "nprogress": "^0.2.0",
93 | "prop-types": "^15.7.2",
94 | "query-string": "^7.1.1",
95 | "react": "^18.1.0",
96 | "react-color": "^2.19.3",
97 | "react-dom": "^18.1.0",
98 | "react-error-boundary": "^3.1.4",
99 | "react-router-dom": "^6.2.1",
100 | "react-spring": "^9.4.5",
101 | "style-loader": "^3.0.0",
102 | "terser-webpack-plugin": "^5.3.3",
103 | "url-loader": "^4.1.1",
104 | "uuid": "^8.3.2",
105 | "webpack": "^5.80.0",
106 | "webpack-bundle-analyzer": "^4.6.1",
107 | "webpack-dev-server": "^3.11.2",
108 | "webpack-merge": "^5.8.0"
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/app/Login/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import axios from 'axios';
3 | import { Form, Input, Button, Checkbox, Link, Message } from '@arco-design/web-react';
4 | import { IconLock, IconUser } from '@arco-design/web-react/icon';
5 | import { useNavigate } from 'react-router-dom';
6 | import { useLocalStorageState } from 'ahooks';
7 | import Banner from './modules/Banner';
8 | import useI18n from 'src/ahooks/useI18n';
9 | import locales from './locales';
10 | import styles from './index.module.less';
11 | import './mock/user';
12 | import CommonSetting from 'src/components/CommonSetting'
13 |
14 | type IUserParams = {
15 | username: string;
16 | password: string;
17 | };
18 | const FormItem = Form.Item;
19 |
20 | export const Login: React.FC = () => {
21 | const [form] = Form.useForm();
22 | const { lang, i18n } = useI18n(locales);
23 | const navigate = useNavigate();
24 | const [userToken, setUserToken] = useLocalStorageState('userToken');
25 | const [loading, setLoading] = useState(false);
26 |
27 | useEffect(() => {
28 | // 判断是否登陆
29 | if (userToken) {
30 | navigate('/weclome');
31 | }
32 | }, []);
33 |
34 | const onSubmit = () => {
35 | form.validate((err, values) => {
36 | if (err) {
37 | return;
38 | }
39 | const { username, password } = values;
40 | login({ username, password });
41 | });
42 | };
43 |
44 | const login = (params: IUserParams) => {
45 | setLoading(true);
46 | axios
47 | .post('/api/user/login', params)
48 | .then((res) => {
49 | const {
50 | status,
51 | msg,
52 | data: { token },
53 | } = res.data;
54 | console.log(msg);
55 | if (status === 'ok') {
56 | Message.success('登录成功');
57 | navigate('/weclome');
58 | setUserToken(token);
59 | } else {
60 | Message.error(msg);
61 | }
62 | })
63 | .finally(() => {
64 | setLoading(false);
65 | });
66 | };
67 |
68 | return (
69 |
70 |
71 |
React Arco Admin
72 |
73 |
74 |
75 |
76 |
77 |
78 |
登录React Arco Admin
79 |
120 |
121 |
鄂ICP备18026800号-1
122 |
123 |
124 |
125 | );
126 | };
127 |
--------------------------------------------------------------------------------
/src/Layout/Header/Header.tsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from 'react-router-dom';
2 | import React, { useEffect, useState } from 'react';
3 | import { Button, Dropdown, Menu, Tooltip, Message, Avatar } from '@arco-design/web-react';
4 | import {
5 | IconFullscreen,
6 | IconFullscreenExit,
7 | IconMoonFill,
8 | IconPoweroff,
9 | IconSettings,
10 | IconSunFill,
11 | IconLanguage,
12 | } from '@arco-design/web-react/icon';
13 | import { useFullscreen, useLocalStorageState } from 'ahooks';
14 | import { useTheme } from 'src/ahooks';
15 | import PageConfig from 'src/components/PageConifg/PageConfig';
16 | import useI18n from 'src/ahooks/useI18n';
17 | import styles from './index.module.less';
18 | import githubSvg from 'src/assets/images/github.svg'
19 |
20 | const themeStyle = {
21 | background: 'var(--theme-color)',
22 | color: '#fff',
23 | };
24 | const Header = () => {
25 | useTheme();
26 | const navigate = useNavigate();
27 | const { lang, i18n, setLang } = useI18n();
28 | const [, setLanguage] = useLocalStorageState('language');
29 | const [arcoThem, setArcoThem] = useLocalStorageState('arco-theme');
30 | const [them, setThem] = useState('');
31 | const [fullscreen, { toggleFullscreen }] = useFullscreen(() => document.documentElement);
32 | const loginOut = () => {
33 | localStorage.removeItem('userToken');
34 | navigate('/login');
35 | };
36 |
37 | useEffect(() => {
38 | if (arcoThem) {
39 | setThem('dark');
40 | } else {
41 | setThem('');
42 | }
43 | }, []);
44 |
45 | const goHome = () => {
46 | navigate('/weclome');
47 | };
48 |
49 | const fullscreenEvent = () => {
50 | toggleFullscreen();
51 | };
52 |
53 | const changeLanguage = (lang: string) => {
54 | if (lang === 'zh-CN') {
55 | Message.info('语言切换至 zh-CN');
56 | setLang('zh-CN');
57 | setLanguage('zh-CN');
58 | } else {
59 | Message.info('Language switch to en-US');
60 | setLang('en-US');
61 | setLanguage('en-US');
62 | }
63 | };
64 |
65 | const languageList = (
66 |
74 | );
75 |
76 | return (
77 |
78 |
79 |
React Arco Admin
80 |
81 |
82 | -
83 |
84 |
87 |
88 |
89 | -
90 | {them === 'dark' ? (
91 |
92 |
103 |
104 | ) : (
105 |
106 |
117 |
118 | )}
119 |
120 | -
121 |
122 |
125 |
126 |
127 | -
128 | {!fullscreen ? (
129 |
130 |
133 |
134 | ) : (
135 |
136 |
139 |
140 | )}
141 |
142 | -
143 |
150 |
151 | -
152 |
156 |
157 |
158 | {i18n[lang]['header.userSetting']}
159 |
160 |
161 |
162 | {i18n[lang]['header.logout']}
163 |
164 |
165 | }
166 | >
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | );
175 | };
176 |
177 | export default Header;
178 |
--------------------------------------------------------------------------------
/src/assets/css/reset.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /* Document
4 | ========================================================================== */
5 |
6 | /**
7 | * 1. Correct the line height in all browsers.
8 | * 2. Prevent adjustments of font size after orientation changes in iOS.
9 | */
10 |
11 | html {
12 | line-height: 1.15; /* 1 */
13 | -webkit-text-size-adjust: 100%; /* 2 */
14 | }
15 |
16 | /* Sections
17 | ========================================================================== */
18 |
19 | /**
20 | * Remove the margin in all browsers.
21 | */
22 |
23 | body {
24 | margin: 0;
25 | }
26 |
27 | /**
28 | * Render the `main` element consistently in IE.
29 | */
30 |
31 | main {
32 | display: block;
33 | }
34 |
35 | /**
36 | * Correct the font size and margin on `h1` elements within `section` and
37 | * `article` contexts in Chrome, Firefox, and Safari.
38 | */
39 |
40 | h1 {
41 | font-size: 2em;
42 | margin: 0.67em 0;
43 | }
44 |
45 | /* Grouping content
46 | ========================================================================== */
47 |
48 | /**
49 | * 1. Add the correct box sizing in Firefox.
50 | * 2. Show the overflow in Edge and IE.
51 | */
52 |
53 | hr {
54 | box-sizing: content-box; /* 1 */
55 | height: 0; /* 1 */
56 | overflow: visible; /* 2 */
57 | }
58 |
59 | /**
60 | * 1. Correct the inheritance and scaling of font size in all browsers.
61 | * 2. Correct the odd `em` font sizing in all browsers.
62 | */
63 |
64 | pre {
65 | font-family: monospace, monospace; /* 1 */
66 | font-size: 1em; /* 2 */
67 | }
68 |
69 | /* Text-level semantics
70 | ========================================================================== */
71 |
72 | /**
73 | * Remove the gray background on active links in IE 10.
74 | */
75 |
76 | a {
77 | background-color: transparent;
78 | }
79 |
80 | /**
81 | * 1. Remove the bottom border in Chrome 57-
82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
83 | */
84 |
85 | abbr[title] {
86 | border-bottom: none; /* 1 */
87 | text-decoration: underline; /* 2 */
88 | text-decoration: underline dotted; /* 2 */
89 | }
90 |
91 | /**
92 | * Add the correct font weight in Chrome, Edge, and Safari.
93 | */
94 |
95 | b,
96 | strong {
97 | font-weight: bolder;
98 | }
99 |
100 | /**
101 | * 1. Correct the inheritance and scaling of font size in all browsers.
102 | * 2. Correct the odd `em` font sizing in all browsers.
103 | */
104 |
105 | code,
106 | kbd,
107 | samp {
108 | font-family: monospace, monospace; /* 1 */
109 | font-size: 1em; /* 2 */
110 | }
111 |
112 | /**
113 | * Add the correct font size in all browsers.
114 | */
115 |
116 | small {
117 | font-size: 80%;
118 | }
119 |
120 | /**
121 | * Prevent `sub` and `sup` elements from affecting the line height in
122 | * all browsers.
123 | */
124 |
125 | sub,
126 | sup {
127 | font-size: 75%;
128 | line-height: 0;
129 | position: relative;
130 | vertical-align: baseline;
131 | }
132 |
133 | sub {
134 | bottom: -0.25em;
135 | }
136 |
137 | sup {
138 | top: -0.5em;
139 | }
140 |
141 | /* Embedded content
142 | ========================================================================== */
143 |
144 | /**
145 | * Remove the border on images inside links in IE 10.
146 | */
147 |
148 | img {
149 | border-style: none;
150 | }
151 |
152 | /* Forms
153 | ========================================================================== */
154 |
155 | /**
156 | * 1. Change the font styles in all browsers.
157 | * 2. Remove the margin in Firefox and Safari.
158 | */
159 |
160 | button,
161 | input,
162 | optgroup,
163 | select,
164 | textarea {
165 | font-family: inherit; /* 1 */
166 | font-size: 100%; /* 1 */
167 | line-height: 1.15; /* 1 */
168 | margin: 0; /* 2 */
169 | }
170 |
171 | /**
172 | * Show the overflow in IE.
173 | * 1. Show the overflow in Edge.
174 | */
175 |
176 | button,
177 | input {
178 | /* 1 */
179 | overflow: visible;
180 | }
181 |
182 | /**
183 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
184 | * 1. Remove the inheritance of text transform in Firefox.
185 | */
186 |
187 | button,
188 | select {
189 | /* 1 */
190 | text-transform: none;
191 | }
192 |
193 | /**
194 | * Correct the inability to style clickable types in iOS and Safari.
195 | */
196 |
197 | button,
198 | [type="button"],
199 | [type="reset"],
200 | [type="submit"] {
201 | -webkit-appearance: button;
202 | }
203 |
204 | /**
205 | * Remove the inner border and padding in Firefox.
206 | */
207 |
208 | button::-moz-focus-inner,
209 | [type="button"]::-moz-focus-inner,
210 | [type="reset"]::-moz-focus-inner,
211 | [type="submit"]::-moz-focus-inner {
212 | border-style: none;
213 | padding: 0;
214 | }
215 |
216 | /**
217 | * Restore the focus styles unset by the previous rule.
218 | */
219 |
220 | button:-moz-focusring,
221 | [type="button"]:-moz-focusring,
222 | [type="reset"]:-moz-focusring,
223 | [type="submit"]:-moz-focusring {
224 | outline: 1px dotted ButtonText;
225 | }
226 |
227 | /**
228 | * Correct the padding in Firefox.
229 | */
230 |
231 | fieldset {
232 | padding: 0.35em 0.75em 0.625em;
233 | }
234 |
235 | /**
236 | * 1. Correct the text wrapping in Edge and IE.
237 | * 2. Correct the color inheritance from `fieldset` elements in IE.
238 | * 3. Remove the padding so developers are not caught out when they zero out
239 | * `fieldset` elements in all browsers.
240 | */
241 |
242 | legend {
243 | box-sizing: border-box; /* 1 */
244 | color: inherit; /* 2 */
245 | display: table; /* 1 */
246 | max-width: 100%; /* 1 */
247 | padding: 0; /* 3 */
248 | white-space: normal; /* 1 */
249 | }
250 |
251 | /**
252 | * Add the correct vertical alignment in Chrome, Firefox, and Opera.
253 | */
254 |
255 | progress {
256 | vertical-align: baseline;
257 | }
258 |
259 | /**
260 | * Remove the default vertical scrollbar in IE 10+.
261 | */
262 |
263 | textarea {
264 | overflow: auto;
265 | }
266 |
267 | /**
268 | * 1. Add the correct box sizing in IE 10.
269 | * 2. Remove the padding in IE 10.
270 | */
271 |
272 | [type="checkbox"],
273 | [type="radio"] {
274 | box-sizing: border-box; /* 1 */
275 | padding: 0; /* 2 */
276 | }
277 |
278 | /**
279 | * Correct the cursor style of increment and decrement buttons in Chrome.
280 | */
281 |
282 | [type="number"]::-webkit-inner-spin-button,
283 | [type="number"]::-webkit-outer-spin-button {
284 | height: auto;
285 | }
286 |
287 | /**
288 | * 1. Correct the odd appearance in Chrome and Safari.
289 | * 2. Correct the outline style in Safari.
290 | */
291 |
292 | [type="search"] {
293 | -webkit-appearance: textfield; /* 1 */
294 | outline-offset: -2px; /* 2 */
295 | }
296 |
297 | /**
298 | * Remove the inner padding in Chrome and Safari on macOS.
299 | */
300 |
301 | [type="search"]::-webkit-search-decoration {
302 | -webkit-appearance: none;
303 | }
304 |
305 | /**
306 | * 1. Correct the inability to style clickable types in iOS and Safari.
307 | * 2. Change font properties to `inherit` in Safari.
308 | */
309 |
310 | ::-webkit-file-upload-button {
311 | -webkit-appearance: button; /* 1 */
312 | font: inherit; /* 2 */
313 | }
314 |
315 | /* Interactive
316 | ========================================================================== */
317 |
318 | /*
319 | * Add the correct display in Edge, IE 10+, and Firefox.
320 | */
321 |
322 | details {
323 | display: block;
324 | }
325 |
326 | /*
327 | * Add the correct display in all browsers.
328 | */
329 |
330 | summary {
331 | display: list-item;
332 | }
333 |
334 | /* Misc
335 | ========================================================================== */
336 |
337 | /**
338 | * Add the correct display in IE 10+.
339 | */
340 |
341 | template {
342 | display: none;
343 | }
344 |
345 | /**
346 | * Add the correct display in IE 10.
347 | */
348 |
349 | [hidden] {
350 | display: none;
351 | }
352 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | commonjs: true,
5 | es6: true,
6 | },
7 | parser: 'babel-eslint',
8 | extends: ['eslint:recommended', 'plugin:react/recommended'],
9 | parserOptions: {
10 | ecmaVersion: 7,
11 | // 开启实验属性
12 | ecmaFeatures: {
13 | experimentalObjectRestSpread: true,
14 | // 修饰器
15 | experimentalDecorators: true,
16 | jsx: true,
17 | },
18 | sourceType: 'module',
19 | },
20 | plugins: ['react', '@typescript-eslint'],
21 | globals: {
22 | process: false,
23 | __DEV__: false,
24 | __dirname: false,
25 | window: true,
26 | define: true,
27 | history: true,
28 | location: true,
29 | wxjs: true,
30 | $: true,
31 | WeixinJSBridge: true,
32 | wx: true,
33 | qq: true,
34 | },
35 | settings: {
36 | react: {
37 | version: '17.0.2',
38 | },
39 | },
40 | /**
41 | * "off" 或 0 - 关闭规则
42 | * "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出),
43 | * "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
44 | */
45 | rules: {
46 | 'no-cond-assign': 2,
47 | 'no-console': [
48 | 'error',
49 | {
50 | allow: ['log', 'warn', 'error', 'info'],
51 | },
52 | ],
53 | // 禁止 function 定义中出现重名参数
54 | 'no-dupe-args': 2,
55 | // 禁止对象字面量中出现重复的 key
56 | 'no-dupe-keys': 2,
57 | // 禁止重复的 case 标签
58 | 'no-duplicate-case': 2,
59 | // 禁止空语句块
60 | 'no-empty': 2,
61 | // 禁止对 catch 子句的参数重新赋值
62 | 'no-ex-assign': 2,
63 | // 禁止不必要的布尔转换
64 | 'no-extra-boolean-cast': 2,
65 | // 禁止不必要的括号 //(a * b) + c;//报错
66 | 'no-extra-parens': 0,
67 |
68 | // 强制所有控制语句使用一致的括号风格
69 | curly: ["error", "multi-line"],
70 | // 禁止 catch 子句的参数与外层作用域中的变量同名
71 | 'no-catch-shadow': 0,
72 | // 不允许标签与变量同名
73 | 'no-label-var': 2,
74 | // 禁用特定的全局变量
75 | 'no-restricted-globals': 2,
76 | // 禁止 var 声明 与外层作用域的变量同名
77 | 'no-shadow': 0,
78 | // 禁止覆盖受限制的标识符
79 | 'no-shadow-restricted-names': 2,
80 | // 禁止将变量初始化为 undefined
81 | 'no-undef-init': 2,
82 | // 禁止将 undefined 作为标识符
83 | 'no-undefined': 0,
84 | // 不允许在变量定义之前使用它们
85 | 'no-use-before-define': 0,
86 | // ////////////
87 | // 风格指南 //
88 | // ////////////
89 | // 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
90 | 'array-bracket-spacing': [2, 'never'],
91 | // 禁止或强制在单行代码块中使用空格(禁用)
92 | 'block-spacing': [1, 'never'],
93 | // 强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
94 | // if while function 后面的{必须与if在同一行,java风格。
95 | 'brace-style': [
96 | 2,
97 | '1tbs',
98 | {
99 | allowSingleLine: true,
100 | },
101 | ],
102 | // 控制逗号前后的空格
103 | 'comma-spacing': [
104 | 2,
105 | {
106 | before: false,
107 | after: true,
108 | },
109 | ],
110 | // 控制逗号在行尾出现还是在行首出现 (默认行尾)
111 | // http://eslint.org/docs/rules/comma-style
112 | 'comma-style': [2, 'last'],
113 | // "SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
114 | // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
115 | 'computed-property-spacing': [2, 'never'],
116 | // 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
117 | // e.g [0,"self"] 指定只能 var that = this. self不能指向其他任何值,this也不能赋值给self以外的其他值
118 | 'consistent-this': [2, 'self', 'that', '_self', '_that', 'me', '_this'],
119 | // 强制使用命名的 function 表达式
120 | 'func-names': 0,
121 | // 文件末尾强制换行
122 | 'eol-last': 2,
123 | indent: ['error', 2, { "SwitchCase": 1 }],
124 | // 要求或禁止在函数标识符和其调用之间有空格
125 | 'func-call-spacing': 2,
126 | // 强制在对象字面量的属性中键和值之间使用一致的间距
127 | 'key-spacing': [
128 | 2,
129 | {
130 | beforeColon: false,
131 | afterColon: true,
132 | },
133 | ],
134 | // 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
135 | 'lines-around-comment': [
136 | 2,
137 | {
138 | beforeBlockComment: true,
139 | },
140 | ],
141 | 'func-style': 0,
142 | // 强制回调函数最大嵌套深度 5层
143 | 'max-nested-callbacks': [2, 5],
144 | // 禁止使用指定的标识符
145 | 'id-blacklist': 0,
146 | // 强制标识符的最新和最大长度
147 | 'id-length': 0,
148 | // 要求标识符匹配一个指定的正则表达式
149 | 'id-match': 0,
150 | // 强制在 JSX 属性中一致地使用双引号或单引号
151 | 'jsx-quotes': 0,
152 | // 强制在关键字前后使用一致的空格 (前后腰需要)
153 | 'keyword-spacing': 2,
154 | // 强制一行的最大长度
155 | 'max-len': [2, 200, { ignoreUrls: true }],
156 | // 强制最大行数
157 | 'max-lines': 0,
158 | // 强制 function 定义中最多允许的参数数量
159 | 'max-params': [1, 5],
160 | // 强制 function 块最多允许的的语句数量
161 | 'max-statements': [1, 200],
162 | // 强制每一行中所允许的最大语句数量
163 | 'max-statements-per-line': 0,
164 | // 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
165 | 'new-cap': [
166 | 2,
167 | {
168 | newIsCap: true,
169 | capIsNew: false,
170 | },
171 | ],
172 | // 要求调用无参构造函数时有圆括号
173 | 'new-parens': 2,
174 | // 要求或禁止 var 声明语句后有一行空行
175 | 'newline-after-var': 0,
176 | // 禁止使用 Array 构造函数
177 | 'no-array-constructor': 2,
178 | // 禁用按位运算符
179 | 'no-bitwise': 0,
180 | // 要求 return 语句之前有一空行
181 | 'newline-before-return': 0,
182 | // 要求方法链中每个调用都有一个换行符
183 | 'newline-per-chained-call': 1,
184 | // 禁用 continue 语句
185 | 'no-continue': 0,
186 | // 禁止在代码行后使用内联注释
187 | 'no-inline-comments': 0,
188 | // 禁止 if 作为唯一的语句出现在 else 语句中
189 | 'no-lonely-if': 0,
190 | // 禁止混合使用不同的操作符
191 | 'no-mixed-operators': 0,
192 | // 禁止空格和 tab 的混合缩进
193 | 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
194 | // 不允许多个空行
195 | 'no-multiple-empty-lines': [
196 | 2,
197 | {
198 | max: 2,
199 | },
200 | ],
201 | // 不允许否定的表达式
202 | 'no-negated-condition': 0,
203 | // 不允许使用嵌套的三元表达式
204 | 'no-nested-ternary': 0,
205 | // 禁止使用 Object 的构造函数
206 | 'no-new-object': 2,
207 | // 禁止使用一元操作符 ++ 和 --
208 | 'no-plusplus': 0,
209 | // 禁止使用特定的语法
210 | 'no-restricted-syntax': 0,
211 | // 禁止 function 标识符和括号之间出现空格
212 | 'no-spaced-func': 2,
213 | // 不允许使用三元操作符
214 | 'no-ternary': 0,
215 | // 禁用行尾空格
216 | 'no-trailing-spaces': 2,
217 | // 禁止标识符中有悬空下划线_bar
218 | 'no-underscore-dangle': 0,
219 | // 禁止可以在有更简单的可替代的表达式时使用三元操作符
220 | 'no-unneeded-ternary': 2,
221 | // 禁止属性前有空白
222 | 'no-whitespace-before-property': 2,
223 | // 要求或禁止在 var 声明周围换行
224 | 'one-var-declaration-per-line': 0,
225 | // 要求或禁止在可能的情况下要求使用简化的赋值操作符
226 | 'operator-assignment': 0,
227 | // 强制操作符使用一致的换行符
228 | 'operator-linebreak': [
229 | 2,
230 | 'after',
231 | {
232 | overrides: {
233 | '?': 'before',
234 | ':': 'before',
235 | },
236 | },
237 | ],
238 | // 要求或禁止块内填充
239 | 'padded-blocks': 0,
240 | // 要求对象字面量属性名称用引号括起来
241 | 'quote-props': 0,
242 | // 强制使用一致的反勾号、双引号或单引号
243 | quotes: [2, 'single', 'avoid-escape'],
244 | // 要求使用 JSDoc 注释
245 | 'require-jsdoc': 0,
246 | // 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
247 | // "semi": [2, "always"],
248 | // 强制分号之前和之后使用一致的空格
249 | 'semi-spacing': 2,
250 | // 要求同一个声明块中的变量按顺序排列
251 | 'sort-vars': 0,
252 | // 强制在块之前使用一致的空格
253 | 'space-before-blocks': [2, 'always'],
254 | // 强制在 function的左括号之前使用一致的空格
255 | 'space-before-function-paren': [0, 'always'],
256 | // 强制在圆括号内使用一致的空格
257 | 'space-in-parens': [2, 'never'],
258 | // 要求操作符周围有空格
259 | 'space-infix-ops': 2,
260 | // 强制在一元操作符前后使用一致的空格
261 | 'space-unary-ops': [
262 | 2,
263 | {
264 | words: true,
265 | nonwords: false,
266 | },
267 | ],
268 | // 强制在注释中 // 或 /* 使用一致的空格
269 | 'spaced-comment': [
270 | 2,
271 | 'always',
272 | {
273 | markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!'],
274 | },
275 | ],
276 | // 要求或禁止 Unicode BOM
277 | 'unicode-bom': 2,
278 | // 要求正则表达式被括号括起来
279 | 'wrap-regex': 0,
280 | // 禁止词法声明 (let、const、function 和 class) 出现在 case或default 子句中
281 | 'no-case-declarations': ['warn'],
282 |
283 | // ////////////
284 | // ES6.相关 //
285 | // ////////////
286 | // 要求箭头函数体使用大括号
287 | // 'arrow-body-style': 2,
288 | // 要求箭头函数的参数使用圆括号
289 | 'arrow-parens': 0,
290 | 'arrow-spacing': [
291 | 2,
292 | {
293 | before: true,
294 | after: true,
295 | },
296 | ],
297 | // 强制 generator 函数中 * 号周围使用一致的空格
298 | 'generator-star-spacing': [
299 | 2,
300 | {
301 | before: true,
302 | after: true,
303 | },
304 | ],
305 | // 禁止修改类声明的变量
306 | 'no-class-assign': 2,
307 | // 不允许箭头功能,在那里他们可以混淆的比较
308 | 'no-confusing-arrow': 0,
309 | // 禁止修改 const 声明的变量
310 | 'no-const-assign': 2,
311 | // 禁止类成员中出现重复的名称
312 | 'no-dupe-class-members': 2,
313 | // 每个模块只能使用一个import
314 | 'no-duplicate-imports': 2,
315 | // 禁止 Symbolnew 操作符和 new 一起使用
316 | 'no-new-symbol': 2,
317 | // 允许指定模块加载时的进口
318 | 'no-restricted-imports': 0,
319 | // 禁止在构造函数中,在调用 super() 之前使用 this 或 super
320 | 'no-this-before-super': 2,
321 | // 禁止不必要的计算性能键对象的文字
322 | 'no-useless-computed-key': 0,
323 | // 要求使用 let 或 const 而不是 var
324 | 'no-var': 1,
325 | // 要求或禁止对象字面量中方法和属性使用简写语法
326 | 'object-shorthand': 0,
327 | // 要求使用箭头函数作为回调
328 | 'prefer-arrow-callback': 0,
329 | // 要求使用 const 声明那些声明后不再被修改的变量
330 | 'prefer-const': 0,
331 | // 要求在合适的地方使用 Reflect 方法
332 | 'prefer-reflect': 0,
333 | // 要求使用扩展运算符而非 .apply()
334 | 'prefer-spread': 0,
335 | // 要求使用模板字面量而非字符串连接
336 | 'prefer-template': 0,
337 | // Suggest using the rest parameters instead of arguments
338 | 'prefer-rest-params': 0,
339 | // 要求generator 函数内有 yield
340 | 'require-yield': 2,
341 | // 要求或禁止模板字符串中的嵌入表达式周围空格的使用
342 | 'template-curly-spacing': 1,
343 | // 强制在 yield* 表达式中 * 周围使用空格
344 | 'yield-star-spacing': 2,
345 |
346 | // 强制使用一致的换行风格
347 | 'linebreak-style': [0, 'unix'],
348 | // 在JSX中强制布尔属性符号
349 | 'react/jsx-boolean-value': 2,
350 | // 在JSX中验证右括号位置
351 | // "react/jsx-closing-bracket-location": 1,
352 | // 在JSX属性和表达式中加强或禁止大括号内的空格。
353 | 'react/jsx-curly-spacing': [
354 | 2,
355 | {
356 | when: 'never',
357 | children: true,
358 | },
359 | ],
360 | // 在数组或迭代器中验证JSX具有key属性
361 | 'react/jsx-key': 2,
362 | // 限制JSX中单行上的props的最大数量
363 | 'react/jsx-max-props-per-line': [
364 | 1,
365 | {
366 | maximum: 5,
367 | },
368 | ],
369 | // 防止在JSX中重复的props
370 | 'react/jsx-no-duplicate-props': 2,
371 | // //防止使用未包装的JSX字符串
372 | // "react/jsx-no-literals": 0,
373 | // 在JSX中禁止未声明的变量
374 | 'react/jsx-no-undef': 2,
375 | // 为用户定义的JSX组件强制使用PascalCase
376 | 'react/jsx-pascal-case': 0,
377 | // 防止反应被错误地标记为未使用
378 | 'react/jsx-uses-react': 2,
379 | // 防止在JSX中使用的变量被错误地标记为未使用
380 | 'react/jsx-uses-vars': 2,
381 | // 防止在componentDidMount中使用setState
382 | 'react/no-did-mount-set-state': 2,
383 | // 防止在componentDidUpdate中使用setState
384 | 'react/no-did-update-set-state': 2,
385 | // 防止使用未知的DOM属性
386 | 'react/no-unknown-property': 2,
387 | // 为React组件强制执行ES5或ES6类
388 | 'react/prefer-es6-class': 2,
389 | // 防止在React组件定义中丢失props验证
390 | 'react/prop-types': 0,
391 | // 使用JSX时防止丢失React
392 | 'react/react-in-jsx-scope': 2,
393 | // 防止没有children的组件的额外结束标签
394 | 'react/self-closing-comp': 0,
395 | // 禁止不必要的bool转换
396 | // "no-extra-boolean-cast": 0,
397 | // 防止在数组中遍历中使用数组key做索引
398 | // "react/no-array-index-key": 0,
399 | // 不使用弃用的方法
400 | 'react/no-deprecated': 2,
401 | // 在JSX属性中强制或禁止等号周围的空格
402 | 'react/jsx-equals-spacing': 2,
403 | 'react/jsx-filename-extension': [
404 | 2,
405 | {
406 | extensions: ['.js', '.jsx', 'tsx', 'ts'],
407 | },
408 | ],
409 | // 禁止未使用的变量
410 | 'no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: false }],
411 | },
412 | }
413 |
--------------------------------------------------------------------------------