diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 23c00cfd..4684cd36 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -104,6 +104,7 @@ module.exports = { src: path.join(paths.appSrc), // 整个源代码目录 forge: path.join(paths.appSrc, 'forge'), military: path.join(paths.appSrc, 'military'), + components: path.join(paths.appSrc, 'components'), // Support React Native Web // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ "react-native": "react-native-web", diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 9f73041c..55730143 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -19,7 +19,9 @@ const getClientEnvironment = require("./env"); let publicPath = "/react/build/"; const publicUrl = publicPath.slice(0, -1); -const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false"; +// const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false"; +const shouldUseSourceMap = process.env.NODE_ENV !== "production"; + const env = getClientEnvironment(publicPath); // This is the production configuration. @@ -54,7 +56,8 @@ module.exports = { }, bail: true, mode: "production", - devtool: false, //测试版 + // devtool: false, //测试版 + devtool: shouldUseSourceMap?'source-map':false, entry: [require.resolve("./polyfills"), paths.appIndexJs], output: { path: paths.appBuild, @@ -90,6 +93,7 @@ module.exports = { src: path.join(paths.appSrc), forge: path.join(paths.appSrc, 'forge'), military: path.join(paths.appSrc, 'military'), + components: path.join(paths.appSrc, 'components'), // Support React Native Web // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ "react-native": "react-native-web", diff --git a/jsconfig.json b/jsconfig.json index 9c99023e..c9c1201c 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -5,6 +5,7 @@ "educoder": ["./src/common/educoder.js"], "forge":["./src/forge"], "military":["./src/military"], + "components":["./src/components"], } }, "exclude": [ diff --git a/package-lock.json b/package-lock.json index 9e0f6e4f..bd282b72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -265,17 +265,17 @@ } }, "@babel/runtime-corejs3": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.16.0.tgz", - "integrity": "sha512-Oi2qwQ21X7/d9gn3WiwkDTJmq3TQtYNz89lRnoFy8VeZpWlsyXvzSwiRrRZ8cXluvSwqKxqHJ6dBd9Rv+p0ZGQ==", + "version": "7.17.2", + "resolved": "http://173.15.15.82:8081/repository/npm-all/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz", + "integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==", "requires": { - "core-js-pure": "^3.19.0", + "core-js-pure": "^3.20.2", "regenerator-runtime": "^0.13.4" }, "dependencies": { "regenerator-runtime": { "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "resolved": "http://173.15.15.82:8081/repository/npm-all/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" } } @@ -3969,9 +3969,9 @@ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" }, "core-js-pure": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.19.1.tgz", - "integrity": "sha512-Q0Knr8Es84vtv62ei6/6jXH/7izKmOrtrxH9WJTHLCMAVeU+8TF8z8Nr08CsH4Ot0oJKzBzJJL9SJBYIv7WlfQ==" + "version": "3.21.1", + "resolved": "http://173.15.15.82:8081/repository/npm-all/core-js-pure/-/core-js-pure-3.21.1.tgz", + "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==" }, "core-util-is": { "version": "1.0.2", @@ -19402,9 +19402,9 @@ } }, "wangeditor": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/wangeditor/-/wangeditor-4.7.7.tgz", - "integrity": "sha512-eUpgP7i4pCYIjxjGMMcSUtkHUCkdX7BBwLZE6umseoK9hs3qIMu1tQ95FG48NJwL71JyAjt5c1gB56PzducL3Q==", + "version": "4.7.12", + "resolved": "http://173.15.15.82:8081/repository/npm-all/wangeditor/-/wangeditor-4.7.12.tgz", + "integrity": "sha512-5KOIpQ0+idKvDTkjZOp7RHYXA97FyJ9mjwy+zQUdMUCqZItlEXzvVOYtOlHkJr/HcbwgIz/7f/trGFggZK5X4g==", "requires": { "@babel/runtime": "^7.11.2", "@babel/runtime-corejs3": "^7.11.2", @@ -19412,38 +19412,26 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.15.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz", - "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==", + "version": "7.17.2", + "resolved": "http://173.15.15.82:8081/repository/npm-all/@babel/runtime/-/runtime-7.17.2.tgz", + "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", "requires": { "regenerator-runtime": "^0.13.4" } }, "regenerator-runtime": { "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "resolved": "http://173.15.15.82:8081/repository/npm-all/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" } } }, "wangeditor-for-react": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/wangeditor-for-react/-/wangeditor-for-react-1.5.3.tgz", - "integrity": "sha512-nnfZW6leWrVP5UFLEl93UmquSxgfkMpRM45nRusuA/QA+OPIKh0+JW4ZSmSjpmolhT8//iyQOry1I5EuVFwgog==", + "version": "1.5.6", + "resolved": "http://173.15.15.82:8081/repository/npm-all/wangeditor-for-react/-/wangeditor-for-react-1.5.6.tgz", + "integrity": "sha512-FLxU4K6PXc9AS3SFfU7vZhUR+EpUEWUVraFPRUiSH9VSzt9jLO9fp4IyReT7ebbp/YaMKXHhN5JLu2v2/zvNrA==", "requires": { - "react": "^17.0.2", - "wangeditor": "^4.7.5" - }, - "dependencies": { - "react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - } + "wangeditor": "^4.7.12" } }, "warning": { diff --git a/package.json b/package.json index 93615bff..e3f0de12 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "styled-components": "^4.4.1", "sw-precache-webpack-plugin": "0.11.4", "url-loader": "0.6.2", - "wangeditor-for-react": "^1.4.0", + "wangeditor-for-react": "^1.5.6", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.10.3", "webpack-manifest-plugin": "^2.2.0", diff --git a/public/css/iconfont.css b/public/css/iconfont.css index c537eb7d..eb29acda 100644 --- a/public/css/iconfont.css +++ b/public/css/iconfont.css @@ -1,14 +1,9 @@ @font-face { font-family: "iconfont"; /* Project id 2340181 */ -<<<<<<< HEAD - src: url('iconfont.woff2?t=1644907653340') format('woff2'), - url('iconfont.woff?t=1644907653340') format('woff'), - url('iconfont.ttf?t=1644907653340') format('truetype'); -======= src: url('iconfont.woff2?t=1648801178933') format('woff2'), url('iconfont.woff?t=1648801178933') format('woff'), url('iconfont.ttf?t=1648801178933') format('truetype'); ->>>>>>> a58c7403 (修改样式) + } .iconfont { @@ -19,8 +14,6 @@ -moz-osx-font-smoothing: grayscale; } -<<<<<<< HEAD -======= .icon-kaiyuanxiangmu:before { content: "\e91d"; } @@ -93,7 +86,6 @@ content: "\e915"; } ->>>>>>> a58c7403 (修改样式) .icon-icon2:before { content: "\e912"; } diff --git a/public/css/iconfont.js b/public/css/iconfont.js index 18a3dd9f..e89ab836 100644 --- a/public/css/iconfont.js +++ b/public/css/iconfont.js @@ -1,5 +1 @@ -<<<<<<< HEAD -!function(a){var c,l,h,i,o,z='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),v=function(a,c){c.parentNode.insertBefore(a,c)};if(t&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function p(){o||(o=!0,h())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}p()}c=function(){var a,c=document.createElement("div");c.innerHTML=z,z=null,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?v(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()},document.addEventListener("DOMContentLoaded",l,!1)):document.attachEvent&&(h=c,i=a.document,o=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,p())})}(window); -======= -!function(a){var l,c,h,i,o,z='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),p=function(a,l){l.parentNode.insertBefore(a,l)};if(t&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function v(){o||(o=!0,h())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}v()}l=function(){var a,l=document.createElement("div");l.innerHTML=z,z=null,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?p(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(c=function(){document.removeEventListener("DOMContentLoaded",c,!1),l()},document.addEventListener("DOMContentLoaded",c,!1)):document.attachEvent&&(h=l,i=a.document,o=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,v())})}(window); ->>>>>>> a58c7403 (修改样式) +!function(a){var c,l,h,i,o,z='',t=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss"),p=function(a,c){c.parentNode.insertBefore(a,c)};if(t&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function v(){o||(o=!0,h())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}v()}c=function(){var a,c;(c=document.createElement("div")).innerHTML=z,z=null,(a=c.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",c=a,(a=document.body).firstChild?p(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()},document.addEventListener("DOMContentLoaded",l,!1)):document.attachEvent&&(h=c,i=a.document,o=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,v())})}(window); \ No newline at end of file diff --git a/public/css/iconfont.json b/public/css/iconfont.json index b7da8b73..e3f90620 100644 --- a/public/css/iconfont.json +++ b/public/css/iconfont.json @@ -6,8 +6,6 @@ "description": "", "glyphs": [ { -<<<<<<< HEAD -======= "icon_id": "28163416", "name": "开源项目", "font_class": "kaiyuanxiangmu", @@ -134,7 +132,7 @@ "unicode_decimal": 59669 }, { ->>>>>>> a58c7403 (修改样式) + "icon_id": "27299393", "name": "icon", "font_class": "icon2", diff --git a/public/css/iconfont.ttf b/public/css/iconfont.ttf index 2505060c..6b7f63cb 100644 Binary files a/public/css/iconfont.ttf and b/public/css/iconfont.ttf differ diff --git a/public/css/iconfont.woff b/public/css/iconfont.woff index 561a134c..d5114e21 100644 Binary files a/public/css/iconfont.woff and b/public/css/iconfont.woff differ diff --git a/public/css/iconfont.woff2 b/public/css/iconfont.woff2 index 494b00b3..374e629b 100644 Binary files a/public/css/iconfont.woff2 and b/public/css/iconfont.woff2 differ diff --git a/src/App.js b/src/App.js index 63b736b7..d243bfd6 100644 --- a/src/App.js +++ b/src/App.js @@ -137,7 +137,7 @@ const AboutUs = Loadable({ // 管理页面 const Managements = Loadable({ - loader: () => import('./military/managements'), + loader: () => import('./managements/index'), loading: Loading, }) // const CreateMerge = Loadable({ @@ -146,7 +146,7 @@ const Managements = Loadable({ // }) // 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。 -const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder","task","notice","managements","expert","competition"]; +const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder","task","notice","managements","expert","competition","administration"]; class App extends Component { constructor(props) { @@ -405,8 +405,12 @@ class App extends Component { path="/register" render={(props) =>} > - {/*管理页面*/} + + {/*管理页面 三个路由,分别对应本环境管理、原测试ruby模板管理、原项目管理*/} + + + {/*403*/} diff --git a/src/components/modal-fun/index.jsx b/src/components/modal-fun/index.jsx new file mode 100644 index 00000000..8fee2c73 --- /dev/null +++ b/src/components/modal-fun/index.jsx @@ -0,0 +1,125 @@ +/* eslint-disable react/jsx-no-duplicate-props */ +import React, { useState } from 'react'; +import * as ReactDOM from 'react-dom'; +import { Modal, Button } from 'antd'; +import './index.scss'; + +// 函数式调用删除、通知等模态框 + +InitModal.defaultProps = { + okText: '确认', //确定按钮的文字 + cancelText: '取消', //取消按钮的文字 + className: '', //传入的模态框类名 + inputId: 'copyText', //要复制的文本的ID + onCancel:()=>{}, //取消的回调 + onOk:()=>{}, //确认的回调 + title:'提示', //模态框名字 + contentTitle:'', //内容标题 + content:'', //详细内容 + afterClose:()=>{}, //关闭模态框以后的回调 +}; + +// 使用函数调用删除组件 +export default function DelModal(props) { + renderModal({ ...props, type: 'delete' }) +} + +// 使用函数调用选择模态框组件 +export function Confirm(props) { + renderModal({ ...props, type: 'confirm' }) +} + +function renderModal(props) { + const { type, afterClose } = props; + const div = document.createElement('div'); + document.body.appendChild(div); + + function destroy() { + afterClose && afterClose(); + const unmountResult = ReactDOM.unmountComponentAtNode(div); + if (unmountResult && div.parentNode) { + div.parentNode.removeChild(div); + } + } + + function modalType(type) { + if (type === 'delete') { + return + + {props.contentTitle} + } + /> + } else if (type === 'confirm') { + return + } else { + return + } + } + + function render() { + setTimeout(() => { + ReactDOM.render( + modalType(type), + div, + ); + }); + } + render(); +} + +// 选择模态框组件 +function InitModal({ + onCancel, + onOk, + title, + contentTitle, + content, + okText, + cancelText, + afterClose, + className, +}) { + + const [visible, setVisible] = useState(true); + + function onCancelModal() { + setVisible(false); + onCancel && onCancel() + } + + function onSuccess() { + setVisible(false); + onOk && onOk(); + } + + return ( + + {cancelText} + , + , + ]} + > +
+ {contentTitle &&

{contentTitle}

} +

{content}

+
+
+ ) +} diff --git a/src/components/modal-fun/index.scss b/src/components/modal-fun/index.scss new file mode 100644 index 00000000..24f7b021 --- /dev/null +++ b/src/components/modal-fun/index.scss @@ -0,0 +1,63 @@ +.myself-modal { + .ant-modal-header { + padding: 9px 24px; + background: #f8f8f8; + border-bottom: 1px solid #eee; + } + .ant-modal-title { + text-align: left; + } + .ant-modal-close { + top: 0px !important; + } + .ant-modal-close-x { + font-size: 24px; + } + .ant-modal-body { + text-align: center; + } + .content-title { + display: flex; + justify-content: center; + align-items: center; + margin: 2rem 0 1rem !important; + font-size: 16px; + color: #333; + letter-spacing: 0; + line-height: 29px; + font-weight: 400; + } + .red-circle { + align-self: flex-start; + color: #ca0002; + font-size: 1.5rem !important; + } + .content-descibe { + font-size: 14px; + color: #666; + line-height: 33px; + font-weight: 400; + } + .ant-modal-footer { + padding: 2rem 0; + text-align: center; + border: 0; + .ant-btn { + width: 6rem; + } + } + .foot-submit { + margin-left: 3rem; + color: #df0002; + &:hover { + border-color: #df0002; + } + } + .ant-btn-default:hover, + .ant-btn-default:active, + .ant-btn-default:focus { + background: #f3f4f6; + color: #333; + border-color: #d0d0d0; + } +} \ No newline at end of file diff --git a/src/components/modal/index.jsx b/src/components/modal/index.jsx new file mode 100644 index 00000000..5b2a7a1a --- /dev/null +++ b/src/components/modal/index.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Modal , Button } from 'antd'; +import './index.scss'; + +function Modals({visible,title,content,onOk,onCancel}){ + return( + + + + + } + > +
{content}
+
+ ) +} +export default Modals; diff --git a/src/components/modal/index.scss b/src/components/modal/index.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/components/pagination-table/index.jsx b/src/components/pagination-table/index.jsx new file mode 100644 index 00000000..91647e0b --- /dev/null +++ b/src/components/pagination-table/index.jsx @@ -0,0 +1,27 @@ +import React, { Fragment } from 'react'; +import { Table, Pagination } from 'antd'; +import './index.scss'; + +export default (props) => { + const {handleRow, total, setCurPage, current, onShowSizeChange, showSizeChanger, pagination} = props; + + return ( +
+ + {total > 10 && (!pagination) && + } + + + ) +} diff --git a/src/components/pagination-table/index.scss b/src/components/pagination-table/index.scss new file mode 100644 index 00000000..64edaae7 --- /dev/null +++ b/src/components/pagination-table/index.scss @@ -0,0 +1,38 @@ +.pagination-table { + margin-top: 1rem; + padding: 1.5rem; + background-color: #ffffff; + box-shadow:0px 0px 10px rgba(88, 116, 255, 0.18); + .ant-table { + border-radius: 0 0 4px 4px; + tr > td{ + border-bottom: none; + border-top: 1px solid #e2e6f5; + } + tr:first-child > td{border-top: none;} + tr:last-child > td{border-bottom: 1px solid #e2e6f5;} + .ant-table-expanded-row td{border-top: none;} + } + .ant-table-thead tr th div { + color: #181818; + font-weight: 500; + } + .ant-table-wrapper { + ::-webkit-scrollbar { + width: 5px; + height: 5px; + } + .ant-table-hide-scrollbar { + margin-bottom: -5px; + overflow-y: hidden !important; + ::-webkit-scrollbar { + width: 5px; + height: 5px; + } + } + } + .ant-pagination { + margin: 2rem 1.5rem; + text-align: right; + } +} diff --git a/src/forge/Settings/SpecialProject.jsx b/src/forge/Settings/SpecialProject.jsx index 883b5d0a..ab7bf348 100644 --- a/src/forge/Settings/SpecialProject.jsx +++ b/src/forge/Settings/SpecialProject.jsx @@ -8,7 +8,7 @@ const { Search } = Input; const LIMIT = 15; function SpecialProject(props){ const [ page , setPage] = useState(1); - const [ searchValue , SetSearchValue ] = useState(undefined); + const [ searchValue , setSearchValue ] = useState(undefined); const [ total , setTotal ] = useState(0); const [ list , setList ] = useState(undefined); const [ status , setStatus ] = useState(undefined); @@ -22,7 +22,7 @@ function SpecialProject(props){ setLoading(true); Init(searchValue, status); } - },[page,project_id]); + },[page, project_id, searchValue]); function Init(search,status){ const url = `/apply_signatures.json`; @@ -125,9 +125,9 @@ function SpecialProject(props){ }).catch(error=>{setLoading(false)}) } - function searchList(){ + function searchList(value){ + setSearchValue(value); setLoading(true); - Init(searchValue,status); } const menu=( @@ -155,9 +155,7 @@ function SpecialProject(props){ enterButton="搜索" style={{width:400}} size="middle" - value={searchValue} - onChange={(e)=>SetSearchValue(e.target.value)} - onSearch={searchList} + onSearch={(value)=>searchList(value)} /> diff --git a/src/home/index.scss b/src/home/index.scss index 2a1b113c..ef329f78 100644 --- a/src/home/index.scss +++ b/src/home/index.scss @@ -106,52 +106,6 @@ line-height: 1.6875em; opacity: 0.8; } - - // 首页特定头部样式起 - - &.newHeaders { - // background-color: #1a2358; - background-color: #0037af; - z-index: 1000; - } - .newFooter{ - background: inherit; - } - .head-nav { - min-width: auto; - } - .head-nav ul#header-nav li a { - font-size: 1em; - color: #fff; - } - .head-nav ul#header-nav li:hover a, - .head-nav ul#header-nav li.active a { - color: #ffa13a; - } - .head-nav ul#header-nav li.active a::after { - background-color: #ffa13a; - } - - .logoDiv img { - min-width: 2.8125em; - height: 2.8125em; - } - - .color-home { - color: #fff; - } - - .color-home:hover { - color: #4154f1; - } - - .register-button { - height: 3em; - margin-left: 1.25em; - padding: 0 1.5625em; - font-size: 0.875em; - } - // 首页特定头部样式止 } // 波浪样式起 diff --git a/src/managements/api.js b/src/managements/api.js new file mode 100644 index 00000000..a40c4fb5 --- /dev/null +++ b/src/managements/api.js @@ -0,0 +1,138 @@ +import fetch,{main_web_site_url} from './fetch'; +import { notification } from 'antd'; + +// 新增后台菜单 +export async function createMenu(data){ + let res = await fetch({ + url: '/api/menu/create', + method: 'post', + data, + }); + return res; +} +//删除后台菜单 +export async function deleteMenu(menuId){ + let res = await fetch({ + url: `/api/menu/delete/${menuId}`, + method: 'post', + }); + return res; +} +//修改菜单状态 +export async function updateMenuStatus(menuId, hidden){ + let res = await fetch({ + url: `/api/menu/updateHidden/${menuId}?hidden=${hidden}`, + method: 'post', + }); + return res; +} +//修改菜单信息 +export async function updateMenu(menuId, data){ + let res = await fetch({ + url: `/api/menu/update/${menuId}`, + method: 'post', + data, + }); + return res; +} +// 分页查询后台菜单 +export async function menuList(parentId,params) { + let res = await fetch({ + url: `/api/menu/list/${parentId}`, + method: 'get', + params, + }); + return res; +} +// 树形结构返回所有菜单列表 +export async function treeList() { + let res = await fetch({ + url: `/api/menu/treeList`, + method: 'get', + }); + return res; +} + +//添加角色 +export async function createRole(data){ + let res = await fetch({ + url: '/api/role/create', + method: 'post', + data, + }); + return res; +} +//获取所有角色 +export async function roleList() { + let res = await fetch({ + url: `/api/role/listAll`, + method: 'get', + }); + return res; +} +//修改角色信息 +export async function updateRole(roleId, data){ + let res = await fetch({ + url: `/api/role/update/${roleId}`, + method: 'post', + data, + }); + return res; +} +//修改角色状态 +export async function updateRoleStatus(roleId, status){ + let res = await fetch({ + url: `/api/role/updateStatus/${roleId}?status=${status}`, + method: 'post', + }); + return res; +} +//删除角色 +export async function deleteRole(roleId){ + let res = await fetch({ + url: `/api/role/delete?ids=${roleId}`, + method: 'post', + }); + return res; +} + +//为角色赋予菜单 +export async function allocMenu(roleId, menuIds){ + let res = await fetch({ + url: `/api/role/allocMenu?roleId=${roleId}&menuIds=${menuIds}`, + method: 'post', + }); + return res; +} +//通过角色id获取菜单列表 +export async function listMenu(roleId) { + let res = await fetch({ + url: `/api/role/listMenu/${roleId}`, + method: 'get', + }); + return res; +} +//修改用户角色 +export async function updateUserRole(userId, ruleId){ + let res = await fetch({ + url: `/api/admin/role/update/?adminId=${userId}&roleIds=${ruleId}`, + method: 'post', + }); + return res; +} +//获取所有管理员用户 +export async function adminUserList() { + let res = await fetch({ + url: `/api/admin/list`, + method: 'get', + }); + return res; +} +// 获取用户角色 +export async function getUserRole(adminId) { + let res = await fetch({ + url: `/api/admin/role/${adminId}`, + method: 'get', + }); + return res; +} \ No newline at end of file diff --git a/src/managements/common/static.js b/src/managements/common/static.js new file mode 100644 index 00000000..2c056a90 --- /dev/null +++ b/src/managements/common/static.js @@ -0,0 +1,22 @@ +// 公告开始 +export const noticeStatus = [ + { code: 0, name: "关闭" }, + { code: 1, name: "正常" }, + { code: 2, name: "草稿" }, +]; + +export const noticeType = [ + { code: 4, name: "招标" }, + { code: 1, name: "更正" }, + { code: 2, name: "中标" }, + { code: 3, name: "废标" }, + { code: 5, name: "技术资产" }, + { code: 6, name: "成交" }, +]; + +export const noticeChecked = [ + { code: 0, name: "未通过" }, + { code: 1, name: "已发布" }, + { code: 2, name: "待审核" }, +]; +//公告结束 \ No newline at end of file diff --git a/src/managements/common/utils.js b/src/managements/common/utils.js new file mode 100644 index 00000000..c1cda7a4 --- /dev/null +++ b/src/managements/common/utils.js @@ -0,0 +1,88 @@ +/** + * 生成唯一字符串 + */ +export function uuid() { + const s = []; + const hexDigits = '0123456789abcdef'; + for (let i = 0; i < 36; i += 1) { + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + } + s[14] = '4'; + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); + s[8] = '-'; + s[13] = '-'; + s[18] = '-'; + s[23] = '-'; + s[0] = 'abcdefghigklmnopqrst'.substr(Math.floor(Math.random() * 0x10), 1); + return s.join(''); +} + +// 获取当前日期前N天数组 +// format='MM-dd' +export function beforeDayArr(length = 31, myDate = new Date(), flag = 1) { + myDate.setDate(myDate.getDate()); + let dateArray = []; + let dateTemp; + for (let i = length; i > 0; i--) { + dateTemp = (myDate.getMonth() + 1) + "-" + myDate.getDate(); + dateArray.unshift(dateTemp); + myDate.setDate(myDate.getDate() - flag); + } + return dateArray; +} + +/** + * param拼接到url地址上,get请求 + * @param {*} url + * @param {*} params + * @param {*} prefix + */ +export const paramToUrl = (url, params) => { + if (url.indexOf('?') == -1) { + // url += '?r=' + Math.random(); + url += '?'; + } + let time = 0; + for (let attr in params) { + if (!time) { + url += attr + '=' + params[attr] || ''; + time++; + } else { + url += '&' + attr + '=' + params[attr] || ''; + } + } + return url; +} + + +const isDev = window.location.port == 8000; +// const isdev2 = window.location.hostname === 'www.educoder.net' +export const TEST_HOST = "https://testforgeplus.trustie.net/" +export function getImageUrl(path) { + const local = 'https://testforgeplus.trustie.net'; + if (isDev) { + return `${local}/${path}` + } + return `https://forgeplus.trustie.net${path}`; +} + + +export const uploadNameSizeSeperator = '  ' + +export function appendFileSizeToUploadFileAll(fileList) { + return fileList && fileList.map(item => { + if (item.name&&item.name.indexOf(uploadNameSizeSeperator) == -1) { + return Object.assign({}, item, { name: `${item.name}${uploadNameSizeSeperator}${bytesToSize(item.size)}` }) + } + return item; + }) +} + + +export function bytesToSize(bytes) { + var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + if (bytes == 0) return '0 Byte'; + if (!bytes) return ''; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + return parseFloat(bytes / Math.pow(1024, i), 2).toFixed(1) + ' ' + sizes[i]; +} \ No newline at end of file diff --git a/src/managements/competition/setting/index.jsx b/src/managements/competition/setting/index.jsx index c611588f..5081b497 100644 --- a/src/managements/competition/setting/index.jsx +++ b/src/managements/competition/setting/index.jsx @@ -21,6 +21,9 @@ export default Form.create()(({ form, match, history, current_user, showNotifica const [plainOptions, setPlainOptions] = useState([]); const [admin_data, setAdmin_data] = useState([]); + const [contentMd,setContentMd]=useState(false); + const [guideMd,setGuideMd]=useState(false); + let tableRef = useRef(); useEffect(() => { @@ -49,27 +52,29 @@ export default Form.create()(({ form, match, history, current_user, showNotifica // manager_ids: details.manager_ids, }; setFieldsValue(initValue); + setContent(details.content); + setGuide(details.guide); + setAbout_us(details.about_us); setTimeout(() => { - setContent(details.content); + setContentMd(true); }, 200); setTimeout(() => { - setGuide(details.guide); - }, 800); + setGuideMd(true); + }, 300); setTimeout(() => { - setAbout_us(details.about_us); document.querySelector('#title').focus(); - }, 1400); + setLoading(false); + }, 600); if (details.is_local) { zonesArr(details.zones_local); } else { zonesArr(details.zones); } setAdmin_data(Array.isArray(details.admin_data) ? details.admin_data : []); + }else{ + setLoading(false); } - setLoading(false); }).catch((error) => { - }).finally(() => { - setLoading(false); }) } }, [id]) @@ -252,7 +257,6 @@ export default Form.create()(({ form, match, history, current_user, showNotifica - {helper('赛项', 'sub_competitions', [{ required: true, message: "请填写赛项" }], @@ -315,26 +319,26 @@ export default Form.create()(({ form, match, history, current_user, showNotifica - { onContentChange(value, 'content') }} - /> + />} {getFieldDecorator("content", { rules: [{ required: true, message: "请输入大赛介绍" }], })()} - { onContentChange(value, 'guide') }} - /> + />} {getFieldDecorator("guide", { rules: [{ required: true, message: "请输入赛事指南" }], })()} diff --git a/src/managements/components/DelModal.jsx b/src/managements/components/DelModal.jsx new file mode 100644 index 00000000..b2ba4434 --- /dev/null +++ b/src/managements/components/DelModal.jsx @@ -0,0 +1,20 @@ +import { Modal } from 'antd'; + +export default ( + handleOk, + title, + content, + handleCancel) => { + return Modal.confirm({ + title: title || "警告", + content: content || "确认删除?", + okText: '确定', + cancelText: '取消', + onOk() { + handleOk && handleOk(); + }, + onCancel() { + handleCancel && handleCancel(); + } + }); +} \ No newline at end of file diff --git a/src/managements/components/EchartBar/index.jsx b/src/managements/components/EchartBar/index.jsx new file mode 100644 index 00000000..0f91c59e --- /dev/null +++ b/src/managements/components/EchartBar/index.jsx @@ -0,0 +1,152 @@ +import React, { useEffect } from 'react'; +import * as echarts from 'echarts'; + +let fontSizeText = 16; +let clientWidth = document.body.clientWidth; +if (clientWidth < 1000) { + fontSizeText = 16 +} else if (clientWidth >= 1000 && clientWidth <= 2000) { + fontSizeText = 14 + (clientWidth - 1000) / 500; +} else if (clientWidth > 2000) { + fontSizeText = 16 + (clientWidth - 2000) / 500; +} else if (clientWidth > 3000) { + fontSizeText = 18 + (clientWidth - 3000) / 2000; +} +let fontSizeTitle = 1.25 * fontSizeText; + + +export default ({ id,className, title, xData, yData }) => { + + useEffect(() => { + let newEchartBar = document.getElementById(id) && echarts.init(document.getElementById(id)); + let textColor = "#7988a5"; + let normalColor = "#e8e8ed"; + + let option = { + grid: { + left: "3%", + top: "15%", + right: "8%", + bottom: 0, + containLabel: true + }, + title: { + text: title, + fontSize: fontSizeTitle, + position: 'inside', + textStyle: { + color: textColor + } + }, + tooltip: { + trigger: "axis", + axisPointer: { + type: "shadow", + textStyle: { + color: "#fff" + } + }, + }, + xAxis: [{ + type: "category", + data: xData, + axisPointer: { + type: "shadow" + }, + axisLabel: { + textStyle: { + color: textColor, + fontSize: .75 * fontSizeText + }, + interval: 0, + rotate: 40 + }, + axisLine: { + lineStyle: { + color: normalColor + } + }, + axisTick: { + show: false + }, + splitLine: { + show: false + } + }], + yAxis: [{ + type: "value", + minInterval: 1, + nameTextStyle: { + color: textColor, + fontSize: fontSizeText + }, + axisLabel: { + formatter: "{value}", + textStyle: { + color: textColor, + fontSize: fontSizeText + } + }, + axisLine: { + lineStyle: { + color: normalColor + } + }, + axisTick: { + show: false + }, + splitLine: { + lineStyle: { + color: normalColor + } + }, + }], + series: [{ + type: "bar", + data: yData, + barWidth: "60%", + itemStyle: { + normal: { + color: { + type: "linear", + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, + color: "#deeefe" + }, + { + offset: 1, + color: "#2c8bff" + } + ], + globalCoord: false + } + } + }, + label: { + show: true, + // position: ['-20%', -1.2 * fontSizeText], + position: "top", + // color: "#2c8bff", + color: '#555', + fontSize: fontSizeText, + formatter: function (params) { + // var percent = 0; + // percent = ((params.value / allStaff.num) * 100).toFixed(0); + return params.value || '' + // + '\n' + percent + '%'; + }, + } + }] + }; + newEchartBar && newEchartBar.setOption(option); + }, [id, title, xData, yData]) + + return ( +
+
+ ) +} \ No newline at end of file diff --git a/src/managements/components/EchartLine/index.jsx b/src/managements/components/EchartLine/index.jsx new file mode 100644 index 00000000..4b5d1349 --- /dev/null +++ b/src/managements/components/EchartLine/index.jsx @@ -0,0 +1,198 @@ +import React, { useEffect, useState } from 'react'; +import * as echarts from 'echarts'; + + +let fontSizeText = 16; +let clientWidth = document.body.clientWidth; +if (clientWidth < 1000) { + fontSizeText = 16 +} else if (clientWidth >= 1000 && clientWidth <= 2000) { + fontSizeText = 14 + (clientWidth - 1000) / 500; +} else if (clientWidth > 2000) { + fontSizeText = 16 + (clientWidth - 2000) / 500; +} else if (clientWidth > 3000) { + fontSizeText = 18 + (clientWidth - 3000) / 2000; +} + +const colorList = ["#27e8b4", '#77b4fd', '#ffd55f', '#ac90ef', '#9E87FF']; + +export default ({ id = "uid", className, title, xData, seriesArr }) => { + + useEffect(() => { + + let newEchartBar = document.getElementById(id) && echarts.init(document.getElementById(id)); + let option = { + backgroundColor: '#fff', + legend: { + icon: 'circle', + top: '5%', + itemWidth: 6, + itemGap: 20, + textStyle: { + color: '#556677' + } + }, + tooltip: { + trigger: 'axis', + axisPointer: { + lineStyle: { + color: "#DCE2E8" + } + }, + backgroundColor: '#fff', + textStyle: { + color: '#5c6c7c' + }, + padding: [10, 10], + extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)' + }, + grid: { + top: '15%' + }, + xAxis: [{ + type: 'category', + data: xData, + axisLine: { + lineStyle: { + color: '#DCE2E8' + } + }, + axisTick: { + show: false + }, + axisLabel: { + // interval: 0, + textStyle: { + color: '#556677' + }, + // 默认x轴字体大小 + fontSize: 0.75*fontSizeText, + // margin:文字到x轴的距离 + margin: fontSizeText, + rotate: 40 + }, + axisPointer: { + label: { + padding: [0, 0, 10, 0], + /* + 除了padding[0]建议必须是0之外,其他三项可随意设置 + 和CSSpadding相同,[上,右,下,左] + 如果需要下边线超出文字,设左右padding即可,注:左右padding最好相同 + padding[2]的10: + 10 = 文字距下边线的距离 + 下边线的宽度 + 如:UI图中文字距下边线距离为7 下边线宽度为2 + 则padding: [0, 0, 9, 0] + */ + // 这里的margin和axisLabel的margin要一致! + margin: fontSizeText, + // 移入时的字体大小 + fontSize: 0.75*fontSizeText, + backgroundColor: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, + color: '#fff' // 0% 处的颜色 + }, { + // offset: 0.9, + offset: 0.86, + //0.86 = (文字 + 文字距下边线的距离)/(文字 + 文字距下边线的距离 + 下边线的宽度) + color: '#fff' // 0% 处的颜色 + }, { + offset: 0.86, + color: '#33c0cd' // 0% 处的颜色 + }, { + offset: 1, + color: '#33c0cd' // 100% 处的颜色 + }], + global: false // 缺省为 false + } + } + }, + boundaryGap: false + }], + yAxis: [{ + name: "(个)", + minInterval: 1, + type: 'value', + axisTick: { + show: false + }, + axisLine: { + show: false, + lineStyle: { + color: '#556677' + } + }, + axisLabel: { + textStyle: { + color: '#556677' + } + }, + splitLine: { + lineStyle: { + color: '#e8e8ed' + } + } + }], + series: [], + }; + + for (let i = 0; i < seriesArr.length; i++) { + if (seriesArr[i].data) { + option.series.push({ + name: seriesArr[i].name, + type: 'line', + data: seriesArr[i].data, + symbolSize: 1, + symbol: 'circle', + smooth: true, + yAxisIndex: 0, + showSymbol: false, + itemStyle: { + normal: { + color: colorList[i], + borderColor: colorList[i] + } + } + }); + } + if(seriesArr[i].name=='新增代码数'&&seriesArr[i].data){ + option.yAxis.push({ + name: "(行)", + type: 'value', + position: 'right', + axisTick: { + show: false + }, + axisLabel: { + textStyle: { + color: '#556677' + }, + formatter: '{value}' + }, + axisLine: { + show: false, + lineStyle: { + color: '#556677' + } + }, + splitLine: { + show: false + } + }); + } + } + + + newEchartBar && newEchartBar.setOption(option); + }, [xData,seriesArr]) + + return ( +
+
+ ) +} \ No newline at end of file diff --git a/src/managements/components/ShowNotification.jsx b/src/managements/components/ShowNotification.jsx new file mode 100644 index 00000000..aa76e04d --- /dev/null +++ b/src/managements/components/ShowNotification.jsx @@ -0,0 +1,8 @@ +import { notification } from 'antd'; + +export default (message)=>{ + notification.open({ + message: "提示", + description: message, + }); +} \ No newline at end of file diff --git a/src/managements/components/Upload.jsx b/src/managements/components/Upload.jsx new file mode 100644 index 00000000..a16768fb --- /dev/null +++ b/src/managements/components/Upload.jsx @@ -0,0 +1,77 @@ +import React, { useEffect, useState } from "react"; +import { Upload, Button } from 'antd'; +import { appendFileSizeToUploadFileAll } from '../common/utils'; +import ShowNotification from './ShowNotification'; + + +function Uploads({ className, size, actionUrl, fileList, load, number }) { + const [files, setFiles] = useState(undefined); + + useEffect(() => { + if (fileList) { + init(); + } + }, [fileList]); + + function init() { + let f = appendFileSizeToUploadFileAll(fileList); + setFiles(f); + } + function onAttachmentRemove(file) { + if (!file.percent || file.percent === 100) { + deleteAttachment(file); + return false; + } + } + function deleteAttachment(file) { + let id = (file.response && file.response.data && file.response.data.id) || file.id; + + // 暂时不直接删除上传的文件,只在最后保存的时候进行修改 + let nf = files.filter(item => { + let itemId = (item.response && item.response.data && item.response.data.id) || item.id; + return itemId !== id; + }); + setFiles(nf); + load && load(nf); + } + + + function handleChange(info) { + if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { + let fileList = info.fileList; + if (number) { + fileList = fileList.slice(-number); + } + if (info.file.status === 'done' && info.file.response.code === "0") { + fileList.pop(); + ShowNotification(info.file.response.message); + } + setFiles(appendFileSizeToUploadFileAll(fileList)); + if (info.file.status === 'done' || info.file.status === 'removed') load && load(fileList); + } + } + + function beforeUpload(file) { + const isLt100M = file.size / 1024 / 1024 < size; + if (!isLt100M) { + ShowNotification(`文件大小必须小于${size}MB!`); + } + return isLt100M; + } + + const upload = { + name: 'file', + fileList: files, + action: actionUrl + `/busiAttachments/upload`, + onChange: handleChange, + onRemove: onAttachmentRemove, + beforeUpload: beforeUpload, + }; + return ( + + + (你可以上传小于{size}MB的文件) + + ) +} +export default Uploads; \ No newline at end of file diff --git a/src/managements/components/layouts/config.js b/src/managements/components/layouts/config.js new file mode 100644 index 00000000..30379d83 --- /dev/null +++ b/src/managements/components/layouts/config.js @@ -0,0 +1,464 @@ +/** + * 路由配置表 + * key:必填,唯一标识 + * title:必填,菜单名称 + * location:路由地址 + * icon:左侧图标 + * target:打开方式,参考HTML标签的target属性 + * urltype:路由类别,用于拓展其它项目管理菜单,self为在当前项目下跳转路由,main_web_site_url或其它为匹配setting中对应路参数径 + * children:子元素数组 + */ + +export default [ + { + key: "statistics", + title: "概览", + location: "", + icon: "icon-yonghuguanli", + target: "_self", + urltype: "self", + children: [ + { + key: "global", + title: "全局统计", + location: "/managements/statistics/global", + menustatus: "Y", + parentKey: "statistics", + icon: "", + target: "", + urltype: "self", + children: [], + }, + // { + // key: "member", + // title: "成员工作统计", + // location: "/managements/statistics/member", + // menustatus: "Y", + // parentKey: "statistics", + // icon: "", + // target: "", + // urltype: "self", + // children: [], + // }, + // { + // key: "activity", + // title: "项目活跃度统计", + // location: "/managements/statistics/activity", + // menustatus: "Y", + // parentKey: "statistics", + // icon: "", + // target: "", + // urltype: "self", + // children: [], + // }, + ], + }, + { + key: "task", + title: "创客空间", + location: "", + icon: "icon-weixuanze-chuangkekongjian", + target: "_self", + urltype: "self", + children: [ + { + key: "taskAdmin", + title: "创客任务列表", + location: "/managements/task/taskAdmin", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "commission", + title: "待办事项", + location: "", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "_self", + urltype: "self", + children: [ + { + key: "delayManage", + title: "延期任务处理", + location: "/managements/task/delayManage", + menustatus: "Y", + parentKey: "commission", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "payProof", + title: "支付报酬凭证上传", + location: "/managements/task/payProof", + menustatus: "Y", + parentKey: "commission", + icon: "", + target: "", + urltype: "self", + children: [], + }, + ], + }, + { + key: "publish", + title: "任务发布审批", + location: "", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "_self", + urltype: "self", + children: [ + { + key: "overall", + title: "统筹任务发布审批", + location: "/managements/task/taskManage/1", + menustatus: "Y", + parentKey: "publish", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "autonomous", + title: "自主任务发布审批", + location: "/managements/task/taskManage/0", + menustatus: "Y", + parentKey: "publish", + icon: "", + target: "", + urltype: "self", + children: [], + }, + ], + }, + { + key: "outcomeReview", + title: "成果评论审批", + location: "/managements/task/paperManage", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "supportingMaterials", + title: "佐证材料审批", + location: "/managements/task/proofManage", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "agreement", + title: "协议审批", + location: "/managements/task/agreementManage", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "appeal", + title: "申诉审批", + location: "", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "self", + children: [ + { + key: "resultsSubmission", + title: "成果提交申诉审批", + location: "/managements/task/paperComplain", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "publicityPeriod", + title: "公示期成果申诉审批", + location: "/managements/task/publicityComplain", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "self", + children: [], + }, + ], + }, + // { + // key: "basics", + // title: "基础数据", + // location: "", + // menustatus: "Y", + // parentKey: "task", + // icon: "", + // target: "_self", + // urltype: "self", + // children: [ + // { + // key: "categories", + // title: "任务领域", + // location: "/admin/categories/list", + // menustatus: "Y", + // parentKey: "task", + // icon: "", + // target: "_self", + // urltype: "main_web_site_url", + // children: [], + // }, + // { + // key: "industries", + // title: "行业信息", + // location: "/managements/industries/list", + // menustatus: "Y", + // parentKey: "task", + // icon: "", + // target: "_self", + // urltype: "main_web_site_url", + // children: [], + // }, + // { + // key: "placements", + // title: "职位信息", + // location: "/managements/placements/list", + // menustatus: "Y", + // parentKey: "task", + // icon: "", + // target: "_self", + // urltype: "main_web_site_url", + // children: [], + // }, + // ], + // }, + ], + }, + { + key: "competition", + title: "竞赛活动", + location: "", + icon: "icon-jingsaihuodong", + target: "_self", + urltype: "self", + children: [ + { + key: "competitionList", + title: "竞赛列表", + location: "/admin/competitions/list", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "", + urltype: "main_web_site_url", + params:'layout=none', + children: [], + }, + { + key: "competitionRegion", + title: "赛区配置", + location: "/admin/competitions/region_zone_edit", + menustatus: "Y", + parentKey: "task", + icon: "", + target: "_blank", + urltype: "main_web_site_url", + params:'layout=none', + children: [], + }, + { + key: "competitionExpert", + title: "竞赛评审列表", + location: "/managements/expert/competition", + menustatus: "Y", + parentKey: "competition", + icon: "", + target: "", + urltype: "self", + children: [], + }, + ], + }, + { + key: "userManagement", + title: "用户管理", + location: "", + icon: "icon-yonghuguanli", + target: "_self", + urltype: "self", + children: [ + { + key: "expertRegister", + title: "专家注册列表", + location: "/managements/expert/register", + menustatus: "Y", + parentKey: "userManagement", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "expertList", + title: "专家库列表", + location: "/managements/expert/list", + menustatus: "Y", + parentKey: "userManagement", + icon: "", + target: "", + urltype: "self", + children: [], + }, + ], + }, + { + key: "notice", + title: "公告管理", + location: "", + icon: "icon-yonghuguanli", + target: "_self", + urltype: "self", + children: [ + { + key: "noticeList1", + title: "已发布", + location: "/managements/notice/list/1", + menustatus: "Y", + parentKey: "notice", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "noticeList2", + title: "待审核", + location: "/managements/notice/list/2", + menustatus: "Y", + parentKey: "notice", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "noticeList0", + title: "未通过", + location: "/managements/notice/list/0", + menustatus: "Y", + parentKey: "notice", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "noticeDraft", + title: "草稿箱", + location: "/managements/notice/list/draft", + menustatus: "Y", + parentKey: "notice", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "noticeEdit", + title: "发布公告", + location: "/managements/notice/edit", + menustatus: "Y", + parentKey: "notice", + icon: "", + target: "", + urltype: "self", + children: [], + }, + ], + }, + { + key: "jurisdiction", + title: "权限管理", + location: "", + icon: "icon-yonghuguanli", + target: "_self", + urltype: "self", + children: [ + { + key: "adminUserList", + title: "管理员列表", + location: "/managements/jurisdiction/users", + menustatus: "Y", + parentKey: "jurisdiction", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "roleList", + title: "角色列表", + location: "/managements/jurisdiction/roles", + menustatus: "Y", + parentKey: "jurisdiction", + icon: "", + target: "", + urltype: "self", + children: [], + }, + { + key: "menuList", + title: "菜单列表", + location: "/managements/jurisdiction/menu", + menustatus: "Y", + parentKey: "jurisdiction", + icon: "", + target: "", + urltype: "self", + children: [], + }, + ], + }, + { + key: "settings", + title: "网站配置", + location: "", + icon: "icon-yonghuguanli", + target: "", + urltype: "current_main_site_url", + children: [ + { + key: "platform_communicates", + title: "社区动态管理", + location: "/managements/admins/platform_communicates", + menustatus: "Y", + parentKey: "settings", + icon: "", + target: "", + urltype: "current_main_site_url", + params:'layout=none', + children: [], + }, + ], + }, +]; diff --git a/src/managements/components/layouts/index.jsx b/src/managements/components/layouts/index.jsx new file mode 100644 index 00000000..7623add6 --- /dev/null +++ b/src/managements/components/layouts/index.jsx @@ -0,0 +1,197 @@ +import React, { useState, useEffect, Fragment } from "react"; +import { Link } from "react-router-dom"; +import { Menu } from "antd"; +import { main_web_site_url } from '../../fetch'; +import "./index.scss"; +const { SubMenu } = Menu; + + +export default (props) => { + const { current_user, children, history, menuList, location: { pathname } } = props; + const setting = (localStorage.chromesetting && JSON.parse(localStorage.chromesetting)) || {}; + const [openKeys, setOpenKeys] = useState([]); + const [current, setCurrent] = useState(); + const [keyPath, setKeyPath] = useState(); + const [acitve, setActive] = useState({}); + + const titleObj = {}; + const locationObj = {}; + const parentKeyObj = {}; + const allRouter = []; + const rootSubmenuKeys = []; + + function titleFun(menus) { + menus.forEach(i => { + if (i.hidden === 1) { + titleObj[i.key] = i.title; + locationObj[i.location] = i; + i.key = i.key + ''; + i.parentKey = i.parentKey + ''; + i.location && allRouter.push(i.location); + i.parentKey == '0' && rootSubmenuKeys.push(i.key); + if (i.hidden === 1 && Array.isArray(i.children) && i.children.length > 0) { + titleFun(i.children); + i.parentKey && (parentKeyObj[i.key] = i.parentKey + ''); + } + } + + }) + } + + menuList && menuList.length && titleFun(menuList); + + useEffect(() => { + sessionStorage.setItem("current_user", JSON.stringify(current_user)); + }, [current_user.login]); + + + + useEffect(() => { + if (locationObj[pathname]) { + setCurrent(locationObj[pathname] ? [locationObj[pathname].key] : []); + setKeyPath(locationObj[pathname] ? [locationObj[pathname].parentKey, locationObj[pathname].key] : []); + const defaultOpenKeys = []; + const myKey = pathname && locationObj[pathname] && locationObj[pathname].parentKey; + defaultOpenKeys.push(myKey ? myKey : "task"); + parentKeyObj[myKey] && defaultOpenKeys.push(parentKeyObj[myKey]); + setActive(locationObj[pathname]); + setOpenKeys(defaultOpenKeys); + } + }, [locationObj[pathname]]) + + + function handleClick(e) { + setCurrent([e.key]); + setKeyPath(e.keyPath.reverse()); + } + + function itemClick(item) { + let myIframe = document.getElementById("iframe"); + if (myIframe) { + myIframe.height = 'auto'; + } + setActive(item) + } + + function getMenuList(menus) { + return menus.map(function (item) { + if (item.hidden === 1) { + let title = ( + + {item.icon && } + {item.title} + + ); + if (Array.isArray(item.children) && item.children.length > 0) { + return ( + {getMenuList(item.children)} + ); + } else { + return ( + + {item.location ? { itemClick(item) }}>{title} + : title} + + ); + } + } + }); + } + + function head(keyPath) { + return keyPath ? keyPath.map((i, index) => { + return {titleObj[i]}{index !== keyPath.length - 1 && ' / '} + }) : '' + } + + function iframeLoad() { + try { + let myIframe = document.getElementById("iframe"); + if (myIframe.contentDocument) { + myIframe.height = myIframe.contentDocument.querySelector('.content').clientHeight + 260; + myIframe.contentDocument.querySelector('.admin-body-container').style.overflow = 'hidden'; + // let MutationObserver =window.MutationObserver ; + + // // let MutationObserver =myIframe.contentWindow.MutationObserver || myIframe.contentWindow.WebKitMutationObserver || myIframe.contentWindow.MozMutationObserver; + // let observer = new MutationObserver(function (mutations) { + // console.log("asdf", mutations); + // }); + // observer.observe(myIframe.contentDocument.querySelector('.content'), { + // attributes: true, + // attributeFilter:['style'], + // attributeOldValue:true + // }); + } + } catch (err) { + console.error(err); + } + } + + // useEffect(() => { + // try { + // document.getElementById("iframe").contentDocument.querySelector('.content').addEventListener("message", iframeHeight, false); + // return () => { + // document.getElementById("iframe").contentDocument.querySelector('.content').removeEventListener("message", iframeHeight, false); + // } + // } catch (err) { + // console.error(err); + // } + // }, []); + + function onOpenChange(newOpenKeys) { + const latestOpenKey = newOpenKeys.find(key => openKeys.indexOf(key) === -1); + if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) { + setOpenKeys(newOpenKeys); + } else { + setOpenKeys(latestOpenKey ? [latestOpenKey] : []); + } + } + + useEffect(() => { + // 添加路由监听函数 + history.listen(historyLocation => { + // 每次路由变化都会执行这个方法 + if (allRouter.includes(historyLocation.pathname)) { + setCurrent([locationObj[historyLocation.pathname].key]); + setKeyPath([locationObj[pathname].parentKey, locationObj[pathname].key]); + } + }) + }, [history]); + + + useEffect(() => { + window.addEventListener("message", iframeHeight, false); + return () => { + window.removeEventListener("message", iframeHeight, false); + } + }, []); + + function iframeHeight(e) { + if (e.origin === main_web_site_url && e.data && !isNaN(e.data)) { + document.getElementById("iframe").height=Number(e.data)+20; + } + } + + return ( + +
+ + {menuList && menuList.length && getMenuList(menuList)} + +
+
+
{head(keyPath)}
+ {acitve && acitve.urlType && acitve.urlType !== 'self' ? + + : children + } +
+
+ ); +}; diff --git a/src/managements/components/layouts/index.scss b/src/managements/components/layouts/index.scss new file mode 100644 index 00000000..0c05771e --- /dev/null +++ b/src/managements/components/layouts/index.scss @@ -0,0 +1,67 @@ +.layouts { + position: fixed; + top: 70px; + left: 0; + width: 13.75rem; + height: calc(100vh - 70px); + overflow: hidden; + overflow-y: scroll; + background-color:#ffffff; + border-radius:4px; + box-shadow:0px 3px 10px rgba(88, 116, 255, 0.07); + &::-webkit-scrollbar{ + width: 0; + } + .left-icon { + font-size: 1.25rem; + margin-right: 0.625rem; + margin-left: -.3125rem; + } + .ant-menu-submenu:hover, .ant-menu-submenu-selected, .ant-menu:hover, .ant-menu-submenu-selected .ant-menu{ + background-color:#fbfbff; + } + + .ant-menu-submenu-selected{ + .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected{ + background-image:linear-gradient(90deg,#e7efff 0%,#f0f5ff 65.69%,#ffffff 100%); + &::after{ + position: absolute; + top: 0; + left: 0; + bottom: 0; + content: ''; + border: none; + border-left: 5px solid #2a3ee9; + } + } + } + li.ant-menu-item{ + border: none; + &:hover{ + background-color:#e2e6f5; + a{ color: rgba(0, 0, 0, 0.65) !important;} + } + } +} +.managements { + .head-title { + padding-top:1.25rem; + span { + color: #a6adc4; + font-size: 0.875rem; + line-height: 1.375rem; + + &:last-child{ + color:#596078; + } + } + } + .iframe-item{ + width: 100%; + min-height: calc(100vh - 225px); + border: 0; + } + .ant-input, .ant-select-selection{ + border-color:#e2e6f5; + } +} diff --git a/src/managements/fetch.js b/src/managements/fetch.js new file mode 100644 index 00000000..cd96a228 --- /dev/null +++ b/src/managements/fetch.js @@ -0,0 +1,11 @@ + +import javaFetch from '../military/javaFetch'; + + +let settings = localStorage.chromesetting && JSON.parse(localStorage.chromesetting); +let actionUrl = settings && settings.api_urls && settings.api_urls.task ? settings.api_urls.task : 'https://task.osredm.com'; +// let actionUrl = 'http://10.47.38.60:8088'; +const service = javaFetch(actionUrl); +export const httpUrl = actionUrl; +export const main_web_site_url = settings && settings.main_web_site_url; +export default service; \ No newline at end of file diff --git a/src/managements/index.js b/src/managements/index.js new file mode 100644 index 00000000..3e9e8f76 --- /dev/null +++ b/src/managements/index.js @@ -0,0 +1,334 @@ +import React, { useEffect, useState } from "react"; +import { Route, Switch } from "react-router-dom"; +import { withRouter } from "react-router"; +import { SnackbarHOC } from "educoder"; +import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC"; +import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC"; +import Loadable from "react-loadable"; +import Loading from "../Loading"; +import { ImageLayerOfCommentHOC } from "../modules/page/layers/ImageLayerOfCommentHOC"; +import Layouts from "./components/layouts"; +import './index.scss'; + +// 概览 +const Statistics = Loadable({ + loader: () => import("./statistics/"), + loading: Loading, +}); + +{/* 任务管理审核 */ } +const TaskManage = Loadable({ + loader: () => import("../military/task/taskManage"), + loading: Loading, +}); + +{/* 成果管理审核 */ } +const PaperManage = Loadable({ + loader: () => import("../military/task/paperManage"), + loading: Loading, +}); + +{/* 成果申诉管理审核 */ } +const PaperComplain = Loadable({ + loader: () => import("../military/task/paperComplain"), + loading: Loading, +}); + +const ProofManage = Loadable({ + loader: () => import("../military/task/proofManage"), + loading: Loading, +}); + +const PublicityComplain = Loadable({ + loader: () => import("../military/task/publicityComplain"), + loading: Loading, +}); + +const AgreementManage = Loadable({ + loader: () => import("../military/task/agreementManage"), + loading: Loading, +}); + +const PayProof = Loadable({ + loader: () => import("../military/task/payProof"), + loading: Loading, +}); + +const TaskAdmin = Loadable({ + loader: () => import("../military/task/taskAdmin"), + loading: Loading, +}); + +const DelayManage = Loadable({ + loader: () => import("../military/task/delayManage"), + loading: Loading, +}); +// 协议签订模板 +const AgreementContent = Loadable({ + loader: () => import("../military/task/agreementContent"), + loading: Loading, +}); + + + +// 专家库 +const ExpertList = Loadable({ + loader: () => import("../military/expert/expertList"), + loading: Loading, +}); +// 专家审核 +const RegisterList = Loadable({ + loader: () => import("../military/expert/registerList"), + loading: Loading, +}); +//创客任务评审规则 +const ReviewRules = Loadable({ + loader: () => import("../military/expert/reviewRules"), + loading: Loading, +}); +//创客任务选择评审专家 +const SelectExpert = Loadable({ + loader: () => import("../military/expert/selectExpert"), + loading: Loading, +}); +//创客任务评审结果查看 +const ReviewResult = Loadable({ + loader: () => import("../military/expert/reviewResult"), + loading: Loading, +}); +//竞赛评审任务列表 +const CompetitionList = Loadable({ + loader: () => import("../military/expert/competionList"), + loading: Loading, +}); +// 定制化竞赛列表,如启智 +const CompetionCustomize =Loadable({ + loader: () => import("../managements/competition/competionCustomize"), + loading: Loading, +}) +const CompetionSetting=Loadable({ + loader: () => import("../managements/competition/setting"), + loading: Loading, +}); +// 公告管理 +const NoticeManage = Loadable({ + loader: () => import("./notice"), + loading: Loading, +}); + +// 权限管理 +// 非普通用户(管理员)列表 +const AdminUserList = Loadable({ + loader: () => import("./jurisdiction/userList"), + loading: Loading, +}) +// 系统角色列表 +const RoleList = Loadable({ + loader: () => import("./jurisdiction/roles"), + loading: Loading, +}) +// 菜单列表 +const MenuList = Loadable({ + loader: () => import("./jurisdiction/menuList"), + loading: Loading, +}) + +const Managements = (propsF) => { + + return ( +
+ + + + {/* 概览 */} + ( + + )} + > + + {/* 任务管理审核 */} + ( + + )} + > + + {/* 成果管理审核 */} + ( + + )} + > + + {/* 成果申诉管理审核 */} + ( + + )} + > + + {/* 公示期成果申诉管理审核 */} + ( + + )} + > + + {/* 佐证管理审核 */} + ( + + )} + > + + {/* 管理员协议审核 */} + ( + + )} + > + + {/* 管理员上传支付凭证 */} + ( + + )} + > + + {/* 管理员任务列表 */} + ( + + )} + > + + {/* 延期管理 */} + ( + + )} + > + + {/* 协议内容 */} + ( + + )} + > + + {/* 专家审核 */} + ( + + )} + > + {/* 专家库列表 */} + ( + + )} + > + {/* 创客任务评审规则 */} + ( + + )} + > + {/* 创客任务选择评审专家 */} + ( + + )} + > + {/* 创客任务评审结果查看 */} + ( + + )} + > + {/* 竞赛评审任务列表 */} + ( + + )} + > + {/* 非普通用户(管理员)列表 */} + ( + + )} + > + {/* 系统角色列表 */} + ( + + )} + > + {/* 菜单列表 */} + ( + + )} + > + + {/* 公告管理 */} + ( + + )} + > + + {/* 定制竞赛配置详情 */} + ( + + )} + > + ( + + )} + > + {/* 定制竞赛配置列表 */} + ( + + )} + > + + +
+ ); +} + +export default withRouter( + ImageLayerOfCommentHOC({ + imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget", + parentSelector: ".newMain", + })(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Managements)))) +); diff --git a/src/managements/index.scss b/src/managements/index.scss new file mode 100644 index 00000000..3004018a --- /dev/null +++ b/src/managements/index.scss @@ -0,0 +1,91 @@ +.managements{ + margin-left: 13.75rem; + padding:0 1.875rem 1.6rem; + background: #f6f9fe; + min-height: 80vh; + .content{ + margin-top:1rem; + } +} +.management-content-head{ + background-color: #fff; + margin-top: 1.5rem; + padding: 1.5rem; + border-radius:4px; + box-shadow:0px 0px 10px rgba(88, 116, 255, 0.18); + >p{ + color:#18191e; + padding-bottom: 10px; + border-bottom: 1px solid #e2e6f5; + } +} +.managements .ant-table-thead > tr > th,.managements .ant-table-tbody > tr > td { + padding: 1rem 1rem; + text-align: left; + .ant-btn{ + padding: 0 9px; + } + .f20{ + border-color:#f20000; + span{ + color:#f20000; + } + } + .f89{ + border-color:#f89d00; + span{ + color:#f89d00; + } + } + .c2a3{ + border-color:#2a3ee9; + &.bor{ + background-color: #fff; + span{color:#2a3ee9;} + } + &.full{ + background-color:#2a3ee9; + span{color: white;} + } + &.disabled{ + background-color:#f2f2f2; + border: none; + span{color:#a6adc4;} + } + } +} +.f20{ + border-color:#f20000; + span{ + color:#f20000; + } +} +.f89{ + border-color:#f89d00; + span{ + color:#f89d00; + } +} +.c2a3{ + border-color:#2a3ee9; + &.bor{ + background-color: #fff; + span{color:#2a3ee9;} + } + &.full{ + background-color:#2a3ee9; + span{color: white;} + } + &.disabled{ + background-color:#f2f2f2; + border: none; + span{color:#a6adc4;} + } +} +// 系统默认字体大小是16px,大屏的在此基础上增大字体 +@media screen and (min-width: 1921px) { + html{ + font-size: calc(100% + 6 * (100vw - 1920px) / 1000 ); + font-size: calc(100% + 6 * (100vw - 1920px) / 1000 ); + } +} \ No newline at end of file diff --git a/src/managements/jurisdiction/index.scss b/src/managements/jurisdiction/index.scss new file mode 100644 index 00000000..f1e12ea1 --- /dev/null +++ b/src/managements/jurisdiction/index.scss @@ -0,0 +1,27 @@ +.search-list{ + margin-top: 1rem; + display: flex; + justify-content: flex-end; +} +.menuDetail{ + >div{ + display: flex; + align-items: center; + } + .ant-form-item-label{ + width: 5rem; + justify-content: flex-end; + margin-right: 1rem; + } +} +.menuDetail{ + .has-error .ant-form-explain{ + position: absolute; + } + .ant-row{ + margin-top: 24px; + } + .ant-row:first-child{ + margin-top: 0; + } + } \ No newline at end of file diff --git a/src/managements/jurisdiction/menuList/index.jsx b/src/managements/jurisdiction/menuList/index.jsx new file mode 100644 index 00000000..cc685379 --- /dev/null +++ b/src/managements/jurisdiction/menuList/index.jsx @@ -0,0 +1,285 @@ +import React, { useCallback, useEffect, useState, useMemo } from 'react'; +import { Input, Select, Button, Form, message, Switch, Radio, Popconfirm, TreeSelect } from 'antd'; +import {createMenu, deleteMenu, treeList, updateMenu, updateMenuStatus} from '../../api'; +import PaginationTable from '../../../components/pagination-table'; +import Modal from '../../../components/modal/index'; + +import './index.scss'; +import '../index.scss'; +const Option = Select.Option; + +export default Form.create()(({ form}) => { + const { getFieldDecorator, setFieldsValue, getFieldsValue, validateFields } = form; + const [reload, setReload] = useState(); + const [loading, setLoading] = useState(false); + const [curPage, setCurPage] = useState(1); + const [total, setTotal] = useState(0); + // 菜单列表 + const [dataList, setDataList] = useState([]); + // 菜单列表(多一个无上级菜单) + const [treeNode, setTreeNode] = useState([]); + // 新增、编辑菜单详情信息 + const [menuDetailModal, setMenuDetailModal] = useState(false); + const [editMenuModal, setEditMenuModal] = useState(false); + const [editMenuId, setEditMenuId] = useState(undefined); + const [treeSelectValue, setTreeSelectValue] = useState(undefined); + const helper = useCallback( + (label, name, rules, widget, initialValue) => ( + + {getFieldDecorator(name, { rules, initialValue, validateFirst: true, })(widget)} + + ), []); + + const columns = useMemo(() => { + return [ + { + title: '菜单名称', + dataIndex: 'title', + render: (text, record) => ( + text || record.name + ), + }, + { + title: '菜单级数', + dataIndex: 'level', + render: (text, record) => (text+1), + }, + { + title: '路由', + dataIndex: 'location', + width: "20%", + render: (text, record) => (text || '--'), + }, + { + title: '前端图标', + dataIndex: 'icon', + width: "20%", + render: (text, record) => {return text || '--'} + }, + { + title: '是否显示', + dataIndex: 'hidden', + render: (text, record) => { + return changeMenuStatus(checked, record.key)}/> + } + }, + { + title: '操作', + key: 'action', + dataIndex: 'key', + align: 'center', + render: (text, record) => { + return( + + + deleteMenuById(text)}> + + + + )}, + }, + ] + }, [curPage, reload]); + + const menuDetail =( +
+ {helper( + "菜单名称", + "menuName", + [{ max: 20, message: '长度不能超过20个字符' },{required: true, message: "菜单名称不能为空"}], + + )} + {helper( + "上级菜单", + "parentKey", + [], + setTreeSelectValue(value)} + />, + )} + {helper( + "路由", + "location", + [{ max: 100, message: '长度不能超过100个字符' }], + + )} + {helper( + "前端图标", + "icon", + [{ max: 100, message: '长度不能超过100个字符' }], + + )} + {helper( + "是否显示", + "hidden", + [], + + + + , + )} + {helper( + "环境", + "urlType", + [], + , + )} +
+ ) + + //菜单是否启用 + function changeMenuStatus(checked, MenuId){ + updateMenuStatus(MenuId, checked ? 1: 0).then(response=>{ + if(response && response.message=='success'){ + message.success("状态更新成功"); + setReload(Math.random()); + }else{ + message.error('状态更新失败'); + } + }) + } + + function addMenu(){ + validateFields((error, values) => { + if(!error){ + // 获取用户输入的内容 + const params = { + name: values.menuName, + title: values.menuName, + parentKey: parseInt(values.parentKey || 0), + location: values.location, + icon: values.icon, + hidden: values.hidden, + level: parseInt(values.parentKey)+1, + urlType:values.urlType, + } + createMenu(params).then(response=>{ + if(response && response.message=='success'){ + message.success("菜单新建成功"); + setReload(Math.random()); + setMenuDetailModal(false); + } + }) + } + }) + } + + //删除菜单 + function deleteMenuById(menuId){ + // 获取用户输入的内容 + deleteMenu(menuId).then(response=>{ + if(response && response.message=='success'){ + message.success("菜单删除成功"); + setReload(Math.random()); + }else{ + message.error('菜单删除失败'); + } + }) + } + + //编辑菜单 + function editMenuDetail(roleDetail){ + setEditMenuId(roleDetail.key); + setFieldsValue({ + menuName: roleDetail.name, + parentKey: roleDetail.parentKey.toString(), + location: roleDetail.location, + icon: roleDetail.icon, + hidden: roleDetail.hidden, + urlType: roleDetail.urlType + }); + setEditMenuModal(true); + } + + // 编辑菜单信息 + function editMenu(){ + validateFields((error, values) => { + if(!error){ + // 获取用户输入的内容 + const params = { + ...values, + title: values.menuName, + name: values.menuName, + } + updateMenu(editMenuId, params).then(response=>{ + if(response && response.message=='success'){ + setReload(Math.random()); + setEditMenuModal(false); + message.success("菜单更新成功"); + }else{ + message.error('菜单更新失败'); + } + }) + } + }) + } + + // 遍历数组,对象新增value属性,新增或者修改菜单 选择上级菜单需要使用 + function valueFun(menus) { + const treeList = menus.slice(0, menus.length); + treeList.map(item=>{ + item.value = item.key; + if (Array.isArray(item.children) && item.children.length > 0) { + valueFun(item.children); + } + }) + treeList.unshift({key:'0', value: '0', title: "无上级菜单"}); + setTreeNode(treeList); + } + + useEffect(()=>{ + setLoading(true); + treeList().then(response=>{ + if(response && response.message=='success'){ + setDataList(response.data); + valueFun(response.data); + } + }).finally(()=>{ + setLoading(false); + }) + },[reload]) + + return ( +
+
+

菜单列表

+
+ + setMenuDetailModal(false)}/> + setEditMenuModal(false)}/> +
+
+
+ row.key} + dataSource={dataList} + columns={columns} + total={total} + current={curPage} + setCurPage={(page)=>{setCurPage(page)}} + className="expertReviewTable" + indentSize={30} + /> +
+
+ ) +}) \ No newline at end of file diff --git a/src/managements/jurisdiction/menuList/index.scss b/src/managements/jurisdiction/menuList/index.scss new file mode 100644 index 00000000..eba0f9f1 --- /dev/null +++ b/src/managements/jurisdiction/menuList/index.scss @@ -0,0 +1,20 @@ +// .ant-table-row-collapsed::after, .ant-table-row-expanded::after{ +// content: '查看下级'; +// color:#2a3ee9; +// } +// .ant-table-row-expand-icon, .ant-table-row-expand-icon{ +// width: auto; +// height: 32px; +// line-height: 32px; +// padding: 0 9px; +// border-color:#2a3ee9; +// background-color: #fff; +// :hover, :hover::after{ +// border-color:#2a3ee9; +// opacity: 0.75; +// } +// } + +.column-select .ant-select-selection{ + width: 193px; +} \ No newline at end of file diff --git a/src/managements/jurisdiction/roles/index.jsx b/src/managements/jurisdiction/roles/index.jsx new file mode 100644 index 00000000..874a32fa --- /dev/null +++ b/src/managements/jurisdiction/roles/index.jsx @@ -0,0 +1,286 @@ +import React, { useCallback, useEffect, useState, useMemo } from 'react'; +import { Input, Button, Form, message, Radio, Switch, Popconfirm, Tree } from 'antd'; +import Modal from '../../../components/modal/index'; +import PaginationTable from '../../../components/pagination-table'; +import './index.scss'; +import '../index.scss'; +import '../../index.scss'; +import { createRole, deleteRole, roleList, updateRoleStatus, updateRole, treeList, allocMenu, listMenu } from 'src/managements/api'; +const { TextArea } = Input; + +export default Form.create()(({form}) => { + const { getFieldDecorator, setFieldsValue, getFieldsValue, validateFields } = form; + const [reload, setReload] = useState(); + const [loading, setLoading] = useState(false); + const [curPage, setCurPage] = useState(1); + const [total, setTotal] = useState(0); + //角色列表 + const [dataList, setDataList] = useState([]); + // 角色详情弹框 + const [roleDetailModal, setRoleDetailModal] = useState(false); + // 更新角色信息弹框 + const [editRoleModal, setEditRoleModal] = useState(false); + const [editRoleId, setEditRoleId] = useState(undefined); + //分配菜单弹框 + const [menuModal, setMenuModal] = useState(false); + const [menuTreeData, setMenuTreeData] = useState(false); + const [roleId, setRoleId] = useState(undefined); + //选中的菜单项 + const [selectedMenu, setSelectedMenu] = useState(undefined); + // 选中的菜单项(子菜单项未选中,带父菜单项) + const [selectedMenuFa, setSelectedMenuFa] = useState(undefined); + // 一级菜单项 + let oneTree = []; + + const helper = useCallback( + (label, name, rules, widget, initialValue) => ( + + {getFieldDecorator(name, { rules, initialValue, validateFirst: true, })(widget)} + + ), []); + + const columns = useMemo(() => { + return [ + { + title: '编号', + dataIndex: 'number', + render: (text, record, index) => ( + index+1 + ), + }, + { + title: '角色名称', + dataIndex: 'name' + }, + { + title: '描述', + dataIndex: 'description', + width: "20%" + }, + { + title: '添加时间', + dataIndex: 'createTime', + render: (text, record) => { + text = text && text.replaceAll('-','/').substring(0,16); + return text || '--' + } + }, + // { + // title: '是否启用', + // dataIndex: 'status', + // render: (text, record) => { + // return changeRoleStatus(checked, record.id)}/> + // } + // }, + { + title: '操作', + key: 'action', + dataIndex: 'id', + align: 'center', + render: (text, record) => { + return( + + + + deleteRoleById(text)}> + + + + )}, + }, + ] + }, [curPage]); + + const roleDetail =( +
+ {helper( + "角色名称", + "name", + [{ max: 20, message: '长度不能超过20个字符'}, + { required: true, message: "角色名称不能为空"}, + { validator: (rule, value, callback)=>{ + // 角色名称是否存在 要过滤掉编辑菜单项的初始值 + const filter = dataList.filter(item=>{ return item.id !== editRoleId && item.name === value}); + filter.length>0 ? callback('此角色名称已存在'):callback(); + }}], + + )} + {helper( + "描述", + "description", + [{ max: 500, message: '长度不能超过500个字符'}], +