├── .gitignore ├── .idea ├── encodings.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jsLibraryMappings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── CBDB.iml ├── CBDB.ipr ├── CBDB.iws ├── LICENSE ├── README.md ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── paths.js ├── polyfills.js ├── webpack.config.dev.js ├── webpack.config.prod.js └── webpackDevServer.config.js ├── imgs ├── cbdb.jpg └── slt.png ├── package-lock.json ├── package.json ├── public ├── Ming.geojson ├── Qing.geojson ├── Song.geojson ├── Tang.json ├── Yuan.geojson ├── china.json ├── favicon.ico ├── index.html └── manifest.json ├── scripts ├── build.js ├── start.js └── test.js ├── src ├── App.css ├── App.js ├── App.test.js ├── CBDB.jpg ├── components │ ├── charts │ │ ├── D3SimpleForceChart.js │ │ └── D3SimpleForceChart1.js │ └── layout │ │ ├── RootBreadcrumb.js │ │ ├── RootHeader.js │ │ └── SiderMenus.js ├── containers │ ├── Home.js │ ├── NotFound.js │ ├── SimpleForceChart.js │ ├── SimpleMapTimelineChart.js │ └── SimpleRelationshipChart.js ├── index.css ├── index.js ├── logo.svg ├── registerServiceWorker.js ├── routes │ └── index.js ├── style │ ├── CBDB.jpg │ ├── CBDB.png │ ├── Charts.css │ ├── Common.css │ ├── Home.css │ ├── SimpleForceChart.css │ └── Tables.css └── utils │ ├── d3-cloud.js │ ├── d3-cubic-scale-radial.js │ ├── d3-quartic-scale-radial.js │ ├── d3-scale-radial.js │ ├── d3-viz.js │ ├── envconfig.js │ └── tools.js ├── webpack.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 21 | 22 | 486 | 487 | 488 | 489 | 490 | 491 | 494 | 495 | 497 | 498 | 499 | 500 | 501 |
502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 |
序号人名官职名就职地生年终年
514 |
515 | 516 | 517 | 518 | 519 | 520 | ) 521 | } 522 | } 523 | 524 | 525 | export default SimpleMapTimelineChart; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { HashRouter } from 'react-router-dom'; 4 | import './index.css'; 5 | import App from './App'; 6 | // import registerServiceWorker from './registerServiceWorker'; 7 | 8 | ReactDOM.render(( 9 | 10 | 11 | 12 | ), document.getElementById('root')); 13 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (!isLocalhost) { 36 | // Is not local host. Just register service worker 37 | registerValidSW(swUrl); 38 | } else { 39 | // This is running on localhost. Lets check if a service worker still exists or not. 40 | checkValidServiceWorker(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Home from '../containers/Home'; 3 | import { Route, Switch } from 'react-router-dom'; 4 | import SimpleForceChart from '../containers/SimpleForceChart'; 5 | import NotFound from '../containers/NotFound'; 6 | import RootBreadcrumb from '../components/layout/RootBreadcrumb'; 7 | import SiderMenus from '../components/layout/SiderMenus'; 8 | import SimpleMapTimelineChart from "../containers/SimpleMapTimelineChart"; 9 | import SimpleRelationshipChart from "../containers/SimpleRelationshipChart"; 10 | 11 | 12 | export const SiderMenusRoute = () => 13 | 14 | 15 | export const RootBreadcrumbRoute = () => 16 | 17 | 18 | export const ContentRoute = () => 19 | 20 | 21 | 22 | 23 | 24 | {/* Finally, catch all unmatched routes */} 25 | 26 | 27 | 28 | export default ContentRoute 29 | -------------------------------------------------------------------------------- /src/style/CBDB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YLDJack/CBDB-VisualizationSystem/3bf46f4e2aef187850328806c05a8ce0ebf9f06d/src/style/CBDB.jpg -------------------------------------------------------------------------------- /src/style/CBDB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YLDJack/CBDB-VisualizationSystem/3bf46f4e2aef187850328806c05a8ce0ebf9f06d/src/style/CBDB.png -------------------------------------------------------------------------------- /src/style/Charts.css: -------------------------------------------------------------------------------- 1 | .d3-tip { 2 | font-size: 12px; 3 | line-height: 1; 4 | font-weight: bold; 5 | padding: 12px; 6 | background: rgba(121, 85, 72, 0.8); 7 | color: #fff; 8 | border-radius: 2px; 9 | line-height: 1.5; 10 | } 11 | /* Creates a small triangle extender for the tooltip */ 12 | 13 | .d3-tip:after { 14 | box-sizing: border-box; 15 | display: inline; 16 | font-size: 10px; 17 | width: 100%; 18 | line-height: 1; 19 | color: rgba(121, 85, 72, 0.8); 20 | content: "\25BC"; 21 | position: absolute; 22 | text-align: center; 23 | } 24 | /* Style northward tooltips differently */ 25 | 26 | .d3-tip.n:after { 27 | margin: -3px 0 0 0; 28 | top: 100%; 29 | left: 0; 30 | } 31 | 32 | .bar-chart--simple .bar:hover{ 33 | fill:purple; 34 | cursor: pointer; 35 | } 36 | 37 | .stacked-demo.d3-tip { 38 | background: rgba(0, 0, 0, 1); 39 | } 40 | 41 | .stacked-demo.d3-tip:after { 42 | color: rgba(0, 0, 0, 1); 43 | } 44 | 45 | .group-tick line { 46 | stroke: #000; 47 | } 48 | 49 | .ribbons { 50 | fill-opacity: 0.67; 51 | } 52 | 53 | .simple-chord.d3-tip:after{ 54 | display: none; 55 | } 56 | 57 | .simple-pack-chart-demo .node--hover circle { 58 | stroke: #000; 59 | stroke-width: 1.2px; 60 | } 61 | 62 | .simple-pack-chart-demo text { 63 | font: 10px sans-serif; 64 | text-anchor: middle; 65 | fill:#333; 66 | } 67 | .radar-chart-demo .radar-line:hover{ 68 | fill-opacity:0.5; 69 | stroke-width: 3px; 70 | } 71 | 72 | .force-chart--simple .links line{ 73 | stroke: #ddd; 74 | } 75 | 76 | .force-chart--simple .nodes text{ 77 | font-size: 16px; 78 | font-weight: 700; 79 | } 80 | 81 | .force-chart--simple .nodes g{ 82 | cursor: move; 83 | } 84 | 85 | .force-chart--simple .link-text text { 86 | font-size: 14px ; 87 | fill:#000; 88 | fill-opacity: 0; 89 | } 90 | 91 | .tag-cloud-chart--simple .nodes text { 92 | font-weight: 700; 93 | } 94 | 95 | .simple-voronoi-chart-demo .links { 96 | stroke: #fff; 97 | } 98 | 99 | .simple-voronoi-chart-demo .polygons { 100 | stroke: none; 101 | } 102 | 103 | .simple-voronoi-chart-demo .polygons :first-child { 104 | fill: #ccc; 105 | } 106 | 107 | .simple-voronoi-chart-demo .sites { 108 | fill: #fff; 109 | stroke: #fff; 110 | } 111 | 112 | .simple-voronoi-chart-demo .sites :first-child { 113 | fill: #fff; 114 | } 115 | -------------------------------------------------------------------------------- /src/style/Common.css: -------------------------------------------------------------------------------- 1 | small { 2 | opacity: .9; 3 | } 4 | .text-muted{ 5 | opacity: .9; 6 | } 7 | .clear{ 8 | display: block; 9 | overflow: hidden; 10 | } 11 | .center { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | } 16 | .y-center{ 17 | display: flex; 18 | align-items: center; 19 | } 20 | .block{ 21 | display: block; 22 | } 23 | .inline { 24 | display: inline; 25 | } 26 | .none{ 27 | display: none; 28 | } 29 | .b-white { 30 | border-color: #ffffff; 31 | } 32 | .w-full { 33 | width: 100%; 34 | } 35 | 36 | .w-auto { 37 | width: auto; 38 | } 39 | 40 | .h-auto { 41 | height: auto; 42 | } 43 | 44 | .h-full { 45 | height: 100%; 46 | } 47 | 48 | .h-v { 49 | height: 100vh; 50 | } 51 | 52 | .h-v-5 { 53 | height: 50vh; 54 | } 55 | 56 | 57 | .pull-left { 58 | float: left; 59 | } 60 | 61 | .pull-right { 62 | float: right; 63 | } 64 | 65 | .w-40 { 66 | width: 40px; 67 | height: 40px; 68 | line-height: 40px; 69 | display: inline-block; 70 | text-align: center; 71 | } 72 | 73 | .text{ 74 | font-size: 1rem; 75 | } 76 | .text-2x{ 77 | font-size: 2rem; 78 | } 79 | .text-3x{ 80 | font-size: 3rem; 81 | } 82 | .text-4x{ 83 | font-size: 4rem; 84 | } 85 | 86 | .text-center { 87 | text-align: center; 88 | } 89 | 90 | .text-left { 91 | text-align: left; 92 | } 93 | 94 | .text-right { 95 | text-align: right; 96 | } 97 | 98 | .text-danger, 99 | .text-danger-hover a:hover { 100 | color: #f44455 !important; 101 | } 102 | .text-dark, 103 | .text-dark-hover a:hover { 104 | color: #2e3e4e !important; 105 | } 106 | .text-info, 107 | .text-info-hover a:hover { 108 | color: #6887ff !important; 109 | } 110 | .text-success, 111 | .text-success-hover a:hover { 112 | color: #6cc788 !important; 113 | } 114 | .text-blue, 115 | .text-blue-hover a:hover { 116 | color: #2196f3 !important; } 117 | 118 | .pb-s { padding-bottom: 0.5rem !important; } 119 | .pb-m { padding-bottom: 1rem !important; } 120 | .pb-l { padding-bottom: 2rem !important; } 121 | .pa-s { padding: 0.5rem !important; } 122 | .pa-m { padding: 1rem !important; } 123 | .pa-l { padding: 2rem !important; } 124 | 125 | .mr-s { margin-right: 0.5rem !important; } 126 | .mr-m { margin-right: 1rem !important; } 127 | .mr-l { margin-right: 2rem !important; } 128 | .mb-s { margin-bottom: 0.5rem !important; } 129 | .mb-m { margin-bottom: 1rem !important; } 130 | .mb-l { margin-bottom: 2rem !important; } 131 | 132 | .img-responsive { 133 | width: 100%; 134 | height: auto; 135 | } 136 | 137 | .img-circle { 138 | border-radius: 50%; 139 | } 140 | 141 | #root .logo { 142 | width: 120px; 143 | height: 31px; 144 | background: #333; 145 | border-radius: 6px; 146 | margin: 16px 28px 16px 0; 147 | float: left; 148 | line-height: 31px; 149 | text-align: center; 150 | color: #fff; 151 | } 152 | 153 | .ant-menu-submenu-horizontal > .ant-menu { 154 | width: 120px; 155 | left: -40px; 156 | } 157 | 158 | .header.fixed { 159 | position: fixed; 160 | top: 0; 161 | left: 0; 162 | width: 100%; 163 | z-index: 10; 164 | } 165 | 166 | .ant-layout-sider.fixed { 167 | position: fixed; 168 | top: 64px; 169 | left: 0; 170 | z-index: 10; 171 | min-height: 100%; 172 | height: 100%; 173 | } 174 | 175 | .content-max{ 176 | padding: 0 10px 10px 210px; 177 | transition: padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1); 178 | } 179 | 180 | .content-normal{ 181 | padding: 0 10px 10px 74px; 182 | } 183 | 184 | .ant-layout-sider, .ant-layout-sider-trigger { 185 | transition: all 0.15s cubic-bezier(0.645, 0.045, 0.355, 1); 186 | } 187 | 188 | .ant-layout-sider-children { 189 | height: 100%; 190 | overflow-y:auto; 191 | } 192 | .ant-layout-sider-collapsed .ant-layout-sider-children{ 193 | overflow-y:visible; 194 | } 195 | 196 | .ant-layout-sider-children::-webkit-scrollbar { 197 | width: 4px; 198 | height: 4px; 199 | background-color: transparent; 200 | } 201 | .ant-layout-sider-children::-webkit-scrollbar-track { 202 | -webkit-box-shadow: inset 0 0 2px rgba(0,0,0,0.3); 203 | border-radius: 2px; 204 | background-color: #29065b; 205 | } 206 | .ant-layout-sider-children::-webkit-scrollbar-thumb { 207 | border-radius: 2px; 208 | -webkit-box-shadow: inset 0 0 2px rgba(0,0,0,.3); 209 | background-color: #ccc; 210 | } 211 | 212 | .ant-menu-dark .ant-menu-item .ant-badge > a { 213 | color: rgba(255, 255, 255, 0.67); 214 | } 215 | 216 | .ant-menu-dark .ant-menu-item .ant-badge :hover > a, .ant-menu-dark .ant-menu-item-active .ant-badge > a, .ant-menu-dark .ant-menu-submenu-active .ant-badge > a, .ant-menu-dark:not(.ant-menu-inline) .ant-menu-submenu-open .ant-badge > a, .ant-menu-dark .ant-menu-submenu-selected .ant-badge > a, .ant-menu-dark .ant-menu-submenu-title:hover .ant-badge > a { 217 | color: #fff; 218 | text-decoration: none; 219 | } -------------------------------------------------------------------------------- /src/style/Home.css: -------------------------------------------------------------------------------- 1 | h1{ 2 | text-align: center; 3 | font-size:4em; 4 | } 5 | 6 | .back{ 7 | background-image: url("CBDB.png"); 8 | background-repeat: no-repeat; 9 | width: 100%; 10 | height: 600px; 11 | background-position: center center; 12 | } 13 | .gutter-box { /*地图属性块属性*/ 14 | display: inline-block; 15 | vertical-align:top; /*两张Card保持并列*/ 16 | width: 75%; 17 | } 18 | 19 | .map{ /*地图长宽属性*/ 20 | height:1400px; 21 | width:1200px; 22 | } 23 | 24 | 25 | /* simplechinachart 's css*/ 26 | .info-div{ /*信息卡块属性*/ 27 | display: inline-block; 28 | vertical-align:top; 29 | width:25%; 30 | height:100%; 31 | } 32 | 33 | .info-div1{ /*信息卡块属性*/ 34 | 35 | /*position: relative;*/ 36 | /*vertical-align:top;*/ 37 | /*float: left;*/ 38 | width:100%; 39 | /*height:30%;*/ 40 | margin: 0px; 41 | } 42 | 43 | .info-div2{ 44 | display: inline-block; 45 | vertical-align:top; 46 | width:20%; 47 | } 48 | 49 | 50 | .select-div{ 51 | width: 100%; 52 | height: 30px; 53 | border-radius: 5px; 54 | /*box-shadow: 0 0 5px #ccc;*/ 55 | position: relative; 56 | margin-bottom: 10px; 57 | } 58 | 59 | .select-style{ 60 | /*border: none;*/ /*清除select的边框样式*/ 61 | /*outline: none; */ /*清除select聚焦时候的边框颜色*/ 62 | width: 35%; /*将select的宽高等于div的宽高*/ 63 | height: 30px; 64 | line-height: 40px; 65 | /*appearance:none; *//*隐藏select的下拉图标*/ 66 | /*-webkit-appearance: none; 67 | -moz-appearance: none;*/ 68 | margin-left:5px; 69 | } 70 | 71 | .button-style{ 72 | width: 20%; 73 | height: 30px; 74 | margin-left: 5px; 75 | text-decoration:none; 76 | background: #e8e8e8; /*按钮背景色*/ 77 | color: #464646; 78 | font-size:12px; 79 | border: 0; 80 | outline: none; 81 | -webkit-transition:all linear 0.30s; 82 | -moz-transition:all linear 0.30s; 83 | transition:all linear 0.30s; 84 | } 85 | 86 | .table-div{ /*表格属性*/ 87 | height:320px; 88 | overflow-y: auto; /*超出自动显示*/ 89 | } 90 | 91 | .table-div1{ /*表格属性*/ 92 | overflow-y: auto; /*超出自动显示*/ 93 | /* height:100%;*/ 94 | margin-bottom: 36px; 95 | } 96 | /*simplechinachart 's css*/ 97 | 98 | 99 | 100 | /*simplemaptimeline 's css*/ 101 | .gutter-box-timeline{ 102 | display: inline-block; 103 | vertical-align:top; /*两张Card保持并列*/ 104 | height:1000px; 105 | width:75%; 106 | } 107 | 108 | .gutter-box-timeline1{ 109 | display: inline-block; 110 | vertical-align:top; /*两张Card保持并列*/ 111 | height:1000px; 112 | width:40%; 113 | } 114 | 115 | 116 | /*simplemaptimeline 's css*/ 117 | 118 | 119 | 120 | .no-padding .ant-card-body { 121 | padding: 0 !important; 122 | } 123 | .ant-card { 124 | overflow: hidden; 125 | font-size: 13px; 126 | } 127 | 128 | .card-tool { 129 | position: absolute; 130 | right: 24px; 131 | top: 24px; 132 | } 133 | 134 | .list-group .list-group-item { 135 | position: relative; 136 | display: block; 137 | margin-bottom: -1px; 138 | padding: 12px 16px; 139 | background: transparent; 140 | border: 1px solid #ddd; 141 | border-color: rgba(120, 130, 140, 0.065); 142 | border-width: 1px 0; 143 | } 144 | 145 | .list-group .list-group-item:first-child { 146 | border-top-width: 0; 147 | } 148 | 149 | .list-group .list-group-item:last-child { 150 | border-bottom-width: 0; 151 | } 152 | 153 | 154 | /*搜索框1*/ 155 | .bar1 { 156 | text-align: center; 157 | margin: 10px; 158 | } 159 | 160 | .bar1 input { 161 | text-align: center; 162 | height: 40px; 163 | width: 300px; 164 | border: 2px solid #bababa; 165 | border-radius: 2px; 166 | background: #f7f7f7; 167 | color: #545454; 168 | font-size: 14px; 169 | } 170 | .bar1 button { 171 | text-align: center; 172 | height: 40px; 173 | width: 72px; 174 | border-radius: 2px; 175 | margin-left: 30px; 176 | border-radius: 2px; 177 | background: #e2e2e2; 178 | font-size: 14px; 179 | } 180 | 181 | 182 | 183 | 184 | 185 | 186 | table 187 | { 188 | border-collapse: collapse; 189 | margin: 0 auto; 190 | /* padding: 30px;*/ 191 | text-align: center; 192 | } 193 | table td, table th 194 | { 195 | border: 1px solid rgba(0, 0, 0, 0.13); /*边框线*/ 196 | color: rgb(102, 102, 102); /*文字颜色*/ 197 | height: 30px; 198 | } 199 | table thead th 200 | { 201 | background-color: rgb(236, 236, 236); /*字段标题背景色:序号、官职名...*/ 202 | width: 100px; 203 | } 204 | table tr:nth-child(odd) 205 | { 206 | background: rgb(236, 236, 236); /*偶数行背景色*/ 207 | } 208 | table tr:nth-child(even) 209 | { 210 | background: #ffffff; /*奇数行背景色*/ 211 | } 212 | 213 | /*分别设置每一行中单元格的宽度 214 | .item1 215 | { 216 | width: 10%; 217 | } 218 | 219 | .item2 220 | { 221 | width: 10%; 222 | } 223 | .item3 224 | { 225 | width: 50%; 226 | } 227 | .item4 228 | { 229 | width: 30%; 230 | } 231 | */ -------------------------------------------------------------------------------- /src/style/SimpleForceChart.css: -------------------------------------------------------------------------------- 1 | .main{ 2 | text-align: center; 3 | } -------------------------------------------------------------------------------- /src/style/Tables.css: -------------------------------------------------------------------------------- 1 | .ant-table { 2 | font-size: 13px; 3 | } -------------------------------------------------------------------------------- /src/utils/d3-cloud.js: -------------------------------------------------------------------------------- 1 | // Word cloud layout by Jason Davies, https://www.jasondavies.com/wordcloud/ 2 | // Algorithm due to Jonathan Feinberg, http://static.mrfeinberg.com/bv_ch03.pdf 3 | 4 | var dispatch = require("d3-dispatch").dispatch; 5 | 6 | var cloudRadians = Math.PI / 180, 7 | // eslint-disable-next-line 8 | cw = 1 << 11 >> 5, 9 | ch = 1 << 11; 10 | 11 | module.exports = function() { 12 | var size = [256, 256], 13 | text = cloudText, 14 | font = cloudFont, 15 | fontSize = cloudFontSize, 16 | fontStyle = cloudFontNormal, 17 | fontWeight = cloudFontNormal, 18 | rotate = cloudRotate, 19 | padding = cloudPadding, 20 | spiral = archimedeanSpiral, 21 | words = [], 22 | timeInterval = Infinity, 23 | event = dispatch("word", "end"), 24 | timer = null, 25 | random = Math.random, 26 | cloud = {}, 27 | canvas = cloudCanvas; 28 | 29 | cloud.canvas = function(_) { 30 | return arguments.length ? (canvas = functor(_), cloud) : canvas; 31 | }; 32 | 33 | cloud.start = function() { 34 | var contextAndRatio = getContext(canvas()), 35 | board = zeroArray((size[0] >> 5) * size[1]), 36 | bounds = null, 37 | n = words.length, 38 | i = -1, 39 | tags = [], 40 | data = words.map(function(d, i) { 41 | d.text = text.call(this, d, i); 42 | d.font = font.call(this, d, i); 43 | d.style = fontStyle.call(this, d, i); 44 | d.weight = fontWeight.call(this, d, i); 45 | d.rotate = rotate.call(this, d, i); 46 | d.size = ~~fontSize.call(this, d, i); 47 | d.padding = padding.call(this, d, i); 48 | return d; 49 | }).sort(function(a, b) { return b.size - a.size; }); 50 | 51 | if (timer) clearInterval(timer); 52 | timer = setInterval(step, 0); 53 | step(); 54 | 55 | return cloud; 56 | 57 | function step() { 58 | var start = Date.now(); 59 | while (Date.now() - start < timeInterval && ++i < n && timer) { 60 | var d = data[i]; 61 | d.x = (size[0] * (random() + .5)) >> 1; 62 | d.y = (size[1] * (random() + .5)) >> 1; 63 | cloudSprite(contextAndRatio, d, data, i); 64 | if (d.hasText && place(board, d, bounds)) { 65 | tags.push(d); 66 | event.call("word", cloud, d); 67 | if (bounds) cloudBounds(bounds, d); 68 | else bounds = [{x: d.x + d.x0, y: d.y + d.y0}, {x: d.x + d.x1, y: d.y + d.y1}]; 69 | // Temporary hack 70 | d.x -= size[0] >> 1; 71 | d.y -= size[1] >> 1; 72 | } 73 | } 74 | if (i >= n) { 75 | cloud.stop(); 76 | event.call("end", cloud, tags, bounds); 77 | } 78 | } 79 | } 80 | 81 | cloud.stop = function() { 82 | if (timer) { 83 | clearInterval(timer); 84 | timer = null; 85 | } 86 | return cloud; 87 | }; 88 | 89 | function getContext(canvas) { 90 | canvas.width = canvas.height = 1; 91 | var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2); 92 | canvas.width = (cw << 5) / ratio; 93 | canvas.height = ch / ratio; 94 | 95 | var context = canvas.getContext("2d"); 96 | context.fillStyle = context.strokeStyle = "red"; 97 | context.textAlign = "center"; 98 | 99 | return {context: context, ratio: ratio}; 100 | } 101 | 102 | function place(board, tag, bounds) { 103 | // eslint-disable-next-line 104 | var perimeter = [{x: 0, y: 0}, {x: size[0], y: size[1]}], 105 | startX = tag.x, 106 | startY = tag.y, 107 | maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]), 108 | s = spiral(size), 109 | dt = random() < .5 ? 1 : -1, 110 | t = -dt, 111 | dxdy, 112 | dx, 113 | dy; 114 | // eslint-disable-next-line 115 | while (dxdy = s(t += dt)) { 116 | dx = ~~dxdy[0]; 117 | dy = ~~dxdy[1]; 118 | 119 | if (Math.min(Math.abs(dx), Math.abs(dy)) >= maxDelta) break; 120 | 121 | tag.x = startX + dx; 122 | tag.y = startY + dy; 123 | 124 | if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || 125 | tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) continue; 126 | // TODO only check for collisions within current bounds. 127 | if (!bounds || !cloudCollide(tag, board, size[0])) { 128 | if (!bounds || collideRects(tag, bounds)) { 129 | var sprite = tag.sprite, 130 | w = tag.width >> 5, 131 | sw = size[0] >> 5, 132 | lx = tag.x - (w << 4), 133 | sx = lx & 0x7f, 134 | msx = 32 - sx, 135 | h = tag.y1 - tag.y0, 136 | x = (tag.y + tag.y0) * sw + (lx >> 5), 137 | last; 138 | for (var j = 0; j < h; j++) { 139 | last = 0; 140 | for (var i = 0; i <= w; i++) { 141 | board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0); 142 | } 143 | x += sw; 144 | } 145 | delete tag.sprite; 146 | return true; 147 | } 148 | } 149 | } 150 | return false; 151 | } 152 | 153 | cloud.timeInterval = function(_) { 154 | return arguments.length ? (timeInterval = _ == null ? Infinity : _, cloud) : timeInterval; 155 | }; 156 | 157 | cloud.words = function(_) { 158 | return arguments.length ? (words = _, cloud) : words; 159 | }; 160 | 161 | cloud.size = function(_) { 162 | return arguments.length ? (size = [+_[0], +_[1]], cloud) : size; 163 | }; 164 | 165 | cloud.font = function(_) { 166 | return arguments.length ? (font = functor(_), cloud) : font; 167 | }; 168 | 169 | cloud.fontStyle = function(_) { 170 | return arguments.length ? (fontStyle = functor(_), cloud) : fontStyle; 171 | }; 172 | 173 | cloud.fontWeight = function(_) { 174 | return arguments.length ? (fontWeight = functor(_), cloud) : fontWeight; 175 | }; 176 | 177 | cloud.rotate = function(_) { 178 | return arguments.length ? (rotate = functor(_), cloud) : rotate; 179 | }; 180 | 181 | cloud.text = function(_) { 182 | return arguments.length ? (text = functor(_), cloud) : text; 183 | }; 184 | 185 | cloud.spiral = function(_) { 186 | return arguments.length ? (spiral = spirals[_] || _, cloud) : spiral; 187 | }; 188 | 189 | cloud.fontSize = function(_) { 190 | return arguments.length ? (fontSize = functor(_), cloud) : fontSize; 191 | }; 192 | 193 | cloud.padding = function(_) { 194 | return arguments.length ? (padding = functor(_), cloud) : padding; 195 | }; 196 | 197 | cloud.random = function(_) { 198 | return arguments.length ? (random = _, cloud) : random; 199 | }; 200 | 201 | cloud.on = function() { 202 | var value = event.on.apply(event, arguments); 203 | return value === event ? cloud : value; 204 | }; 205 | 206 | return cloud; 207 | }; 208 | 209 | function cloudText(d) { 210 | return d.text; 211 | } 212 | 213 | function cloudFont() { 214 | return "serif"; 215 | } 216 | 217 | function cloudFontNormal() { 218 | return "normal"; 219 | } 220 | 221 | function cloudFontSize(d) { 222 | return Math.sqrt(d.value); 223 | } 224 | 225 | function cloudRotate() { 226 | return (~~(Math.random() * 6) - 3) * 30; 227 | } 228 | 229 | function cloudPadding() { 230 | return 1; 231 | } 232 | 233 | // Fetches a monochrome sprite bitmap for the specified text. 234 | // Load in batches for speed. 235 | function cloudSprite(contextAndRatio, d, data, di) { 236 | if (d.sprite) return; 237 | var c = contextAndRatio.context, 238 | ratio = contextAndRatio.ratio; 239 | 240 | c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio); 241 | var x = 0, 242 | y = 0, 243 | maxh = 0, 244 | n = data.length; 245 | --di; 246 | while (++di < n) { 247 | d = data[di]; 248 | c.save(); 249 | c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font; 250 | var w = c.measureText(d.text + "m").width * ratio, 251 | h = d.size << 1; 252 | if (d.rotate) { 253 | var sr = Math.sin(d.rotate * cloudRadians), 254 | cr = Math.cos(d.rotate * cloudRadians), 255 | wcr = w * cr, 256 | wsr = w * sr, 257 | hcr = h * cr, 258 | hsr = h * sr; 259 | // eslint-disable-next-line 260 | w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5; 261 | h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr)); 262 | } else { 263 | // eslint-disable-next-line 264 | w = (w + 0x1f) >> 5 << 5; 265 | } 266 | if (h > maxh) maxh = h; 267 | if (x + w >= (cw << 5)) { 268 | x = 0; 269 | y += maxh; 270 | maxh = 0; 271 | } 272 | if (y + h >= ch) break; 273 | c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio); 274 | if (d.rotate) c.rotate(d.rotate * cloudRadians); 275 | c.fillText(d.text, 0, 0); 276 | // eslint-disable-next-line 277 | if (d.padding) c.lineWidth = 2 * d.padding, c.strokeText(d.text, 0, 0); 278 | c.restore(); 279 | d.width = w; 280 | d.height = h; 281 | d.xoff = x; 282 | d.yoff = y; 283 | d.x1 = w >> 1; 284 | d.y1 = h >> 1; 285 | d.x0 = -d.x1; 286 | d.y0 = -d.y1; 287 | d.hasText = true; 288 | x += w; 289 | } 290 | var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data, 291 | sprite = []; 292 | while (--di >= 0) { 293 | d = data[di]; 294 | if (!d.hasText) continue; 295 | // eslint-disable-next-line 296 | var w = d.width, 297 | w32 = w >> 5, 298 | // eslint-disable-next-line 299 | h = d.y1 - d.y0; 300 | // Zero the buffer 301 | for (var i = 0; i < h * w32; i++) sprite[i] = 0; 302 | x = d.xoff; 303 | if (x == null) return; 304 | y = d.yoff; 305 | var seen = 0, 306 | seenRow = -1; 307 | for (var j = 0; j < h; j++) { 308 | // eslint-disable-next-line 309 | for (var i = 0; i < w; i++) { 310 | var k = w32 * j + (i >> 5), 311 | m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0; 312 | sprite[k] |= m; 313 | seen |= m; 314 | } 315 | if (seen) seenRow = j; 316 | else { 317 | d.y0++; 318 | h--; 319 | j--; 320 | y++; 321 | } 322 | } 323 | d.y1 = d.y0 + seenRow; 324 | d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32); 325 | } 326 | } 327 | 328 | // Use mask-based collision detection. 329 | function cloudCollide(tag, board, sw) { 330 | sw >>= 5; 331 | var sprite = tag.sprite, 332 | w = tag.width >> 5, 333 | lx = tag.x - (w << 4), 334 | sx = lx & 0x7f, 335 | msx = 32 - sx, 336 | h = tag.y1 - tag.y0, 337 | x = (tag.y + tag.y0) * sw + (lx >> 5), 338 | last; 339 | for (var j = 0; j < h; j++) { 340 | last = 0; 341 | for (var i = 0; i <= w; i++) { 342 | // eslint-disable-next-line 343 | if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0)) 344 | & board[x + i]) return true; 345 | } 346 | x += sw; 347 | } 348 | return false; 349 | } 350 | 351 | function cloudBounds(bounds, d) { 352 | var b0 = bounds[0], 353 | b1 = bounds[1]; 354 | if (d.x + d.x0 < b0.x) b0.x = d.x + d.x0; 355 | if (d.y + d.y0 < b0.y) b0.y = d.y + d.y0; 356 | if (d.x + d.x1 > b1.x) b1.x = d.x + d.x1; 357 | if (d.y + d.y1 > b1.y) b1.y = d.y + d.y1; 358 | } 359 | 360 | function collideRects(a, b) { 361 | return a.x + a.x1 > b[0].x && a.x + a.x0 < b[1].x && a.y + a.y1 > b[0].y && a.y + a.y0 < b[1].y; 362 | } 363 | 364 | function archimedeanSpiral(size) { 365 | var e = size[0] / size[1]; 366 | return function(t) { 367 | return [e * (t *= .1) * Math.cos(t), t * Math.sin(t)]; 368 | }; 369 | } 370 | 371 | function rectangularSpiral(size) { 372 | var dy = 4, 373 | dx = dy * size[0] / size[1], 374 | x = 0, 375 | y = 0; 376 | return function(t) { 377 | var sign = t < 0 ? -1 : 1; 378 | // See triangular numbers: T_n = n * (n + 1) / 2. 379 | switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) { 380 | case 0: x += dx; break; 381 | case 1: y += dy; break; 382 | case 2: x -= dx; break; 383 | default: y -= dy; break; 384 | } 385 | return [x, y]; 386 | }; 387 | } 388 | 389 | // TODO reuse arrays? 390 | function zeroArray(n) { 391 | var a = [], 392 | i = -1; 393 | while (++i < n) a[i] = 0; 394 | return a; 395 | } 396 | 397 | function cloudCanvas() { 398 | return document.createElement("canvas"); 399 | } 400 | 401 | function functor(d) { 402 | return typeof d === "function" ? d : function() { return d; }; 403 | } 404 | 405 | var spirals = { 406 | archimedean: archimedeanSpiral, 407 | rectangular: rectangularSpiral 408 | }; -------------------------------------------------------------------------------- /src/utils/d3-cubic-scale-radial.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if (typeof module === 'object' && module.exports) { 3 | /* eslint-disable global-require */ 4 | // CommonJS 5 | var d3 = require('d3') 6 | module.exports = factory(d3) 7 | /* eslint-enable global-require */ 8 | } else { 9 | // Browser global. 10 | // eslint-disable-next-line no-param-reassign 11 | root.d3.cubicScaleRadial = factory(root.d3) 12 | } 13 | 14 | }(this, function(d3) { 15 | function cubic(x) { 16 | return x * x * x; 17 | } 18 | 19 | function radial() { 20 | var linear = d3.scaleLinear(); 21 | 22 | function scale(x) { 23 | return Math.pow(linear(x), 1/3); 24 | } 25 | 26 | scale.domain = function(_) { 27 | return arguments.length ? (linear.domain(_), scale) : linear.domain(); 28 | }; 29 | 30 | scale.nice = function(count) { 31 | return (linear.nice(count), scale); 32 | }; 33 | 34 | scale.range = function(_) { 35 | return arguments.length ? (linear.range(_.map(cubic)), scale) : linear.range().map(scale); 36 | }; 37 | 38 | scale.ticks = linear.ticks; 39 | scale.tickFormat = linear.tickFormat; 40 | 41 | return scale; 42 | } 43 | 44 | return radial; 45 | })); -------------------------------------------------------------------------------- /src/utils/d3-quartic-scale-radial.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if (typeof module === 'object' && module.exports) { 3 | /* eslint-disable global-require */ 4 | // CommonJS 5 | var d3 = require('d3') 6 | module.exports = factory(d3) 7 | /* eslint-enable global-require */ 8 | } else { 9 | // Browser global. 10 | // eslint-disable-next-line no-param-reassign 11 | root.d3.quarticScaleRadial = factory(root.d3) 12 | } 13 | 14 | }(this, function(d3) { 15 | function quartic(x) { 16 | return x * x * x * x; 17 | } 18 | 19 | function radial() { 20 | var linear = d3.scaleLinear(); 21 | 22 | function scale(x) { 23 | return Math.pow(linear(x), 1/4); 24 | } 25 | 26 | scale.domain = function(_) { 27 | return arguments.length ? (linear.domain(_), scale) : linear.domain(); 28 | }; 29 | 30 | scale.nice = function(count) { 31 | return (linear.nice(count), scale); 32 | }; 33 | 34 | scale.range = function(_) { 35 | return arguments.length ? (linear.range(_.map(quartic)), scale) : linear.range().map(scale); 36 | }; 37 | 38 | scale.ticks = linear.ticks; 39 | scale.tickFormat = linear.tickFormat; 40 | 41 | return scale; 42 | } 43 | 44 | return radial; 45 | })); -------------------------------------------------------------------------------- /src/utils/d3-scale-radial.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if (typeof module === 'object' && module.exports) { 3 | /* eslint-disable global-require */ 4 | // CommonJS 5 | var d3 = require('d3') 6 | module.exports = factory(d3) 7 | /* eslint-enable global-require */ 8 | } else { 9 | // Browser global. 10 | // eslint-disable-next-line no-param-reassign 11 | root.d3.scaleRadial = factory(root.d3) 12 | } 13 | 14 | }(this, function(d3) { 15 | function square(x) { 16 | return x * x; 17 | } 18 | 19 | function radial() { 20 | var linear = d3.scaleLinear(); 21 | 22 | function scale(x) { 23 | return Math.sqrt(linear(x)); 24 | } 25 | 26 | scale.domain = function(_) { 27 | return arguments.length ? (linear.domain(_), scale) : linear.domain(); 28 | }; 29 | 30 | scale.nice = function(count) { 31 | return (linear.nice(count), scale); 32 | }; 33 | 34 | scale.range = function(_) { 35 | return arguments.length ? (linear.range(_.map(square)), scale) : linear.range().map(Math.sqrt); 36 | }; 37 | 38 | scale.ticks = linear.ticks; 39 | scale.tickFormat = linear.tickFormat; 40 | 41 | return scale; 42 | } 43 | 44 | return radial; 45 | })); -------------------------------------------------------------------------------- /src/utils/envconfig.js: -------------------------------------------------------------------------------- 1 | /* 2 | 全局配置文件 3 | */ 4 | let baseURL; 5 | let imgUrl = '//cangdu.org:8100/img/' 6 | if (process.env.NODE_ENV=='development'){ 7 | baseURL = 'http://localhost:3000' 8 | }else { 9 | baseURL = 'http://localhost:3000' 10 | } 11 | export default {imgUrl,baseURL} -------------------------------------------------------------------------------- /src/utils/tools.js: -------------------------------------------------------------------------------- 1 | export const getQueryString = (str, name) => { 2 | const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); 3 | const r = str.match(reg); 4 | return r ? unescape(r[2]) : null; 5 | }; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const glob = require('glob'); 3 | const path = require('path'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 7 | 8 | const webpackConfig = { 9 | entry: { 10 | app:'/src/app.jsx', 11 | vendor: [ 12 | 'react', 13 | 'antd', 14 | 'axios', 15 | 'react-dom', 16 | 'react-router', 17 | 'jquery' 18 | ] 19 | }, 20 | output:{ 21 | // path:path.resolve(__dirname, './dist/'), 22 | // path:path.resolve('C:/wamp64/www/path/'), 23 | // filename:'[name].[chunkhash:6].js' 24 | path:'/dist', //打包后的文件存放的地方 25 | filename:'bundle.js', 26 | publicPath:'http://localhost:8080/dist/' 27 | }, 28 | optimization: { 29 | splitChunks: { 30 | chunks: 'all' 31 | } 32 | }, 33 | mode: 'production', 34 | module:{ 35 | rules:[ 36 | {oneof : [{ 37 | test:/\.js?$/, 38 | exclude:/node_modules/, 39 | loader:'babel-loader', 40 | query:{ 41 | presets:['es2015','react'] 42 | } 43 | }, 44 | { 45 | test: /\.(scss|sass|css)$/, 46 | loader: ExtractTextPlugin.extract({fallback: "style-loader", use: "css-loader"}) 47 | }, 48 | { 49 | test: require.resolve('jquery'), 50 | use: [{ 51 | loader: 'expose-loader', 52 | options: 'jQuery' 53 | },{ 54 | loader: 'expose-loader', 55 | options: '$' 56 | }] 57 | }]} 58 | ] 59 | }, 60 | plugins: [ 61 | new ExtractTextPlugin("[name].[chunkhash:6].css"), 62 | new CleanWebpackPlugin( 63 | ['path'], 64 | { 65 | root: 'C:/wamp64/www/', 66 | verbose: true, 67 | dry: false 68 | } 69 | ), 70 | new webpack.optimize.UglifyJsPlugin(), 71 | new webpack.optimize.CommonsChunkPlugin({ 72 | name:'vendor', 73 | filename:'vendor.js' 74 | }) 75 | ], 76 | }; 77 | 78 | 79 | // 获取指定路径下的入口文件 80 | function getEntries(globPath) { 81 | const files = glob.sync(globPath), 82 | entries = {}; 83 | console.log(files) 84 | files.forEach(function(filepath) { 85 | const split = filepath.split('/'); 86 | const name = split[split.length - 2]; 87 | entries[name] = './' + filepath; 88 | }); 89 | return entries; 90 | } 91 | 92 | const entries = getEntries('src/**/index.js'); 93 | 94 | Object.keys(entries).forEach(function(name) { 95 | webpackConfig.entry[name] = entries[name]; 96 | const plugin = new HtmlWebpackPlugin({ 97 | filename: name + '.html', 98 | template: './public/index.html', 99 | inject: true, 100 | chunks: [name] 101 | }); 102 | webpackConfig.plugins.push(plugin); 103 | }) 104 | 105 | module.exports = webpackConfig; --------------------------------------------------------------------------------