├── src
├── components
│ ├── left
│ │ ├── left.less
│ │ └── left.tsx
│ ├── header
│ │ ├── header.tsx
│ │ └── header.less
│ └── content
│ │ ├── content.less
│ │ └── content.tsx
├── sessionStorage
│ └── api.js
├── store
│ ├── index.js
│ └── reducer.js
├── index.tsx
├── admin
│ ├── admin.less
│ └── admin.tsx
├── react-app-env.d.ts
├── config.js
└── serviceWorker.ts
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── config
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── pnpTs.js
├── modules.js
├── paths.js
├── env.js
├── webpackDevServer.config.js
└── webpack.config.js
├── .gitignore
├── tsconfig.json
├── scripts
├── test.js
├── start.js
└── build.js
├── README.md
└── package.json
/src/components/left/left.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EdenStrive/X-admin/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/sessionStorage/api.js:
--------------------------------------------------------------------------------
1 | const setStorage = (key,value) =>{
2 | sessionStorage.setItem(key, value);
3 | }
4 |
5 | const getStorage = (key)=>{
6 | return sessionStorage.getItem(key);
7 | }
8 |
9 | export { setStorage, getStorage }
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 | import reducer from './reducer'
3 |
4 | const store = createStore( reducer
5 | , window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
6 | );
7 |
8 | export default store;
--------------------------------------------------------------------------------
/src/components/header/header.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './header.less';
3 |
4 | const Header: React.FC= () => {
5 | return (
6 |
10 | );
11 | }
12 |
13 | export default Header;
14 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/components/content/content.less:
--------------------------------------------------------------------------------
1 | .editable-cell {
2 | position: relative;
3 | }
4 | .editable-cell-value-wrap {
5 | padding: 5px 12px;
6 | cursor: pointer;
7 | }
8 | .editable-row:hover .editable-cell-value-wrap {
9 | border: 1px solid #d9d9d9;
10 | border-radius: 4px;
11 | padding: 4px 11px;
12 | }
13 | .t_content{
14 | border: 1px solid #e4e1e1;
15 | margin-top: 24px;
16 | width: 90%;
17 | margin-left: 5%;
18 | border-radius: 3px;
19 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | X-admin
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './admin/admin';
4 | import * as serviceWorker from './serviceWorker';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 |
8 | // If you want your app to work offline and load faster, you can change
9 | // unregister() to register() below. Note this comes with some pitfalls.
10 | // Learn more about service workers: https://bit.ly/CRA-PWA
11 | serviceWorker.unregister();
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 |
26 | #
27 | node_modules/
28 |
29 | #
30 | dist/
31 |
32 | #
33 | package-lock.json
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/header/header.less:
--------------------------------------------------------------------------------
1 | .h_body{
2 | position: relative;
3 | top: 0px;
4 | left: 0px;
5 | // border:1px solid black;
6 | height: 80px;
7 | width: 100%;
8 | display: flex;
9 | flex-direction: row;
10 | div:nth-child(1){
11 | width: 230px;
12 | font-size: 30px;
13 | // border:1px solid rgb(190, 35, 35);
14 | text-align: center;
15 | line-height: 70px;
16 | color: rgb(255,255,255);
17 | background: rgb(117, 113, 249);
18 | min-width: 230px;
19 | }
20 | div:nth-child(2){
21 | // border:10px solid rgb(47, 82, 158);
22 | width: calc(100vw - 230px);
23 | min-width: 970px;
24 | background: rgb(235, 245, 255)
25 | }
26 | }
--------------------------------------------------------------------------------
/config/pnpTs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { resolveModuleName } = require('ts-pnp');
4 |
5 | exports.resolveModuleName = (
6 | typescript,
7 | moduleName,
8 | containingFile,
9 | compilerOptions,
10 | resolutionHost
11 | ) => {
12 | return resolveModuleName(
13 | moduleName,
14 | containingFile,
15 | compilerOptions,
16 | resolutionHost,
17 | typescript.resolveModuleName
18 | );
19 | };
20 |
21 | exports.resolveTypeReferenceDirective = (
22 | typescript,
23 | moduleName,
24 | containingFile,
25 | compilerOptions,
26 | resolutionHost
27 | ) => {
28 | return resolveModuleName(
29 | moduleName,
30 | containingFile,
31 | compilerOptions,
32 | resolutionHost,
33 | typescript.resolveTypeReferenceDirective
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/src/admin/admin.less:
--------------------------------------------------------------------------------
1 |
2 | *{
3 | margin: 0px;
4 | padding: 0px;
5 | border: 0px;
6 | }
7 |
8 | .father{
9 | position: relative;
10 | width: 100%;
11 | height: 100%;
12 | min-width: 1200px;
13 |
14 | .headers{
15 | position: relative;
16 | top: 0px;
17 | left: 0px;
18 | // border:1px solid black;
19 | height: 80px;
20 | width: 100%;
21 | }
22 | }
23 |
24 |
25 | .bodys{
26 | position: relative;
27 | width: 100%;
28 | height: 100%;
29 | display: flex;
30 | flex-direction: row;
31 | .left{
32 | position: relative;
33 | width: 230px;
34 | left: 0px;
35 | min-height: calc(100vh - 80px);
36 | // border:1px solid red
37 | }
38 | .right{
39 | position: relative;
40 | height: 100%;
41 | // border:1px solid green;
42 | width: calc(100% - 230px);
43 | min-height: calc(100vh - 80px);
44 | background-color: rgb(255, 255, 255)
45 | }
46 | }
47 |
48 | .xxx{
49 | font-size: 25px;
50 | margin-top: 30px;
51 | margin-left: 20px;
52 | }
--------------------------------------------------------------------------------
/src/admin/admin.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Header from '../components/header/header'
3 | import Left from '../components/left/left'
4 | import store from '../store/index'
5 | import Content from '../components/content/content'
6 | import './admin.less';
7 |
8 | const App: React.FC= () => {
9 | const [welcome, setWelco] = useState(false)
10 |
11 | const viewTable = ()=>{
12 | setWelco(true)
13 | console.log("最后删除的元素为:",store.getState().delete)
14 | console.log("最后添加的元素为:",store.getState().add)
15 | }
16 |
17 | store.subscribe(viewTable)
18 |
19 | return (
20 |
21 | {/* 后台管理头部 */}
22 |
23 |
24 |
25 |
26 | {/* 后台管理底部 */}
27 |
28 |
29 | {/* body左导航 */}
30 |
31 |
32 |
33 |
34 | {/* 内容 */}
35 |
36 |
37 | {welcome ?
:
点击左侧导航进行每个表格的新增、编辑与删除
}
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
45 | export default App;
46 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const camelcase = require('camelcase');
5 |
6 | // This is a custom Jest transformer turning file imports into filenames.
7 | // http://facebook.github.io/jest/docs/en/webpack.html
8 |
9 | module.exports = {
10 | process(src, filename) {
11 | const assetFilename = JSON.stringify(path.basename(filename));
12 |
13 | if (filename.match(/\.svg$/)) {
14 | // Based on how SVGR generates a component name:
15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
16 | const pascalCaseFileName = camelcase(path.parse(filename).name, {
17 | pascalCase: true,
18 | });
19 | const componentName = `Svg${pascalCaseFileName}`;
20 | return `const React = require('react');
21 | module.exports = {
22 | __esModule: true,
23 | default: ${assetFilename},
24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
25 | return {
26 | $$typeof: Symbol.for('react.element'),
27 | type: 'svg',
28 | ref: ref,
29 | key: null,
30 | props: Object.assign({}, props, {
31 | children: ${assetFilename}
32 | })
33 | };
34 | }),
35 | };`;
36 | }
37 |
38 | return `module.exports = ${assetFilename};`;
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/src/store/reducer.js:
--------------------------------------------------------------------------------
1 | //由于同级组件之间传参非常的麻烦,所以此处用redux
2 | const defaultState = {
3 | admin: 0,
4 | content:[
5 | [
6 | {
7 | key: 0
8 | }
9 | ]
10 | ],
11 | delete:null,
12 | add:null
13 | }
14 |
15 |
16 |
17 |
18 |
19 | //行为
20 | export default (state = defaultState, action)=>{
21 | //reducer 可以接受state,但是绝对不能修改state
22 | const newState = JSON.parse(JSON.stringify(state)) //对数据进行深拷贝
23 |
24 | if (action.type == "change_admin") {
25 | newState.admin = action.value
26 | if (newState.content[action.value] == undefined) {
27 | newState.content[action.value] = [
28 | {
29 | key: 0
30 | }
31 | ]
32 | }
33 | }else if(action.type == "change_content"){
34 | newState.content[action.value] = action.content
35 | }else if(action.type == "change_session"){
36 | newState.admin = action.value
37 | newState.content[action.value] = JSON.parse(action.local)
38 | }else if(action.type == "bug_bug"){
39 | newState.content[0] = JSON.parse(action.value)
40 | }else if(action.type == "delete"){
41 | newState.delete = action.key
42 | }else if(action.type == "add"){
43 | newState.add = action.key
44 | }
45 | //新的newState会自动替换老的state
46 | return newState
47 | }
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | declare namespace NodeJS {
6 | interface ProcessEnv {
7 | readonly NODE_ENV: 'development' | 'production' | 'test';
8 | readonly PUBLIC_URL: string;
9 | }
10 | }
11 |
12 | declare module '*.bmp' {
13 | const src: string;
14 | export default src;
15 | }
16 |
17 | declare module '*.gif' {
18 | const src: string;
19 | export default src;
20 | }
21 |
22 | declare module '*.jpg' {
23 | const src: string;
24 | export default src;
25 | }
26 |
27 | declare module '*.jpeg' {
28 | const src: string;
29 | export default src;
30 | }
31 |
32 | declare module '*.png' {
33 | const src: string;
34 | export default src;
35 | }
36 |
37 | declare module '*.webp' {
38 | const src: string;
39 | export default src;
40 | }
41 |
42 | declare module '*.svg' {
43 | import * as React from 'react';
44 |
45 | export const ReactComponent: React.FunctionComponent>;
46 |
47 | const src: string;
48 | export default src;
49 | }
50 |
51 | declare module '*.module.css' {
52 | const classes: { [key: string]: string };
53 | export default classes;
54 | }
55 |
56 | declare module '*.module.scss' {
57 | const classes: { [key: string]: string };
58 | export default classes;
59 | }
60 |
61 | declare module '*.module.sass' {
62 | const classes: { [key: string]: string };
63 | export default classes;
64 | }
65 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI or explicitly running all tests
42 | if (
43 | !process.env.CI &&
44 | argv.indexOf('--watchAll') === -1
45 | ) {
46 | // https://github.com/facebook/create-react-app/issues/5210
47 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
48 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
49 | }
50 |
51 |
52 | jest.run(argv);
53 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | const obj = {
2 | //左边导航内容
3 | sub:[
4 | ["sub1","mail","Part One",[
5 |
6 | [0,"班级"],
7 | [1,"水果"]
8 |
9 | ]],
10 | ["sub2","appstore","Part Two",[
11 |
12 | [2,"工单"],
13 |
14 | ]]
15 | ],
16 |
17 | //表格内容
18 | content:[
19 | [
20 | {
21 | title: '姓名',
22 | dataIndex: '姓名',
23 | width: '30%',
24 | editable: true,
25 | },
26 | {
27 | title: '年龄',
28 | dataIndex: '年龄',
29 | editable: true,
30 | },
31 | {
32 | title: '学号',
33 | dataIndex: '学号',
34 | editable: true,
35 | }
36 | ],
37 |
38 | [
39 | {
40 | title: '果名',
41 | dataIndex: '果名',
42 | width: '20%',
43 | editable: true,
44 | },
45 | {
46 | title: '大小',
47 | dataIndex: '大小',
48 | editable: true,
49 | },
50 | {
51 | title: '价格',
52 | dataIndex: '价格',
53 | editable: true,
54 | }
55 | ],
56 |
57 | [
58 | {
59 | title: '名称',
60 | dataIndex: '名称',
61 | width: '20%',
62 | editable: true,
63 | },
64 | {
65 | title: '价格',
66 | dataIndex: '价格',
67 | editable: true,
68 | }
69 |
70 | ]
71 |
72 |
73 |
74 | ]
75 | }
76 | export default obj
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # X-admin
2 | 基于React(hook)+Typescript+antd的后台管理系统的脚手架,只需配置文件即可
3 |
4 | 效果预览地址:
5 | - [点击这里](http://www.xingwenpeng.cn:8010/)
6 |
7 | 2019-06-24
8 |
9 | > * 添加项目整体目录完成
10 |
11 | 2019-07-06
12 |
13 | > * 实践react hook搭配antd(Ts写法)
14 |
15 | > * 添加配置文件config.js,实现配置文件实现动态增加左侧导航功能
16 |
17 | 2019-07-08
18 |
19 | > * 基本完成脚手架(后续进行完善)
20 |
21 | 2019-07-09
22 |
23 | - [x] 功能已全部实现
24 | - [x] 暴露接口
25 |
26 | 2019-07-12
27 |
28 | - [ ] 反馈有bug,解决中(无法及时响应删除的添加的反应,逐条处理)
29 |
30 | ------
31 | **框架使用:**
32 |
33 | 环境要求:
34 |
35 | > * node版本 10+
36 | > * npm版本 6+
37 |
38 | ```javascript
39 | 1.首先git clone下来
40 | 2.使用脚手架目录打开cmd,输入命令npm install(如果失败请使用管理员权限打开cmd)
41 | 3.在命令行中输入npm start
42 | ```
43 |
44 | 打开界面:
45 |
46 | 红色框框处使我们第一个需要配置的地方:
47 |
48 | 1.打开src目录下面的config.js
49 | 2.进行配置:
50 |
51 | 
52 |
53 | sub对应的为为左侧导航。content为导航所对应的表格字段
54 |
55 | 白色箭头所指数组的前三个元素分别对应导航key值,导航的图标,以及导航的名称。
56 |
57 | 数组第四个元素为数组,对应为导航子元素的key值以及子元素的名称。
58 |
59 | 注意:每次添加完子元素时。都要在下面content数组中对应的key值中添加相应的表格字段,比如班级的ke y为0,那就在content数组中0位置添加表格字段。
60 |
61 | 表格字段可选width,title,以及是否可以被编辑editable
62 |
63 | ----
64 |
65 | 1.都设置完成后打开界面
66 | 
67 |
68 | 2. 如下图可以添加表格内容,删除内容,直接点击空表格进行编辑(刷新页面不会使数据丢失)
69 |
70 | 
71 |
72 | 3.在admin.tsx中暴露新增数据与删除数据的接口,删除元素为子导航的key+被删除数据的key,添加为被删除数据的key+子导航的key。
73 |
74 | 
75 |
76 | ----
77 |
78 | 总结:框架还在完善中,有很多需要完善的地方,框架扩展性比较强。都看到这里了,大佬点个start吧
79 | 
80 |
--------------------------------------------------------------------------------
/config/modules.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const paths = require('./paths');
6 | const chalk = require('react-dev-utils/chalk');
7 |
8 | /**
9 | * Get the baseUrl of a compilerOptions object.
10 | *
11 | * @param {Object} options
12 | */
13 | function getAdditionalModulePaths(options = {}) {
14 | const baseUrl = options.baseUrl;
15 |
16 | // We need to explicitly check for null and undefined (and not a falsy value) because
17 | // TypeScript treats an empty string as `.`.
18 | if (baseUrl == null) {
19 | // If there's no baseUrl set we respect NODE_PATH
20 | // Note that NODE_PATH is deprecated and will be removed
21 | // in the next major release of create-react-app.
22 |
23 | const nodePath = process.env.NODE_PATH || '';
24 | return nodePath.split(path.delimiter).filter(Boolean);
25 | }
26 |
27 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
28 |
29 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is
30 | // the default behavior.
31 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
32 | return null;
33 | }
34 |
35 | // Allow the user set the `baseUrl` to `appSrc`.
36 | if (path.relative(paths.appSrc, baseUrlResolved) === '') {
37 | return [paths.appSrc];
38 | }
39 |
40 | // Otherwise, throw an error.
41 | throw new Error(
42 | chalk.red.bold(
43 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." +
44 | ' Create React App does not support other values at this time.'
45 | )
46 | );
47 | }
48 |
49 | function getModules() {
50 | // Check if TypeScript is setup
51 | const hasTsConfig = fs.existsSync(paths.appTsConfig);
52 | const hasJsConfig = fs.existsSync(paths.appJsConfig);
53 |
54 | if (hasTsConfig && hasJsConfig) {
55 | throw new Error(
56 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
57 | );
58 | }
59 |
60 | let config;
61 |
62 | // If there's a tsconfig.json we assume it's a
63 | // TypeScript project and set up the config
64 | // based on tsconfig.json
65 | if (hasTsConfig) {
66 | config = require(paths.appTsConfig);
67 | // Otherwise we'll check if there is jsconfig.json
68 | // for non TS projects.
69 | } else if (hasJsConfig) {
70 | config = require(paths.appJsConfig);
71 | }
72 |
73 | config = config || {};
74 | const options = config.compilerOptions || {};
75 |
76 | const additionalModulePaths = getAdditionalModulePaths(options);
77 |
78 | return {
79 | additionalModulePaths: additionalModulePaths,
80 | hasTsConfig,
81 | };
82 | }
83 |
84 | module.exports = getModules();
85 |
--------------------------------------------------------------------------------
/src/components/left/left.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { Menu, Icon } from 'antd';
3 | import { getStorage } from '../../sessionStorage/api'
4 | import obj from '../../config'
5 | import store from '../../store/index'
6 | import 'antd/dist/antd.css';
7 | import './left.less';
8 |
9 | const { SubMenu } = Menu;
10 | const Left: React.FC= () => {
11 |
12 | //初始化状态 openKeys为 sub1 同时定义状态为string
13 | const [ openKeys , setOpenKeys ] = useState(["sub1"])
14 | const [ rootSubmenuKeys, setRoot] = useState([]) //切换
15 | const [ meau , setMeau ] = useState([])
16 |
17 | //副作用
18 | const onOpenChange = (openKeys:string[]) =>{
19 | const latestOpenKey:any = openKeys.find(key => openKeys.indexOf(key) === -1);
20 | if(rootSubmenuKeys.indexOf(latestOpenKey) === -1){
21 | //hook特性不需要加花括号
22 | setOpenKeys(openKeys);
23 | }else {
24 | setOpenKeys(
25 | latestOpenKey ? [latestOpenKey] : []
26 | );
27 | }
28 | }
29 |
30 | const change_content = (key:any)=>{
31 | let keys = key.key
32 | if (getStorage(keys) == undefined) {
33 | const action = {
34 | type:"change_admin",
35 | value:keys
36 | }
37 | store.dispatch(action)
38 | }else{
39 | const action = {
40 | type:"change_session",
41 | value:keys,
42 | local: getStorage(keys)
43 | }
44 | store.dispatch(action)
45 | }
46 | }
47 |
48 | useEffect(()=>{
49 | // console.log(obj.sub) //sub就是配置文件中的左边导航
50 | let sub:any[] = obj.sub
51 | let total_arr:any[] = []
52 | let root:any[] = []
53 |
54 | for(let id = 0; id < sub.length; id++){
55 | let child_arr:any[] = []
56 | let big_key = sub[id][0] //big key
57 | let big_type = sub[id][1]//big type
58 | let big_name = sub[id][2]//big name
59 | let child = sub[id][3]
60 | for(let id in child){
61 | let key = child[id][0]
62 | let content = child[id][1]
63 | let childs = ({content})
64 | child_arr.push(childs)
65 | }
66 | let part = ( {big_name} } >
67 | {child_arr}
68 | )
69 | total_arr.push(part)
70 | root.push(big_key)
71 | }
72 | setRoot(root)
73 | setMeau(total_arr)
74 | },[])
75 |
76 | return (
77 |
78 |
81 |
82 | );
83 | }
84 |
85 | export default Left;
86 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebook/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(inputPath, needsSlash) {
15 | const hasSlash = inputPath.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return inputPath.substr(0, inputPath.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${inputPath}/`;
20 | } else {
21 | return inputPath;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right