diff --git a/api/app/lib/controllers/control/analysis.js b/api/app/lib/controllers/control/analysis.js index 16dbdbe..b2de347 100644 --- a/api/app/lib/controllers/control/analysis.js +++ b/api/app/lib/controllers/control/analysis.js @@ -140,7 +140,7 @@ async function personnelApp (ctx) { findOptions.where.id = { $in: userInfo.correlationProject } } if (pepId) { - findOptions.where.id = pepId + findOptions.where.id = { $in: pepId.split(',') } } const proRes = await models.ProjectCorrelation.findAndCountAll(findOptions) @@ -199,7 +199,8 @@ async function personnelApp (ctx) { let personnel = userRes.rows.filter(r => r.correlationProject.length > 0) if (pepId) { - personnel = personnel.filter(r => r.dataValues.correlationProject.map(v => v.id).includes(Number(pepId))) + let pepIds = pepId.split(',') + personnel = personnel.filter(r => r.dataValues.correlationProject.map(v => v.id).some(pepId => pepIds.includes(pepId))) } ctx.status = 200 diff --git a/api/app/lib/controllers/control/data.js b/api/app/lib/controllers/control/data.js index fe5e291..0b7d5e4 100644 --- a/api/app/lib/controllers/control/data.js +++ b/api/app/lib/controllers/control/data.js @@ -250,7 +250,11 @@ async function getAlarmsHandleStatistics (ctx) { const models = ctx.fs.dc.models; const data = await models.AlarmHandleStatistics.findAll({ order: [['time', 'DESC']], - where: projectCorrelationId ? { projectCorrelationId: projectCorrelationId } : {}, + where: projectCorrelationId ? + { + projectCorrelationId: { $in: projectCorrelationId.split(',') } + } + : {}, limit: 1 }) ctx.status = 200; @@ -278,8 +282,8 @@ async function getLatestDynamic (ctx) { if (projectCorrelationId) {//查指定项目,控制台全局切换 where.projectCorrelationId = projectCorrelationId } - - + + let news = await models.LatestDynamicList.findAll({//最新动态 include: [{ model: models.ProjectCorrelation, @@ -322,7 +326,7 @@ async function getLatestDynamic (ctx) { } } - + // EM 推送的特殊处理 @@ -336,7 +340,7 @@ async function getLatestDynamic (ctx) { }) : [] - + for (let { dataValues: p } of emailLogProjectCorrelationRes) { if (p.pepProjectId) { @@ -400,7 +404,7 @@ async function getLatestDynamic (ctx) { confirm//确认 }; } catch (error) { - + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; ctx.body = { diff --git a/api/app/lib/controllers/project/group.js b/api/app/lib/controllers/project/group.js new file mode 100644 index 0000000..be05ecd --- /dev/null +++ b/api/app/lib/controllers/project/group.js @@ -0,0 +1,110 @@ +'use strict'; +const moment = require('moment') + +async function groupList (ctx) { + try { + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const res = await models.ProjectGroup.findAll({ + where: { + pomsUserId: userId + }, + order: [['id', 'DESC']] + }) + + ctx.status = 200; + ctx.body = res + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function editGroup (ctx) { + try { + const { models } = ctx.fs.dc; + const { userId } = ctx.fs.api + const { id, name, pomsProjectIds = [] } = ctx.request.body + + if (!name || !pomsProjectIds || !pomsProjectIds.length) { + throw '参数错误!' + } + + let repeatNameRes = await models.ProjectGroup.findOne({ + where: { + pomsUserId: userId, + name, + } + }) + let repeatProjectRes = await models.ProjectGroup.findOne({ + where: { + pomsUserId: userId, + pomsProjectIds + } + }) + if (repeatNameRes && (!id || (id && repeatNameRes.id != id))) { + throw '已有相同名称的分组信息!' + } + if (repeatProjectRes && (!id || (id && repeatProjectRes.id != id))) { + throw '已有相同项目的分组信息!' + } + + if (id) { + await models.ProjectGroup.update({ + name, + pomsProjectIds, + }, { + where: { + id + } + }) + } else { + await models.ProjectGroup.create({ + name, + pomsProjectIds, + pomsUserId: userId, + }, { + where: { + id + } + }) + } + + ctx.status = 204; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +async function delGroup (ctx) { + try { + const { models } = ctx.fs.dc; + + await models.ProjectGroup.destroy({ + where: { + id: ctx.query.groupId + } + }) + + ctx.status = 204; + } catch (error) { + ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); + ctx.status = 400; + ctx.body = { + message: typeof error == 'string' ? error : undefined + } + } +} + +module.exports = { + groupList, + editGroup, + delGroup, +}; \ No newline at end of file diff --git a/api/app/lib/models/project_group.js b/api/app/lib/models/project_group.js new file mode 100644 index 0000000..66a7336 --- /dev/null +++ b/api/app/lib/models/project_group.js @@ -0,0 +1,57 @@ +/* eslint-disable*/ + +'use strict'; + +module.exports = dc => { + const DataTypes = dc.ORM; + const sequelize = dc.orm; + const ProjectGroup = sequelize.define("projectGroup", { + id: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: true, + field: "id", + autoIncrement: true, + unique: "project_group_id_uindex" + }, + name: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: null, + comment: null, + primaryKey: false, + field: "name", + autoIncrement: false + }, + pomsProjectIds: { + type: DataTypes.ARRAY(DataTypes.INTEGER), + allowNull: false, + defaultValue: null, + comment: "运维项目id", + primaryKey: false, + field: "poms_project_ids", + autoIncrement: false + }, + pomsUserId: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: null, + comment: null, + primaryKey: false, + field: "poms_user_id", + autoIncrement: false, + references: { + key: "id", + model: "user" + } + } + }, { + tableName: "project_group", + comment: "", + indexes: [] + }); + dc.models.ProjectGroup = ProjectGroup; + return ProjectGroup; +}; \ No newline at end of file diff --git a/api/app/lib/routes/project/index.js b/api/app/lib/routes/project/index.js index aa3a5a7..ec18d9e 100644 --- a/api/app/lib/routes/project/index.js +++ b/api/app/lib/routes/project/index.js @@ -2,6 +2,7 @@ const project = require('../../controllers/project'); const projectBind = require('../../controllers/project/bind') +const projectGroup = require('../../controllers/project/group') module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/project/app_list'] = { content: '获取应用列表', visible: true }; @@ -27,4 +28,15 @@ module.exports = function (app, router, opts) { app.fs.api.logAttr['GET/project/structure'] = { content: '获取绑定项目下结构物', visible: true }; router.get('/project/structure', project.strucWithPomsProject); + + // + + app.fs.api.logAttr['GET/project/group'] = { content: '获取项目分组', visible: true }; + router.get('/project/group', projectGroup.groupList); + + app.fs.api.logAttr['PUT/project/group'] = { content: '编辑项目分组', visible: true }; + router.put('/project/group', projectGroup.editGroup); + + app.fs.api.logAttr['DEL/project/group'] = { content: '删除项目分组', visible: true }; + router.delete('/project/group', projectGroup.delGroup); }; \ No newline at end of file diff --git a/api/app/lib/utils/dataRange.js b/api/app/lib/utils/dataRange.js index 407c80e..e6cbfcb 100644 --- a/api/app/lib/utils/dataRange.js +++ b/api/app/lib/utils/dataRange.js @@ -28,7 +28,7 @@ module.exports = function (app, opts) { } if (pepProjectId) { // 有 特定的项目id 就按此查询 - findOption.where.id = pepProjectId + findOption.where.id = { $in: String(pepProjectId).split(',') } } else if (!isSuper) { // 还不是超管或管理员就按关联的项目id的数据范围查 findOption.where.id = { $in: correlationProject } diff --git a/api/sequelize-automate.config.js b/api/sequelize-automate.config.js index 8012e75..a4dd7af 100644 --- a/api/sequelize-automate.config.js +++ b/api/sequelize-automate.config.js @@ -33,7 +33,7 @@ module.exports = { dir: './app/lib/models', // 指定输出 models 文件的目录 typesDir: 'models', // 指定输出 TypeScript 类型定义的文件目录,只有 TypeScript / Midway 等会有类型定义 emptyDir: false, // !!! 谨慎操作 生成 models 之前是否清空 `dir` 以及 `typesDir` - tables: ['workorder'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 + tables: ['project_group'], // 指定生成哪些表的 models,如 ['user', 'user_post'];如果为 null,则忽略改属性 skipTables: [], // 指定跳过哪些表的 models,如 ['user'];如果为 null,则忽略改属性 tsNoCheck: false, // 是否添加 `@ts-nocheck` 注释到 models 文件中 ignorePrefix: [], // 生成的模型名称忽略的前缀,因为 项目中有以下表名是以 t_ 开头的,在实际模型中不需要, 可以添加多个 [ 't_data_', 't_',] ,长度较长的 前缀放前面 diff --git a/web/client/src/app.jsx b/web/client/src/app.jsx index b11c920..6d3cfa1 100644 --- a/web/client/src/app.jsx +++ b/web/client/src/app.jsx @@ -14,14 +14,15 @@ import Service from './sections/service'; import WorkOrder from './sections/workOrder'; import Means from './sections/means'; import Data from './sections/data'; +import ProjectGroup from './sections/projectGroup'; const App = props => { - const { projectName } = props + const { projectName } = props - useEffect(() => { - document.title = projectName; + useEffect(() => { + document.title = projectName; - console.log(` + console.log(` _ _        />  フ       |  _  _ l @@ -33,17 +34,18 @@ const App = props => {  | ( ̄ヽ__ヽ_)__)  \二つ ​ `); - }, []) + }, []) - return ( - - ) + return ( + + ) } export default App; \ No newline at end of file diff --git a/web/client/src/layout/components/header/components/customProjGroupModal.jsx b/web/client/src/layout/components/header/components/customProjGroupModal.jsx new file mode 100644 index 0000000..d660527 --- /dev/null +++ b/web/client/src/layout/components/header/components/customProjGroupModal.jsx @@ -0,0 +1,80 @@ +"use strict"; +import React, { useEffect, useState, useRef } from 'react' +import { connect, createStore } from "react-redux"; +import Immutable from 'immutable'; +import { SplitButtonGroup, Dropdown, Button, Nav, Avatar, Input, useFormApi, Form, Modal } from '@douyinfe/semi-ui'; +import { IconTreeTriangleDown, IconSearch, IconPlus } from '@douyinfe/semi-icons'; +import "../index.less"; + +const { Option } = Form.Select; + +const CustomProjGroupModal = (props) => { + const { visible, cancel, editData, pomsList, dispatch, actions } = props + const form = useRef(); + + return ( + { + e.preventDefault() + form.current.validate() + .then(values => { + console.log(values); + let stoData = { + ...values, + } + if (editData) { + stoData.id = editData.id + } + dispatch(actions.projectGroupAC.editProjectGroup(stoData)).then((res) => { + if (res.success) { + cancel({ refresh: true }) + form.current?.reset() + } + }) + }) + }} + onCancel={(e) => { + e.preventDefault() + form.current?.reset() + cancel() + }} + closeOnEsc={true} + > +
{ + form.current = formApi + if (editData) { + setTimeout(() => { + formApi.setValues(editData) + }, 0); + } + }}> + {({ formState, values, formApi }) => ( + <> + + + { + pomsList.map((item, index) => { + return ( + + ) + }) + } + + + )} + +
+ ) +} + +function mapStateToProps (state) { + const { global, auth } = state; + return { + actions: global.actions, + user: auth.user, + }; +} + +export default connect(mapStateToProps)(CustomProjGroupModal); diff --git a/web/client/src/layout/components/header/index.jsx b/web/client/src/layout/components/header/index.jsx index 275f356..43e4b47 100644 --- a/web/client/src/layout/components/header/index.jsx +++ b/web/client/src/layout/components/header/index.jsx @@ -3,20 +3,27 @@ import React, { useEffect, useState } from 'react' import { connect, createStore } from "react-redux"; import Immutable from 'immutable'; import { pepProject } from '../../actions/global'; -import { SplitButtonGroup, Dropdown, Button, Nav, Avatar, Input, Tooltip, Tabs, TabPane } from '@douyinfe/semi-ui'; -import { IconTreeTriangleDown, IconSearch } from '@douyinfe/semi-icons'; +import { SplitButtonGroup, Dropdown, Button, Nav, Avatar, Input, Tooltip, Tabs, TabPane, Space, Popconfirm, Modal } from '@douyinfe/semi-ui'; +import { IconTreeTriangleDown, IconSearch, IconEdit, IconDelete } from '@douyinfe/semi-icons'; +import CustomProjGroupModal from './components/customProjGroupModal' import PerfectScrollbar from "perfect-scrollbar"; import "./index.less"; let newScrollbar; const Header = (props) => { - const { dispatch, history, user, actions, socket, headerItems, tochange } = props; - const { install } = actions + const { dispatch, history, user, actions, socket, headerItems, tochange, projectGroup } = props; + const { install, projectGroup: projectGroupAC } = actions const [pomsList, setPomsList] = useState([]) const [pomsName, setPomsName] = useState('全局') const [pepProjectId, setPepProjectId] = useState() const [keyword, setKeyword] = useState('') const [Scrollbar, setScrollbar] = useState(false) + const [prjDropdownVis, setPrjDropdownVis] = useState(false) + const [prjDropDownTabKey, setPrjDropDownTabKey] = useState('项目') + const [customProjGroupModalVis, setCustomProjGroupModalVis] = useState(false) + const [customProjGroupDelPopVis, setCustomProjGroupDelPopVis] = useState(false) + const [customProjGroupEditData, setCustomProjGroupEditData] = useState(null) + let userRole = user?.pomsUserInfo?.role let modalRole = [] if (userRole) { @@ -33,6 +40,10 @@ const Header = (props) => { if (userRole?.includes('SuperAdmin') || userRole?.includes('admin')) modalRole = headerItems } + const getProjGroup = () => { + dispatch(projectGroupAC.getProjectGroup()) + } + useEffect(() => { if (JSON.parse(sessionStorage.getItem('pomsUser'))?.token) { dispatch(install.getProjectPoms({ global: 1 })).then((res) => { //获取已绑定项目 @@ -41,6 +52,7 @@ const Header = (props) => { setPomsList(data) } }) + getProjGroup() } }, []) @@ -60,7 +72,15 @@ const Header = (props) => { dispatch(pepProject(pepProjectId)) }, [pepProjectId]) - + const semiPortalZindex = () => { + const semiPortal = document.getElementsByClassName('semi-portal') + for (let sp of semiPortal) { + if (sp.style.zIndex == 1060) { + sp.style.zIndex = 990 + break + } + } + } return ( <>
@@ -86,7 +106,11 @@ const Header = (props) => { logo: ( { + // history.push('/projectGroup/static') + window.open('/projectGroup/static', '_blank'); + }} /> ), text: ( @@ -97,26 +121,42 @@ const Header = (props) => { setScrollbar(!Scrollbar) setKeyword('') }} - clickToHide={true} + // trigger="click" + // clickToHide={true} + trigger={'custom'} + visible={prjDropdownVis} + onClickOutSide={(e) => { + if (customProjGroupModalVis || customProjGroupDelPopVis) { + return + } + setPrjDropdownVis(false) + }} + position="rightBottom" + stopPropagation={true} render={ - - + + setPrjDropDownTabKey(v) + } + >
- } onChange={(v) => setKeyword(v)} showClear onClick={(e)=>e.stopPropagation()}> + } onChange={(v) => setKeyword(v)} showClear onClick={(e) => e.stopPropagation()}>
{pomsList?.length > 0 ? pomsList.filter(u => u.pepProjectName?.includes(keyword))?.map(v => { return - { v.pepProjectName?.length > 15 ? {v.pepProjectName}
}>
{ setPomsName(v.pepProjectName) setPepProjectId(v.pepProjectId) + setPrjDropdownVis(false) }}> {v.pepProjectName?.length > 15 ? `${v.pepProjectName?.substr(0, 15)}` : v.pepProjectName}
@@ -125,6 +165,7 @@ const Header = (props) => { :
{ setPomsName(v.pepProjectName) setPepProjectId(v.pepProjectId) + setPrjDropdownVis(false) }}>{v.pepProjectName}
} @@ -135,11 +176,91 @@ const Header = (props) => { { setPomsName('全局') setPepProjectId('') + setPrjDropdownVis(false) }}>全局
} itemKey="全局">
+ { + setPepProjectId('') + }}>自定义分组
} itemKey="自定义分组"> +
+
+ + } onChange={(v) => setKeyword(v)} showClear onClick={(e) => e.stopPropagation()}> +
+
+ { + projectGroup.filter(u => u.name?.includes(keyword))?.map(v => { + return ( + { + e.stopPropagation() + setPomsName(v.name) + setPepProjectId(v.pomsProjectIds.join(',')) + setPrjDropdownVis(false) + }} + > + { + v.name?.length > 10 ? + {v.name}
}> + + {v.name?.substr(0, 10)}... + + + : + + {v.name} + + } + + { + e.stopPropagation() + semiPortalZindex() + setCustomProjGroupEditData(v) + setCustomProjGroupModalVis(true) + }} /> + { + e.stopPropagation() + semiPortalZindex() + setCustomProjGroupDelPopVis(v.id) + Modal.warning({ + title: '确定删除该自定义分组?', content: '此修改将不可逆', + onCancel: () => { + setCustomProjGroupDelPopVis(null) + }, + onOk: () => { + dispatch(projectGroupAC.delProjectGroup(v.id)).then((res) => { + if (res.success) { + setCustomProjGroupDelPopVis(null) + + getProjGroup() + } + }) + }, + style: { zIndex: 1090 } + }); + }} /> + + + + ) + }) + } +
+ - } trigger="click" position="bottomRight"> + } + > - + @@ -176,7 +297,6 @@ const Header = (props) => { { tochange(item) }} /> - ) } }) : ""} @@ -253,17 +373,32 @@ const Header = (props) => { } /> + { + { + setCustomProjGroupModalVis(false) + setCustomProjGroupEditData(null) + if (refresh) { + getProjGroup() + } + }} + /> + } ); }; function mapStateToProps (state) { - const { global, auth, webSocket } = state; + const { global, auth, webSocket, projectGroup } = state; return { actions: global.actions, user: auth.user, socket: webSocket.socket, + projectGroup: projectGroup.data || [] }; } diff --git a/web/client/src/layout/components/header/index.less b/web/client/src/layout/components/header/index.less index 11a6811..0e23a40 100644 --- a/web/client/src/layout/components/header/index.less +++ b/web/client/src/layout/components/header/index.less @@ -1,18 +1,77 @@ -#top-slider{ - .semi-navigation-item-text{ - font-size: 13px; - color: #F2F3F5; - } - .semi-navigation-item-icon{ - color: #F2F3F5; - } - .semi-navigation-item-selected{ - background: none; - } - .semi-navigation-item{ - margin: 0px; - } - .semi-navigation-item-text{ - overflow: inherit; - } +#top-slider { + .semi-navigation-item-text { + font-size: 13px; + color: #F2F3F5; + } + + .semi-navigation-item-icon { + color: #F2F3F5; + } + + .semi-navigation-item-selected { + background: none; + } + + .semi-navigation-item { + margin: 0px; + } + + .semi-navigation-item-text { + overflow: inherit; + } + + +} + +.customGroupPop { + .customGroupItem { + position: relative; + + .edit-icon, + .del-icon { + display: none; + cursor: pointer; + } + + .del-icon { + color: red; + } + + &:hover .edit-icon, + &:hover .del-icon { + display: inline-block; + opacity: 1; + } + + &:hover .edit-icon { + animation: fadeIn 0.3s ease-in-out; + } + + &:hover .del-icon { + animation: slideIn 0.3s ease-in-out; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + + to { + display: inline-block; + opacity: 1; + } + } + + @keyframes slideIn { + from { + transform: translateX(-10px); + opacity: 0; + } + + to { + transform: translateX(0); + opacity: 1; + } + } + } } \ No newline at end of file diff --git a/web/client/src/sections/install/actions/system.js b/web/client/src/sections/install/actions/system.js index e1ca7b9..da4a446 100644 --- a/web/client/src/sections/install/actions/system.js +++ b/web/client/src/sections/install/actions/system.js @@ -3,79 +3,79 @@ import { ApiTable, basicAction } from '$utils' export function getProjectPoms (query) {//获取已绑定项目 - return (dispatch) => basicAction({ - type: "get", - dispatch: dispatch, - actionType: "GET_PROJECT_POMS", - query: query, - url: `${ApiTable.getProjectPoms}`, - msg: { option: "获取已绑定项目" }, - reducer: { name: "ProjectPoms", params: { noClear: true } }, - }); + return (dispatch) => basicAction({ + type: "get", + dispatch: dispatch, + actionType: "GET_PROJECT_POMS", + query: query, + url: `${ApiTable.getProjectPoms}`, + msg: { option: "获取已绑定项目" }, + reducer: { name: "ProjectPoms", params: { noClear: true } }, + }); } export function getProjectAnxincloud (query) {//获取安心云项目 - return (dispatch) => basicAction({ - type: "get", - dispatch: dispatch, - actionType: "GET_PROJECT_ANXINCLOUD", - query: query, - url: `${ApiTable.getProjectAnxincloud}`, - msg: { option: "获取安心云项目" }, - reducer: { name: "ProjectAnxincloud", params: { noClear: true } }, - }); + return (dispatch) => basicAction({ + type: "get", + dispatch: dispatch, + actionType: "GET_PROJECT_ANXINCLOUD", + query: query, + url: `${ApiTable.getProjectAnxincloud}`, + msg: { option: "获取安心云项目" }, + reducer: { name: "ProjectAnxincloud", params: { noClear: true } }, + }); } export function getProjectPmanage (query) {//获取PEP项目管理项目 - return (dispatch) => basicAction({ - type: "get", - dispatch: dispatch, - actionType: "GET_PROJECT_PMANAGE", - query: query, - url: `${ApiTable.getProjectPmanage}`, - msg: { option: "获取PEP项目管理项目" }, - reducer: { name: "ProjectPmanage", params: { noClear: true } }, - }); + return (dispatch) => basicAction({ + type: "get", + dispatch: dispatch, + actionType: "GET_PROJECT_PMANAGE", + query: query, + url: `${ApiTable.getProjectPmanage}`, + msg: { option: "获取PEP项目管理项目" }, + reducer: { name: "ProjectPmanage", params: { noClear: true } }, + }); } export function getProjectAppList (query) {//获取应用列表 - return (dispatch) => basicAction({ - type: "get", - dispatch: dispatch, - actionType: "GET_PROJECT_APPLIST", - query: query, - url: `${ApiTable.getProjectAppList}`, - msg: { option: "获取应用列表" }, - reducer: { name: "ProjectAppList", params: { noClear: true } }, - }); + return (dispatch) => basicAction({ + type: "get", + dispatch: dispatch, + actionType: "GET_PROJECT_APPLIST", + query: query, + url: `${ApiTable.getProjectAppList}`, + msg: { option: "获取应用列表" }, + reducer: { name: "ProjectAppList", params: { noClear: true } }, + }); } export function postProjectBind (data) {//绑定安心云、项目管理项目 - let msg = '' - if (data) { - msg = data.msg - } - return (dispatch) => - basicAction({ - type: "post", - dispatch: dispatch, - data, - actionType: "POST_PROJECT_BIND", - url: `${ApiTable.postProjectBind}`, - msg: { option: msg }, - reducer: { name: "" }, - }); + let msg = '' + if (data) { + msg = data.msg + } + return (dispatch) => + basicAction({ + type: "post", + dispatch: dispatch, + data, + actionType: "POST_PROJECT_BIND", + url: `${ApiTable.postProjectBind}`, + msg: { option: msg }, + reducer: { name: "" }, + }); } export function deleteProjectBind (data) {//删除安心云、项目管理项目绑定关系 - let bindId = '' - let msg = '' - if (data) { - bindId = data.bindId - msg = data.msg - } - return (dispatch) => - basicAction({ - type: "del", - dispatch: dispatch, - actionType: "DEL_PROJECT_BIND", - url: `${ApiTable.deleteProjectBind.replace("{bindId}", bindId)}`, - msg: { option: msg }, //删除安心云、项目管理项目绑定关系 - reducer: {}, - }); -} \ No newline at end of file + let bindId = '' + let msg = '' + if (data) { + bindId = data.bindId + msg = data.msg + } + return (dispatch) => + basicAction({ + type: "del", + dispatch: dispatch, + actionType: "DEL_PROJECT_BIND", + url: `${ApiTable.deleteProjectBind.replace("{bindId}", bindId)}`, + msg: { option: msg }, //删除安心云、项目管理项目绑定关系 + reducer: {}, + }); +} diff --git a/web/client/src/sections/projectGroup/actions/group.js b/web/client/src/sections/projectGroup/actions/group.js new file mode 100644 index 0000000..c84e90c --- /dev/null +++ b/web/client/src/sections/projectGroup/actions/group.js @@ -0,0 +1,35 @@ +'use strict'; + +import { ApiTable, basicAction } from '$utils' + +export function getProjectGroup () { + return (dispatch) => basicAction({ + type: "get", + dispatch: dispatch, + actionType: "GET_PROJECT_GROPUP", + url: `${ApiTable.projectGroup}`, + msg: { error: "获取项目分组失败" }, + reducer: { name: "projectGroup", params: { noClear: true } }, + }); +} + +export function editProjectGroup (data) { + return (dispatch) => basicAction({ + type: "put", + data, + dispatch: dispatch, + actionType: "EDIT_PROJECT_GROPUP", + url: `${ApiTable.projectGroup}`, + msg: { option: (data?.id ? '编辑' : '添加') + "项目分组" }, + }); +} + +export function delProjectGroup (id) { + return (dispatch) => basicAction({ + type: "del", + dispatch: dispatch, + actionType: "DEL_PROJECT_GROPUP", + url: `${ApiTable.projectGroup}?groupId=${id}`, + msg: { option: "删除项目分组" }, + }); +} \ No newline at end of file diff --git a/web/client/src/sections/projectGroup/actions/index.js b/web/client/src/sections/projectGroup/actions/index.js new file mode 100644 index 0000000..5c08b2d --- /dev/null +++ b/web/client/src/sections/projectGroup/actions/index.js @@ -0,0 +1,7 @@ +'use strict'; + +import * as group from './group' + +export default { + ...group +} \ No newline at end of file diff --git a/web/client/src/sections/projectGroup/containers/index.js b/web/client/src/sections/projectGroup/containers/index.js new file mode 100644 index 0000000..155d0b1 --- /dev/null +++ b/web/client/src/sections/projectGroup/containers/index.js @@ -0,0 +1,4 @@ +'use strict'; +import Static from './static' + +export { Static }; \ No newline at end of file diff --git a/web/client/src/sections/projectGroup/containers/static.jsx b/web/client/src/sections/projectGroup/containers/static.jsx new file mode 100644 index 0000000..83ec969 --- /dev/null +++ b/web/client/src/sections/projectGroup/containers/static.jsx @@ -0,0 +1,23 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { connect } from 'react-redux'; +import { Skeleton, Button, Pagination, Form, Popconfirm, Table, Toast } from '@douyinfe/semi-ui'; +import moment from "moment"; + +const Static = (props) => { + + return ( +
+ +
+ ) +} + +function mapStateToProps (state) { + const { auth, global, } = state; + return { + user: auth.user, + actions: global.actions, + }; +} + +export default connect(mapStateToProps)(Static); diff --git a/web/client/src/sections/projectGroup/index.js b/web/client/src/sections/projectGroup/index.js new file mode 100644 index 0000000..799cdfa --- /dev/null +++ b/web/client/src/sections/projectGroup/index.js @@ -0,0 +1,15 @@ +'use strict'; + +import reducers from './reducers'; +import routes from './routes'; +import actions from './actions'; +import { getNavItem } from './nav-item'; + +export default { + key: 'projectGroup', + name: '项目分组', + reducers: reducers, + routes: routes, + actions: actions, + getNavItem: getNavItem +}; \ No newline at end of file diff --git a/web/client/src/sections/projectGroup/nav-item.jsx b/web/client/src/sections/projectGroup/nav-item.jsx new file mode 100644 index 0000000..1ede9db --- /dev/null +++ b/web/client/src/sections/projectGroup/nav-item.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { IconCode } from '@douyinfe/semi-icons'; + +export function getNavItem (user, dispatch) { + return ( + [ + + ] + ); +} \ No newline at end of file diff --git a/web/client/src/sections/projectGroup/reducers/index.js b/web/client/src/sections/projectGroup/reducers/index.js new file mode 100644 index 0000000..7ed1088 --- /dev/null +++ b/web/client/src/sections/projectGroup/reducers/index.js @@ -0,0 +1,5 @@ +'use strict'; + +export default { + +} \ No newline at end of file diff --git a/web/client/src/sections/projectGroup/routes.js b/web/client/src/sections/projectGroup/routes.js new file mode 100644 index 0000000..4dc94b3 --- /dev/null +++ b/web/client/src/sections/projectGroup/routes.js @@ -0,0 +1,11 @@ +import { Static } from './containers'; + +export default [{ + type: 'outer', + route: { + path: '/projectGroup/static', + key: 'projectGroup', + breadcrumb: '项目集', + component: Static, + } +}]; \ No newline at end of file diff --git a/web/client/src/sections/projectGroup/style.less b/web/client/src/sections/projectGroup/style.less new file mode 100644 index 0000000..e69de29 diff --git a/web/client/src/utils/webapi.js b/web/client/src/utils/webapi.js index 60f33ec..dbb5801 100644 --- a/web/client/src/utils/webapi.js +++ b/web/client/src/utils/webapi.js @@ -33,6 +33,8 @@ export const ApiTable = { getProjectAppList: 'project/app_list',//获取应用列表 deleteProjectBind: 'project/bind/{bindId}',//删除安心云、项目管理项目绑定关系 + //项目分组 + projectGroup: 'project/group', //告警 getProjectPoms: 'project/poms', //获取已绑定项目