Commit d2dbb824 authored by wenmo's avatar wenmo

报警实例和报警组crud

parent 10bc97a6
package com.dlink.controller;
import com.dlink.common.result.ProTableResult;
import com.dlink.common.result.Result;
import com.dlink.model.AlertGroup;
import com.dlink.service.AlertGroupService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* AlertGroupController
*
* @author wenmo
* @since 2022/2/24 20:02
**/
@Slf4j
@RestController
@RequestMapping("/api/alertGroup")
public class AlertGroupController {
@Autowired
private AlertGroupService alertGroupService;
/**
* 新增或者更新
*/
@PutMapping
public Result saveOrUpdate(@RequestBody AlertGroup alertGroup) throws Exception {
if(alertGroupService.saveOrUpdate(alertGroup)){
return Result.succeed("新增成功");
}else {
return Result.failed("新增失败");
}
}
/**
* 动态查询列表
*/
@PostMapping
public ProTableResult<AlertGroup> listAlertGroups(@RequestBody JsonNode para) {
return alertGroupService.selectForProTable(para);
}
/**
* 批量删除
*/
@DeleteMapping
public Result deleteMul(@RequestBody JsonNode para) {
if (para.size()>0){
List<Integer> error = new ArrayList<>();
for (final JsonNode item : para){
Integer id = item.asInt();
if(!alertGroupService.removeById(id)){
error.add(id);
}
}
if(error.size()==0) {
return Result.succeed("删除成功");
}else {
return Result.succeed("删除部分成功,但"+error.toString()+"删除失败,共"+error.size()+"次失败。");
}
}else{
return Result.failed("请选择要删除的记录");
}
}
/**
* 获取指定ID的信息
*/
@PostMapping("/getOneById")
public Result getOneById(@RequestBody AlertGroup alertGroup) throws Exception {
alertGroup = alertGroupService.getById(alertGroup.getId());
return Result.succeed(alertGroup,"获取成功");
}
}
package com.dlink.controller;
import com.dlink.common.result.ProTableResult;
import com.dlink.common.result.Result;
import com.dlink.model.AlertHistory;
import com.dlink.service.AlertHistoryService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* AlertHistoryController
*
* @author wenmo
* @since 2022/2/24 20:43
**/
@Slf4j
@RestController
@RequestMapping("/api/alertHistory")
public class AlertHistoryController {
@Autowired
private AlertHistoryService alertHistoryService;
/**
* 新增或者更新
*/
@PutMapping
public Result saveOrUpdate(@RequestBody AlertHistory alertHistory) throws Exception {
if(alertHistoryService.saveOrUpdate(alertHistory)){
return Result.succeed("新增成功");
}else {
return Result.failed("新增失败");
}
}
/**
* 动态查询列表
*/
@PostMapping
public ProTableResult<AlertHistory> listAlertHistorys(@RequestBody JsonNode para) {
return alertHistoryService.selectForProTable(para);
}
/**
* 批量删除
*/
@DeleteMapping
public Result deleteMul(@RequestBody JsonNode para) {
if (para.size()>0){
List<Integer> error = new ArrayList<>();
for (final JsonNode item : para){
Integer id = item.asInt();
if(!alertHistoryService.removeById(id)){
error.add(id);
}
}
if(error.size()==0) {
return Result.succeed("删除成功");
}else {
return Result.succeed("删除部分成功,但"+error.toString()+"删除失败,共"+error.size()+"次失败。");
}
}else{
return Result.failed("请选择要删除的记录");
}
}
/**
* 获取指定ID的信息
*/
@PostMapping("/getOneById")
public Result getOneById(@RequestBody AlertHistory alertHistory) throws Exception {
alertHistory = alertHistoryService.getById(alertHistory.getId());
return Result.succeed(alertHistory,"获取成功");
}
}
package com.dlink.controller;
import com.dlink.common.result.ProTableResult;
import com.dlink.common.result.Result;
import com.dlink.model.AlertInstance;
import com.dlink.service.AlertInstanceService;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* AlertInstanceController
*
* @author wenmo
* @since 2022/2/24 19:54
**/
@Slf4j
@RestController
@RequestMapping("/api/alertInstance")
public class AlertInstanceController {
@Autowired
private AlertInstanceService alertInstanceService;
/**
* 新增或者更新
*/
@PutMapping
public Result saveOrUpdate(@RequestBody AlertInstance alertInstance) throws Exception {
if(alertInstanceService.saveOrUpdate(alertInstance)){
return Result.succeed("新增成功");
}else {
return Result.failed("新增失败");
}
}
/**
* 动态查询列表
*/
@PostMapping
public ProTableResult<AlertInstance> listAlertInstances(@RequestBody JsonNode para) {
return alertInstanceService.selectForProTable(para);
}
/**
* 批量删除
*/
@DeleteMapping
public Result deleteMul(@RequestBody JsonNode para) {
if (para.size()>0){
List<Integer> error = new ArrayList<>();
for (final JsonNode item : para){
Integer id = item.asInt();
if(!alertInstanceService.removeById(id)){
error.add(id);
}
}
if(error.size()==0) {
return Result.succeed("删除成功");
}else {
return Result.succeed("删除部分成功,但"+error.toString()+"删除失败,共"+error.size()+"次失败。");
}
}else{
return Result.failed("请选择要删除的记录");
}
}
/**
* 获取指定ID的信息
*/
@PostMapping("/getOneById")
public Result getOneById(@RequestBody AlertInstance alertInstance) throws Exception {
alertInstance = alertInstanceService.getById(alertInstance.getId());
return Result.succeed(alertInstance,"获取成功");
}
}
package com.dlink.mapper;
import com.dlink.db.mapper.SuperMapper;
import com.dlink.model.AlertGroup;
import org.apache.ibatis.annotations.Mapper;
/**
* AlertGroupMapper
*
* @author wenmo
* @since 2022/2/24 19:59
**/
@Mapper
public interface AlertGroupMapper extends SuperMapper<AlertGroup> {
}
package com.dlink.mapper;
import com.dlink.db.mapper.SuperMapper;
import com.dlink.model.AlertHistory;
import org.apache.ibatis.annotations.Mapper;
/**
* AlertHistoryMapper
*
* @author wenmo
* @since 2022/2/24 20:32
**/
@Mapper
public interface AlertHistoryMapper extends SuperMapper<AlertHistory> {
}
package com.dlink.mapper;
import com.dlink.db.mapper.SuperMapper;
import com.dlink.model.AlertInstance;
import org.apache.ibatis.annotations.Mapper;
/**
* AlertInstanceMapper
*
* @author wenmo
* @since 2022/2/24 19:48
**/
@Mapper
public interface AlertInstanceMapper extends SuperMapper<AlertInstance> {
}
...@@ -13,5 +13,4 @@ import org.apache.ibatis.annotations.Param; ...@@ -13,5 +13,4 @@ import org.apache.ibatis.annotations.Param;
**/ **/
@Mapper @Mapper
public interface CatalogueMapper extends SuperMapper<Catalogue> { public interface CatalogueMapper extends SuperMapper<Catalogue> {
Catalogue findByParentIdAndName(@Param("parent_id")Integer parent_id, @Param("name")String name);
} }
package com.dlink.model;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dlink.db.model.SuperEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AlertGroup
*
* @author wenmo
* @since 2022/2/24 19:58
**/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("dlink_alert_group")
public class AlertGroup extends SuperEntity {
private static final long serialVersionUID = 7027411164191682344L;
private String alertInstanceIds;
private String note;
}
package com.dlink.model;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* AlertHistory
*
* @author wenmo
* @since 2022/2/24 20:12
**/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("dlink_alert_history")
public class AlertHistory implements Serializable {
private static final long serialVersionUID = -7904869940473678282L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String alertGroupId;
private String jobInstanceId;
private String title;
private String content;
private Integer status;
private String log;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
package com.dlink.model;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dlink.db.model.SuperEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AlertInstance
*
* @author wenmo
* @since 2022/2/24 19:46
**/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("dlink_alert_instance")
public class AlertInstance extends SuperEntity {
private static final long serialVersionUID = -3435401513220527001L;
private String type;
private String params;
}
package com.dlink.model; package com.dlink.model;
import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
...@@ -22,19 +24,31 @@ public class JobInstance implements Serializable { ...@@ -22,19 +24,31 @@ public class JobInstance implements Serializable {
private static final long serialVersionUID = -3410230507904303730L; private static final long serialVersionUID = -3410230507904303730L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id; private Integer id;
private String name; private String name;
private Integer taskId; private Integer taskId;
private Integer clusterId; private Integer clusterId;
private String jid; private String jid;
private String status; private String status;
private Integer historyId; private Integer historyId;
private String error; private String error;
@TableField(fill = FieldFill.INSERT) @TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime; private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) @TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime; private LocalDateTime updateTime;
private LocalDateTime finishTime; private LocalDateTime finishTime;
private Integer failed_restart_count; private Integer failed_restart_count;
} }
package com.dlink.service;
import com.dlink.db.service.ISuperService;
import com.dlink.model.AlertGroup;
/**
* AlertGroupService
*
* @author wenmo
* @since 2022/2/24 20:00
**/
public interface AlertGroupService extends ISuperService<AlertGroup> {
}
package com.dlink.service;
import com.dlink.db.service.ISuperService;
import com.dlink.model.AlertHistory;
/**
* AlertHistoryService
*
* @author wenmo
* @since 2022/2/24 20:42
**/
public interface AlertHistoryService extends ISuperService<AlertHistory> {
}
package com.dlink.service;
import com.dlink.db.service.ISuperService;
import com.dlink.model.AlertInstance;
/**
* AlertInstanceService
*
* @author wenmo
* @since 2022/2/24 19:52
**/
public interface AlertInstanceService extends ISuperService<AlertInstance> {
}
...@@ -16,9 +16,13 @@ import java.util.List; ...@@ -16,9 +16,13 @@ import java.util.List;
public interface CatalogueService extends ISuperService<Catalogue> { public interface CatalogueService extends ISuperService<Catalogue> {
List<Catalogue> getAllData(); List<Catalogue> getAllData();
Catalogue findByParentIdAndName(Integer parent_id, String name); Catalogue findByParentIdAndName(Integer parent_id, String name);
Catalogue createCatalogueAndTask(CatalogueTaskDTO catalogueTaskDTO); Catalogue createCatalogueAndTask(CatalogueTaskDTO catalogueTaskDTO);
Catalogue createCatalogAndFileTask(CatalogueTaskDTO catalogueTaskDTO, String ment); Catalogue createCatalogAndFileTask(CatalogueTaskDTO catalogueTaskDTO, String ment);
boolean toRename(Catalogue catalogue); boolean toRename(Catalogue catalogue);
boolean removeCatalogueAndTaskById(Integer id); boolean removeCatalogueAndTaskById(Integer id);
......
package com.dlink.service.impl;
import com.dlink.db.service.impl.SuperServiceImpl;
import com.dlink.mapper.AlertGroupMapper;
import com.dlink.model.AlertGroup;
import com.dlink.service.AlertGroupService;
import org.springframework.stereotype.Service;
/**
* AlertGroupServiceImpl
*
* @author wenmo
* @since 2022/2/24 20:01
**/
@Service
public class AlertGroupServiceImpl extends SuperServiceImpl<AlertGroupMapper, AlertGroup> implements AlertGroupService {
}
package com.dlink.service.impl;
import com.dlink.db.service.impl.SuperServiceImpl;
import com.dlink.mapper.AlertHistoryMapper;
import com.dlink.model.AlertHistory;
import com.dlink.service.AlertHistoryService;
import org.springframework.stereotype.Service;
/**
* AlertHistoryServiceImpl
*
* @author wenmo
* @since 2022/2/24 20:42
**/
@Service
public class AlertHistoryServiceImpl extends SuperServiceImpl<AlertHistoryMapper, AlertHistory> implements AlertHistoryService {
}
package com.dlink.service.impl;
import com.dlink.db.service.impl.SuperServiceImpl;
import com.dlink.mapper.AlertInstanceMapper;
import com.dlink.model.AlertInstance;
import com.dlink.service.AlertInstanceService;
import org.springframework.stereotype.Service;
/**
* AlertInstanceServiceImpl
*
* @author wenmo
* @since 2022/2/24 19:53
**/
@Service
public class AlertInstanceServiceImpl extends SuperServiceImpl<AlertInstanceMapper, AlertInstance> implements AlertInstanceService {
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dlink.mapper.AlertGroupMapper">
<select id="selectForProTable" resultType="com.dlink.model.AlertGroup">
select
a.*
from
dlink_alert_group a
<where>
1=1
<if test='param.name!=null and param.name!=""'>
and a.name like "%${param.name}%"
</if>
<if test='param.createTime!=null and param.createTime!=""'>
and a.create_time <![CDATA[>=]]> str_to_date( #{param.createTime},'%Y-%m-%d %H:%i:%s')
</if>
<if test='param.updateTime!=null and param.updateTime!=""'>
and a.update_time <![CDATA[>=]]> str_to_date( #{param.updateTime},'%Y-%m-%d %H:%i:%s')
</if>
<if test='ew.sqlSegment!=null and ew.sqlSegment!="" and !ew.sqlSegment.startsWith(" ORDER BY")'>
and
</if>
<if test='ew.sqlSegment!=null and ew.sqlSegment!=""'>
${ew.sqlSegment}
</if>
</where>
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dlink.mapper.AlertHistoryMapper">
<select id="selectForProTable" resultType="com.dlink.model.AlertHistory">
select
a.*
from
dlink_alert_history a
<where>
1=1
<if test='param.createTime!=null and param.createTime!=""'>
and a.create_time <![CDATA[>=]]> str_to_date( #{param.createTime},'%Y-%m-%d %H:%i:%s')
</if>
<if test='param.updateTime!=null and param.updateTime!=""'>
and a.update_time <![CDATA[>=]]> str_to_date( #{param.updateTime},'%Y-%m-%d %H:%i:%s')
</if>
<if test='ew.sqlSegment!=null and ew.sqlSegment!="" and !ew.sqlSegment.startsWith(" ORDER BY")'>
and
</if>
<if test='ew.sqlSegment!=null and ew.sqlSegment!=""'>
${ew.sqlSegment}
</if>
</where>
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dlink.mapper.AlertInstanceMapper">
<select id="selectForProTable" resultType="com.dlink.model.AlertInstance">
select
a.*
from
dlink_alert_instance a
<where>
1=1
<if test='param.name!=null and param.name!=""'>
and a.name like "%${param.name}%"
</if>
<if test='param.createTime!=null and param.createTime!=""'>
and a.create_time <![CDATA[>=]]> str_to_date( #{param.createTime},'%Y-%m-%d %H:%i:%s')
</if>
<if test='param.updateTime!=null and param.updateTime!=""'>
and a.update_time <![CDATA[>=]]> str_to_date( #{param.updateTime},'%Y-%m-%d %H:%i:%s')
</if>
<if test='ew.sqlSegment!=null and ew.sqlSegment!="" and !ew.sqlSegment.startsWith(" ORDER BY")'>
and
</if>
<if test='ew.sqlSegment!=null and ew.sqlSegment!=""'>
${ew.sqlSegment}
</if>
</where>
</select>
</mapper>
...@@ -309,7 +309,7 @@ create table dlink_job_instance ...@@ -309,7 +309,7 @@ create table dlink_job_instance
( (
id int auto_increment comment '自增主键' id int auto_increment comment '自增主键'
primary key, primary key,
name varchar(50) null comment '作业实例名', name varchar(255) null comment '作业实例名',
task_id int null comment 'taskID', task_id int null comment 'taskID',
cluster_id int null comment '集群ID', cluster_id int null comment '集群ID',
jid varchar(50) null comment 'FlinkJobId', jid varchar(50) null comment 'FlinkJobId',
......
...@@ -587,5 +587,9 @@ create table dlink_alert_history ...@@ -587,5 +587,9 @@ create table dlink_alert_history
) )
comment 'Alert历史'; comment 'Alert历史';
-- ----------------------------
-- 0.6.0-SNAPSHOT 2022-02-25
-- ----------------------------
ALTER TABLE `dlink_job_instance` MODIFY COLUMN name varchar(255) NULL COMMENT '作业实例名';
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;
...@@ -66,6 +66,24 @@ export default [ ...@@ -66,6 +66,24 @@ export default [
component: './DataBase', component: './DataBase',
}, },
{ {
path: '/registration/alert',
name: 'alert',
icon: 'alert',
routes: [
{
path: '/registration/alert/alertInstance',
name: 'alertInstance',
icon: 'task',
component: './AlertInstance',
},
{
path: '/registration/alert/alertGroup',
name: 'alertGroup',
icon: 'task',
component: './AlertGroup',
},
],
},{
path: '/registration/document', path: '/registration/document',
name: 'document', name: 'document',
icon: 'container', icon: 'container',
......
...@@ -54,6 +54,9 @@ export default { ...@@ -54,6 +54,9 @@ export default {
'menu.registration.cluster': '集群实例', 'menu.registration.cluster': '集群实例',
'menu.registration.clusterConfiguration': '集群配置', 'menu.registration.clusterConfiguration': '集群配置',
'menu.registration.database': '数据源管理', 'menu.registration.database': '数据源管理',
'menu.registration.alert': '报警管理',
'menu.registration.alert.alertInstance': '报警实例管理',
'menu.registration.alert.alertGroup': '报警组管理',
'menu.studio': 'FlinkSql IDE', 'menu.studio': 'FlinkSql IDE',
'menu.flinksqlstudio': 'FlinkSQL Studio', 'menu.flinksqlstudio': 'FlinkSQL Studio',
'menu.taskcenter': '作业中心', 'menu.taskcenter': '作业中心',
......
import React, {useEffect, useState} from 'react';
import {Form, Button, Input, Modal, Select,Divider,Switch} from 'antd';
import {AlertGroupTableListItem} from "@/pages/AlertGroup/data";
export type AlertGroupFormProps = {
onCancel: (flag?: boolean) => void;
onSubmit: (values: Partial<AlertGroupTableListItem>) => void;
modalVisible: boolean;
values: Partial<AlertGroupTableListItem>;
};
const Option = Select.Option;
const formLayout = {
labelCol: {span: 7},
wrapperCol: {span: 13},
};
const AlertGroupForm: React.FC<AlertGroupFormProps> = (props) => {
const [form] = Form.useForm();
const [formVals, setFormVals] = useState<Partial<AlertGroupTableListItem>>({
id: props.values.id,
name: props.values.name,
alertInstanceIds: props.values.alertInstanceIds,
note: props.values.note,
enabled: props.values.enabled?props.values.enabled:true,
});
const {
onSubmit: handleSubmit,
onCancel: handleModalVisible,
modalVisible,
} = props;
const submitForm = async () => {
const fieldsValue = await form.validateFields();
setFormVals({...formVals, ...fieldsValue});
handleSubmit({...formVals, ...fieldsValue});
};
const renderContent = (formVals) => {
return (
<>
<Form.Item
name="name"
label="标识"
rules={[{required: true, message: '请输入名称!'}]}>
<Input placeholder="请输入唯一英文标识"/>
</Form.Item>
<Form.Item
name="alertInstanceIds"
label="报警实例"
help="请选择报警组实例"
>
<Input placeholder="请选择报警实例"/>
</Form.Item>
<Form.Item
name="note"
label="注释"
>
<Input.TextArea placeholder="请输入文本注释" allowClear
autoSize={{minRows: 3, maxRows: 10}}/>
</Form.Item>
<Form.Item
name="enabled"
label="是否启用">
<Switch checkedChildren="启用" unCheckedChildren="禁用"
defaultChecked={formVals.enabled}/>
</Form.Item>
</>
);
};
const renderFooter = () => {
return (
<>
<Button onClick={() => handleModalVisible(false)}>取消</Button>
<Button type="primary" onClick={() => submitForm()}>
完成
</Button>
</>
);
};
return (
<Modal
width={1200}
bodyStyle={{padding: '32px 40px 48px'}}
destroyOnClose
title={formVals.id?"维护报警组":"创建报警组"}
visible={modalVisible}
footer={renderFooter()}
onCancel={() => handleModalVisible()}
>
<Form
{...formLayout}
form={form}
initialValues={formVals}
>
{renderContent(formVals)}
</Form>
</Modal>
);
};
export default AlertGroupForm;
export type AlertGroupTableListItem = {
id: number,
name: string,
alias: string,
alertInstanceIds: string,
note: string,
enabled: boolean,
createTime: Date,
updateTime: Date,
};
import React, {useRef, useState} from "react";
import {DownOutlined,PlusOutlined} from '@ant-design/icons';
import {ActionType, ProColumns} from "@ant-design/pro-table";
import {Button, Drawer, Modal, Dropdown, Menu} from 'antd';
import {PageContainer, FooterToolbar} from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import ProDescriptions from '@ant-design/pro-descriptions';
import {AlertGroupTableListItem} from "@/pages/AlertGroup/data";
import {handleAddOrUpdate, handleRemove, queryData, updateEnabled} from "@/components/Common/crud";
import AlertGroupForm from "@/pages/AlertGroup/components/AlertGroupForm";
const url = '/api/alertGroup';
const AlertGroupTableList: React.FC<{}> = (props: any) => {
const {dispatch} = props;
const [row, setRow] = useState<AlertGroupTableListItem>();
const [modalVisible, handleModalVisible] = useState<boolean>(false);
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [formValues, setFormValues] = useState({});
const actionRef = useRef<ActionType>();
const [selectedRowsState, setSelectedRows] = useState<AlertGroupTableListItem[]>([]);
const editAndDelete = (key: string | number, currentItem: AlertGroupTableListItem) => {
if (key === 'edit') {
setFormValues(currentItem);
handleUpdateModalVisible(true);
} else if (key === 'delete') {
Modal.confirm({
title: '删除报警组配置',
content: '确定删除该报警组配置吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await handleRemove(url, [currentItem]);
actionRef.current?.reloadAndRest?.();
}
});
}
};
const MoreBtn: React.FC<{
item: AlertGroupTableListItem;
}> = ({item}) => (
<Dropdown
overlay={
<Menu onClick={({key}) => editAndDelete(key, item)}>
<Menu.Item key="edit">编辑</Menu.Item>
<Menu.Item key="delete">删除</Menu.Item>
</Menu>
}
>
<a>
更多 <DownOutlined/>
</a>
</Dropdown>
);
const columns: ProColumns<AlertGroupTableListItem>[] = [
{
title: '名称',
dataIndex: 'name',
tip: '名称是唯一的',
sorter: true,
formItemProps: {
rules: [
{
required: true,
message: '名称为必填项',
},
],
},
render: (dom, entity) => {
return <a onClick={() => setRow(entity)}>{dom}</a>;
},
},
{
title: '报警组ID',
dataIndex: 'id',
hideInTable: true,
hideInForm: true,
hideInSearch: true,
},
{
title: '报警实例',
sorter: true,
dataIndex: 'alertInstanceIds',
},
{
title: '注释',
sorter: true,
valueType: 'textarea',
dataIndex: 'note',
hideInForm: false,
hideInSearch: true,
hideInTable: true,
},
{
title: '是否启用',
dataIndex: 'enabled',
hideInForm: true,
hideInSearch: true,
hideInTable: false,
filters: [
{
text: '已启用',
value: 1,
},
{
text: '已禁用',
value: 0,
},
],
filterMultiple: false,
valueEnum: {
true: {text: '已启用', status: 'Success'},
false: {text: '已禁用', status: 'Error'},
},
},
{
title: '创建时间',
dataIndex: 'createTime',
sorter: true,
valueType: 'dateTime',
hideInTable: true
},
{
title: '最近更新时间',
dataIndex: 'updateTime',
sorter: true,
valueType: 'dateTime',
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => [
<a
onClick={() => {
handleUpdateModalVisible(true);
setFormValues(record);
}}
>
配置
</a>,
<MoreBtn key="more" item={record}/>,
],
},
];
return (
<PageContainer>
<ProTable<AlertGroupTableListItem>
headerTitle="报警组管理"
actionRef={actionRef}
rowKey="id"
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button type="primary" onClick={() => handleModalVisible(true)}>
<PlusOutlined/> 新建
</Button>,
]}
request={(params, sorter, filter) => queryData(url, {...params, sorter, filter})}
columns={columns}
rowSelection={{
onChange: (_, selectedRows) => setSelectedRows(selectedRows),
}}
/>
{selectedRowsState?.length > 0 && (
<FooterToolbar
extra={
<div>
已选择 <a style={{fontWeight: 600}}>{selectedRowsState.length}</a>&nbsp;&nbsp;
<span>
被禁用的集群配置共 {selectedRowsState.length - selectedRowsState.reduce((pre, item) => pre + (item.enabled ? 1 : 0), 0)}
</span>
</div>
}
>
<Button type="primary" danger
onClick={() => {
Modal.confirm({
title: '删除报警组',
content: '确定删除选中的报警组吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await handleRemove(url, selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
});
}}
>
批量删除
</Button>
<Button type="primary"
onClick={() => {
Modal.confirm({
title: '启用报警组',
content: '确定启用选中的报警组吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await updateEnabled(url, selectedRowsState, true);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
});
}}
>批量启用</Button>
<Button danger
onClick={() => {
Modal.confirm({
title: '禁用报警组',
content: '确定禁用选中的报警组吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await updateEnabled(url, selectedRowsState, false);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
});
}}
>批量禁用</Button>
</FooterToolbar>
)}
<AlertGroupForm
onSubmit={async (value) => {
const success = await handleAddOrUpdate(url, value);
if (success) {
handleModalVisible(false);
setFormValues({});
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
onCancel={() => {
handleModalVisible(false);
}}
modalVisible={modalVisible}
values={{}}
/>
{formValues && Object.keys(formValues).length ? (
<AlertGroupForm
onSubmit={async (value) => {
const success = await handleAddOrUpdate(url, value);
if (success) {
handleUpdateModalVisible(false);
setFormValues({});
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
onCancel={() => {
handleUpdateModalVisible(false);
setFormValues({});
}}
modalVisible={updateModalVisible}
values={formValues}
/>
): null}
<Drawer
width={600}
visible={!!row}
onClose={() => {
setRow(undefined);
}}
closable={false}
>
{row?.name && (
<ProDescriptions<AlertGroupTableListItem>
column={2}
title={row?.name}
request={async () => ({
data: row || {},
})}
params={{
id: row?.name,
}}
columns={columns}
/>
)}
</Drawer>
</PageContainer>
);
};
export default AlertGroupTableList;
import React, {useState} from 'react';
import {Modal,List,Card} from 'antd';
import {AlertInstanceTableListItem} from '../data.d';
import {connect} from "umi";
import {ALERT_CONFIG_LIST, ALERT_TYPE, AlertConfig} from "@/pages/AlertInstance/conf";
import {getAlertIcon} from "@/pages/AlertInstance/icon";
import {AlertStateType} from "@/pages/AlertInstance/model";
import DingTalkForm from "@/pages/AlertInstance/components/DingTalkForm";
import {createOrModifyAlertInstance} from "@/pages/AlertInstance/service";
import WeChatForm from "@/pages/AlertInstance/components/WeChatForm";
export type UpdateFormProps = {
onCancel: (flag?: boolean, formVals?: Partial<AlertInstanceTableListItem>) => void;
onSubmit: (values: Partial<AlertInstanceTableListItem>) => void;
modalVisible: boolean;
values: Partial<AlertInstanceTableListItem>;
};
const AlertInstanceChooseForm: React.FC<UpdateFormProps> = (props) => {
const {
onSubmit: handleUpdate,
onCancel: handleChooseModalVisible,
modalVisible,
values
} = props;
const [alertType, setAlertType] = useState<string>();
const chooseOne = (item: AlertConfig)=>{
setAlertType(item.type);
};
const onSubmit = async (value: any)=>{
const success = await createOrModifyAlertInstance(value);
if (success) {
handleChooseModalVisible();
setAlertType(undefined);
handleUpdate(value);
}
};
return (
<Modal
width={800}
bodyStyle={{padding: '32px 40px 48px'}}
title={values?.id?'编辑报警实例':'创建报警实例'}
visible={modalVisible}
onCancel={() => {
setAlertType(undefined);
handleChooseModalVisible();
}}
maskClosable = {false}
destroyOnClose = {true}
footer={null}
>{
(!alertType&&!values?.id)&&(<List
grid={{
gutter: 16,
xs: 1,
sm: 2,
md: 4,
lg: 4,
xl: 4,
xxl: 4,
}}
dataSource={ALERT_CONFIG_LIST}
renderItem={(item:AlertConfig) => (
<List.Item onClick={()=>{chooseOne(item)}}>
<Card>
{getAlertIcon(item.type)}
</Card>
</List.Item>
)}
/>)
}
{(values?.type == ALERT_TYPE.DINGTALK || alertType == ALERT_TYPE.DINGTALK)?
<DingTalkForm
onCancel={() => {
setAlertType(undefined);
handleChooseModalVisible();
}}
modalVisible={values?.type == ALERT_TYPE.DINGTALK || alertType == ALERT_TYPE.DINGTALK}
values={values}
onSubmit={(value) => {
onSubmit(value);
}}
/>:undefined
}
{(values?.type == ALERT_TYPE.WECHAT || alertType == ALERT_TYPE.WECHAT)?
<WeChatForm
onCancel={() => {
setAlertType(undefined);
handleChooseModalVisible();
}}
modalVisible={values?.type == ALERT_TYPE.WECHAT || alertType == ALERT_TYPE.WECHAT}
values={values}
onSubmit={(value) => {
onSubmit(value);
}}
/>:undefined
}
</Modal>
);
};
export default connect(({Alert}: { Alert: AlertStateType }) => ({
instance: Alert.instance,
}))(AlertInstanceChooseForm);
import React, {useEffect, useState} from 'react';
import {Form, Button, Input, Modal, Divider,Switch} from 'antd';
import {AlertInstanceTableListItem} from "@/pages/AlertInstance/data";
import {buildJSONData, getJSONData} from "@/pages/AlertInstance/function";
import {ALERT_TYPE} from "@/pages/AlertInstance/conf";
export type AlertInstanceFormProps = {
onCancel: (flag?: boolean) => void;
onSubmit: (values: Partial<AlertInstanceTableListItem>) => void;
modalVisible: boolean;
values: Partial<AlertInstanceTableListItem>;
};
const formLayout = {
labelCol: {span: 7},
wrapperCol: {span: 13},
};
const DingTalkForm: React.FC<AlertInstanceFormProps> = (props) => {
const [form] = Form.useForm();
const [formVals, setFormVals] = useState<Partial<AlertInstanceTableListItem>>({
id: props.values?.id,
name: props.values?.name,
type: ALERT_TYPE.DINGTALK,
params: props.values?.params,
enabled: props.values?.enabled,
});
const {
onSubmit: handleSubmit,
onCancel: handleModalVisible,
modalVisible,
} = props;
const onValuesChange = (change: any,all: any)=>{
setFormVals({...formVals,...change});
};
const submitForm = async () => {
const fieldsValue = await form.validateFields();
setFormVals(buildJSONData(formVals,fieldsValue));
handleSubmit(buildJSONData(formVals,fieldsValue));
};
const renderContent = (formVals) => {
return (
<>
<Divider>钉钉配置</Divider>
<Form.Item
name="name"
label="名称"
rules={[{required: true, message: '请输入名称!'}]}
>
<Input placeholder="请输入名称"/>
</Form.Item>
<Form.Item
name="webhook"
label="地址"
rules={[{required: true, message: '请输入WebHook!'}]}
>
<Input placeholder="请输入WebHook"/>
</Form.Item>
<Form.Item
name="keyword"
label="关键字"
>
<Input placeholder="请输入keyword"/>
</Form.Item>
<Form.Item
name="secret"
label="密令"
>
<Input placeholder="请输入secret"/>
</Form.Item>
<Form.Item
name="isEnableProxy"
label="开启代理">
<Switch checkedChildren="是" unCheckedChildren="否"
defaultChecked={formVals.isEnableProxy}/>
</Form.Item>
{formVals.isEnableProxy?<>
<Form.Item
name="proxy"
label="代理"
>
<Input placeholder="请输入proxy"/>
</Form.Item>
<Form.Item
name="port"
label="端口号"
>
<Input placeholder="请输入port"/>
</Form.Item>
<Form.Item
name="user"
label="用户"
>
<Input placeholder="请输入user"/>
</Form.Item>
<Form.Item
name="password"
label="密码"
>
<Input.Password placeholder="请输入password"/>
</Form.Item></>:undefined
}
<Form.Item
name="IsAtAll"
label="@所有人">
<Switch checkedChildren="启用" unCheckedChildren="禁用"
defaultChecked={formVals.IsAtAll}/>
</Form.Item>
<Form.Item
name="enabled"
label="是否启用">
<Switch checkedChildren="启用" unCheckedChildren="禁用"
defaultChecked={formVals.enabled}/>
</Form.Item>
</>
);
};
const renderFooter = () => {
return (
<>
<Button onClick={() => handleModalVisible(false)}>取消</Button>
<Button type="primary" onClick={() => submitForm()}>
完成
</Button>
</>
);
};
return (
<Modal
width={1200}
bodyStyle={{padding: '32px 40px 48px'}}
destroyOnClose
title={formVals.id?"维护报警实例配置":"创建报警实例配置"}
visible={modalVisible}
footer={renderFooter()}
onCancel={() => handleModalVisible()}
>
<Form
{...formLayout}
form={form}
initialValues={getJSONData(formVals)}
onValuesChange={onValuesChange}
>
{renderContent(getJSONData(formVals))}
</Form>
</Modal>
);
};
export default DingTalkForm;
import React, {useEffect, useState} from 'react';
import {Form, Button,Radio, Input, Modal, Divider,Switch} from 'antd';
import {AlertInstanceTableListItem} from "@/pages/AlertInstance/data";
import {buildJSONData, getJSONData} from "@/pages/AlertInstance/function";
import {ALERT_TYPE} from "@/pages/AlertInstance/conf";
export type AlertInstanceFormProps = {
onCancel: (flag?: boolean) => void;
onSubmit: (values: Partial<AlertInstanceTableListItem>) => void;
modalVisible: boolean;
values: Partial<AlertInstanceTableListItem>;
};
const formLayout = {
labelCol: {span: 7},
wrapperCol: {span: 13},
};
const WeChatForm: React.FC<AlertInstanceFormProps> = (props) => {
const [form] = Form.useForm();
const [formVals, setFormVals] = useState<Partial<AlertInstanceTableListItem>>({
id: props.values?.id,
name: props.values?.name,
type: ALERT_TYPE.WECHAT,
params: props.values?.params,
enabled: props.values?.enabled,
});
const {
onSubmit: handleSubmit,
onCancel: handleModalVisible,
modalVisible,
} = props;
const onValuesChange = (change: any,all: any)=>{
setFormVals({...formVals,...change});
};
const submitForm = async () => {
const fieldsValue = await form.validateFields();
setFormVals(buildJSONData(formVals,fieldsValue));
handleSubmit(buildJSONData(formVals,fieldsValue));
};
const renderContent = (formVals) => {
return (
<>
<Divider>微信企业号配置</Divider>
<Form.Item
name="name"
label="名称"
rules={[{required: true, message: '请输入名称!'}]}
>
<Input placeholder="请输入名称"/>
</Form.Item>
<Form.Item
name="corpId"
label="企业Id"
rules={[{required: true, message: '请输入企业Id!'}]}
>
<Input placeholder="请输入CorpId"/>
</Form.Item>
<Form.Item
name="secret"
label="密令"
rules={[{required: true, message: '请输入密令!'}]}
>
<Input placeholder="请输入secret"/>
</Form.Item>
<Form.Item
name="users"
label="用户"
rules={[{required: true, message: '请输入用户!'}]}
>
<Input placeholder="请输入用户"/>
</Form.Item>
<Form.Item
name="userSendMsg"
label="发送信息"
rules={[{required: true, message: '请输入发送信息!'}]}
>
<Input placeholder="请输入发送信息"/>
</Form.Item>
<Form.Item
name="agentId"
label="代理ID"
rules={[{required: true, message: '请输入代理ID!'}]}
>
<Input placeholder="请输入代理ID"/>
</Form.Item>
<Form.Item
name="sendType"
label="发送方式"
rules={[{required: true, message: '请输入发送方式!'}]}
>
<Radio.Group >
<Radio value='应用'>应用</Radio>
<Radio value='群聊'>群聊</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="showType"
label="展示方式"
rules={[{required: true, message: '请输入展示方式!'}]}
>
<Radio.Group >
<Radio value='table'>表格</Radio>
<Radio value='text'>文本</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="enabled"
label="是否启用">
<Switch checkedChildren="启用" unCheckedChildren="禁用"
defaultChecked={formVals.enabled}/>
</Form.Item>
</>
);
};
const renderFooter = () => {
return (
<>
<Button onClick={() => handleModalVisible(false)}>取消</Button>
<Button type="primary" onClick={() => submitForm()}>
完成
</Button>
</>
);
};
return (
<Modal
width={1200}
bodyStyle={{padding: '32px 40px 48px'}}
destroyOnClose
title={formVals.id?"维护报警实例配置":"创建报警实例配置"}
visible={modalVisible}
footer={renderFooter()}
onCancel={() => handleModalVisible()}
>
<Form
{...formLayout}
form={form}
initialValues={getJSONData(formVals)}
onValuesChange={onValuesChange}
>
{renderContent(getJSONData(formVals))}
</Form>
</Modal>
);
};
export default WeChatForm;
export type AlertConfig = {
type: string,
}
export const ALERT_TYPE = {
DINGTALK:'DingTalk',
WECHAT:'WeChat',
};
export const ALERT_CONFIG_LIST: AlertConfig[] = [{
type: ALERT_TYPE.DINGTALK,
},{
type: ALERT_TYPE.WECHAT,
}];
export type AlertInstanceTableListItem = {
id: number,
name: string,
type: string,
params: string,
enabled: boolean,
createTime: Date,
updateTime: Date,
};
import {AlertInstanceTableListItem} from "@/pages/AlertInstance/data";
export const getJSONData = (values: AlertInstanceTableListItem) => {
if(!values||!values.params||values.params==''){
return {};
}
let data = JSON.parse(values.params);
return {...data,...values};
}
export const buildJSONData = (values: AlertInstanceTableListItem,params: any) => {
let newValue = values;
if(params.name){
newValue.name = params.name;
delete params.name;
}
if(params.enabled){
newValue.enabled = params.enabled;
delete params.enabled;
}
let data: string = JSON.stringify(params);
return {...newValue,params:data};
}
import Icon from "@ant-design/icons";
import {ALERT_TYPE} from "@/pages/AlertInstance/conf";
const svgSize = '100px';
export const getAlertIcon = (type: string) => {
switch (type) {
case ALERT_TYPE.DINGTALK:
return (<Icon component={DingTalkSvg}/>);
case ALERT_TYPE.WECHAT:
return (<Icon component={WeChatSvg}/>);
default:
return (<Icon component={DingTalkSvg}/>);
}
};
export const DingTalkSvg = () => (
<svg t="1645760285485" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="1804" width={svgSize} height={svgSize}>
<path
d="M512.003 79C272.855 79 79 272.855 79 512.003 79 751.145 272.855 945 512.003 945 751.145 945 945 751.145 945 512.003 945 272.855 751.145 79 512.003 79z m200.075 375.014c-0.867 3.764-3.117 9.347-6.234 16.012h0.087l-0.347 0.648c-18.183 38.86-65.631 115.108-65.631 115.108l-0.215-0.52-13.856 24.147h66.8L565.063 779l29.002-115.368h-52.598l18.27-76.29c-14.76 3.55-32.253 8.436-52.945 15.1 0 0-27.967 16.36-80.607-31.5 0 0-35.501-31.29-14.891-39.078 8.744-3.33 42.466-7.573 69.004-11.122 35.93-4.845 57.965-7.441 57.965-7.441s-110.607 1.643-136.841-2.468c-26.237-4.11-59.525-47.905-66.626-86.377 0 0-10.953-21.117 23.595-11.122 34.547 10 177.535 38.95 177.535 38.95s-185.933-56.992-198.36-70.929c-12.381-13.846-36.406-75.902-33.289-113.981 0 0 1.343-9.521 11.127-6.926 0 0 137.49 62.75 231.475 97.152 94.028 34.403 175.76 51.885 165.2 96.414z"
fill="#3AA2EB" p-id="1805"></path>
</svg>
);
export const WeChatSvg = () => (
<svg t="1645799602238" className="icon" viewBox="0 0 1229 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="1438" width={svgSize} height={svgSize}>
<path
d="M980.52 269.052a432.419 432.419 0 0 0-78.787-111.08A535.486 535.486 0 0 0 577.458 4.682a608.798 608.798 0 0 0-67.124-3.81 598.008 598.008 0 0 0-64.268 3.571 536.12 536.12 0 0 0-326.1 152.418 432.816 432.816 0 0 0-79.342 111.08A384.575 384.575 0 0 0 0 439.877a392.589 392.589 0 0 0 67.997 218.828 599.595 599.595 0 0 0 93.228 105.844l-15.313 119.57c-0.556 1.666-1.27 3.252-1.667 4.919s0 3.253-0.555 4.84 0 2.38 0 3.65v3.887a39.116 39.116 0 0 0 38.878 35.15 39.671 39.671 0 0 0 19.44-5.317h0.555l2.46-1.428 36.576-18.249 109.097-54.35a568.492 568.492 0 0 0 95.211 18.17 580.473 580.473 0 0 0 64.268 3.57 608.084 608.084 0 0 0 67.124-3.888 577.855 577.855 0 0 0 128.536-29.833 77.835 77.835 0 0 1-12.933-5.792 70.14 70.14 0 0 1-35.308-67.838 478.28 478.28 0 0 1-91.165 19.28 490.022 490.022 0 0 1-56.254 3.333 511.841 511.841 0 0 1-53.874-2.936l-11.108-1.507a486.69 486.69 0 0 1-72.122-14.996 49.748 49.748 0 0 0-14.996-2.301 51.89 51.89 0 0 0-23.803 6.189 28.167 28.167 0 0 0-3.095 1.586L241.6 832.704l-3.888 2.222a8.49 8.49 0 0 1-4.046 1.507 6.11 6.11 0 0 1-5.95-6.11l3.411-13.884 4.046-14.996 6.507-24.755 7.22-27.532a39.671 39.671 0 0 0 1.825-11.346 37.37 37.37 0 0 0-15.313-30.23 396.397 396.397 0 0 1-37.45-31.26 360.296 360.296 0 0 1-53.001-62.285 309.993 309.993 0 0 1-53.874-172.57 303.09 303.09 0 0 1 31.737-135.36 345.777 345.777 0 0 1 63.474-88.785A443.051 443.051 0 0 1 456.063 91.8a485.5 485.5 0 0 1 110.128 0 442.972 442.972 0 0 1 268.18 126.155 346.57 346.57 0 0 1 63.474 89.102 303.328 303.328 0 0 1 31.737 134.17c0 4.839 0 9.6-0.556 14.44a71.409 71.409 0 0 1 87.278 10.155c1.19 1.19 2.142 2.46 3.253 3.73a371.477 371.477 0 0 0 1.19-29.754 385.369 385.369 0 0 0-39.672-170.587"
fill="#2484DD" p-id="1439"></path>
<path
d="M1206.885 704.248a71.409 71.409 0 0 0-100.289 0 70.377 70.377 0 0 0-18.963 33.959c0 1.428-0.635 2.777-0.873 4.205s0 2.856-0.555 4.284a216.448 216.448 0 0 1-60.46 113.778c-2.3 2.301-4.76 4.602-7.14 6.744a16.662 16.662 0 0 0 0 23.803 17.138 17.138 0 0 0 22.692 1.19l1.27-1.19a11.267 11.267 0 0 0 1.983-2.38l4.76-4.84a218.272 218.272 0 0 1 111.795-59.507 64.11 64.11 0 0 0 6.823-0.714 60.777 60.777 0 0 0 7.934-1.666 70.298 70.298 0 0 0 31.738-117.745"
fill="#0080ED" p-id="1440"></path>
<path
d="M974.093 883.642a39.195 39.195 0 0 0-4.205-0.793 39.671 39.671 0 0 0-4.284-0.635 217.955 217.955 0 0 1-114.572-60.062 142.024 142.024 0 0 1-6.823-7.062 16.98 16.98 0 0 0-23.803 0 16.82 16.82 0 0 0-1.19 22.533l1.19 1.27a18.566 18.566 0 0 0 2.38 1.983l4.92 4.682a216.448 216.448 0 0 1 59.903 111.08 65.617 65.617 0 0 0 0.715 6.903c0.476 2.46 0.952 4.919 1.586 7.379a69.425 69.425 0 0 0 18.329 31.26 71.409 71.409 0 0 0 100.29 0 69.98 69.98 0 0 0 0-99.654 70.774 70.774 0 0 0-34.198-18.804"
fill="#FA6A07" p-id="1441"></path>
<path
d="M906.81 500.099a70.457 70.457 0 0 0 34.197 118.538l4.285 0.793 4.284 0.556a218.272 218.272 0 0 1 114.572 60.062l6.823 7.062a17.059 17.059 0 0 0 23.803 0 16.583 16.583 0 0 0 1.11-22.454 9.124 9.124 0 0 0-1.11-1.349 17.852 17.852 0 0 0-2.38-1.904l-4.92-4.76a215.257 215.257 0 0 1-59.904-111.08c0-2.302 0-4.603-0.714-6.824s-1.031-4.999-1.666-7.459a71.409 71.409 0 0 0-119.014-31.26"
fill="#33BD00" p-id="1442"></path>
<path
d="M828.499 759.788c0-1.428 0.476-2.856 0.635-4.285a216.05 216.05 0 0 1 60.38-113.777l7.14-6.744a16.741 16.741 0 0 0 0-23.803 16.98 16.98 0 0 0-22.612-1.111l-1.349 1.11a24.993 24.993 0 0 0-1.904 2.381l-4.84 4.84a217.4 217.4 0 0 1-111.794 59.507c-2.222 0-4.523 0-6.824 0.794a47.606 47.606 0 0 0-7.458 1.586 70.298 70.298 0 0 0-31.737 117.745 71.409 71.409 0 0 0 100.29 0 69.663 69.663 0 0 0 18.962-33.958c0-1.429 0.556-2.777 0.794-4.206"
fill="#FCC935" p-id="1443"></path>
</svg>
);
import React, {useRef, useState} from "react";
import {DownOutlined, PlusOutlined} from '@ant-design/icons';
import {ActionType, ProColumns} from "@ant-design/pro-table";
import {Button, message, Input, Drawer, Modal, Dropdown, Menu} from 'antd';
import {PageContainer, FooterToolbar} from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import ProDescriptions from '@ant-design/pro-descriptions';
import {AlertInstanceTableListItem} from "@/pages/AlertInstance/data";
import {handleAddOrUpdate, handleRemove, queryData, updateEnabled} from "@/components/Common/crud";
import AlertInstanceChooseForm from "@/pages/AlertInstance/components/AlertInstanceChooseForm";
const url = '/api/alertInstance';
const AlertInstanceTableList: React.FC<{}> = (props: any) => {
const {dispatch} = props;
const [row, setRow] = useState<AlertInstanceTableListItem>();
const [values, setValues] = useState<AlertInstanceTableListItem>();
const [modalVisible, handleModalVisible] = useState<boolean>(false);
const actionRef = useRef<ActionType>();
const [selectedRowsState, setSelectedRows] = useState<AlertInstanceTableListItem[]>([]);
const editAndDelete = (key: string | number, currentItem: AlertInstanceTableListItem) => {
if (key === 'edit') {
setValues(currentItem);
handleModalVisible(true);
} else if (key === 'delete') {
Modal.confirm({
title: '删除报警实例',
content: '确定删除该报警实例吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await handleRemove(url, [currentItem]);
actionRef.current?.reloadAndRest?.();
}
});
}
};
const MoreBtn: React.FC<{
item: AlertInstanceTableListItem;
}> = ({item}) => (
<Dropdown
overlay={
<Menu onClick={({key}) => editAndDelete(key, item)}>
<Menu.Item key="edit">编辑</Menu.Item>
<Menu.Item key="delete">删除</Menu.Item>
</Menu>
}
>
<a>
更多 <DownOutlined/>
</a>
</Dropdown>
);
const columns: ProColumns<AlertInstanceTableListItem>[] = [
{
title: '名称',
dataIndex: 'name',
tip: '名称是唯一的',
sorter: true,
formItemProps: {
rules: [
{
required: true,
message: '名称为必填项',
},
],
},
render: (dom, entity) => {
return <a onClick={() => setRow(entity)}>{dom}</a>;
},
},
{
title: '实例ID',
dataIndex: 'id',
hideInTable: true,
hideInForm: true,
hideInSearch: true,
},
{
title: '类型',
sorter: true,
dataIndex: 'type',
hideInForm: false,
hideInSearch: true,
hideInTable: false,
filters: [
{
text: 'DingTalk',
value: 'DingTalk',
},{
text: 'WeChat',
value: 'WeChat',
}
],
filterMultiple: false,
valueEnum: {
'DingTalk': {text: 'DingTalk'},
'WeChat': {text: 'WeChat'},
},
},
{
title: '配置',
dataIndex: 'params',
hideInForm: false,
hideInSearch: true,
hideInTable: true,
},
{
title: '是否启用',
dataIndex: 'enabled',
hideInForm: true,
hideInSearch: true,
hideInTable: false,
filters: [
{
text: '已启用',
value: 1,
},
{
text: '已禁用',
value: 0,
},
],
filterMultiple: false,
valueEnum: {
true: {text: '已启用', status: 'Success'},
false: {text: '已禁用', status: 'Error'},
},
},
{
title: '创建时间',
dataIndex: 'createTime',
sorter: true,
valueType: 'dateTime',
hideInTable: true
},
{
title: '最近更新时间',
dataIndex: 'updateTime',
sorter: true,
valueType: 'dateTime',
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => [
<a
onClick={() => {
handleModalVisible(true);
setValues(record);
}}
>
配置
</a>,
<MoreBtn key="more" item={record}/>,
],
},
];
return (
<PageContainer>
<ProTable<AlertInstanceTableListItem>
headerTitle="报警实例管理"
actionRef={actionRef}
rowKey="id"
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button type="primary" onClick={() => handleModalVisible(true)}>
<PlusOutlined/> 新建
</Button>,
]}
request={(params, sorter, filter) => queryData(url, {...params, sorter, filter})}
columns={columns}
rowSelection={{
onChange: (_, selectedRows) => setSelectedRows(selectedRows),
}}
/>
{selectedRowsState?.length > 0 && (
<FooterToolbar
extra={
<div>
已选择 <a style={{fontWeight: 600}}>{selectedRowsState.length}</a>&nbsp;&nbsp;
<span>
被禁用的报警实例共 {selectedRowsState.length - selectedRowsState.reduce((pre, item) => pre + (item.enabled ? 1 : 0), 0)}
</span>
</div>
}
>
<Button type="primary" danger
onClick={() => {
Modal.confirm({
title: '删除报警实例',
content: '确定删除选中的报警实例吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await handleRemove(url, selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
});
}}
>
批量删除
</Button>
<Button type="primary"
onClick={() => {
Modal.confirm({
title: '启用报警实例',
content: '确定启用选中的报警实例吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await updateEnabled(url, selectedRowsState, true);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
});
}}
>批量启用</Button>
<Button danger
onClick={() => {
Modal.confirm({
title: '禁用报警实例',
content: '确定禁用选中的报警实例吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await updateEnabled(url, selectedRowsState, false);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
});
}}
>批量禁用</Button>
</FooterToolbar>
)}
<AlertInstanceChooseForm onCancel={() => {
handleModalVisible(false);
setValues(undefined);
}}
modalVisible={modalVisible}
onSubmit={() => {
actionRef.current?.reloadAndRest?.();
}}
values={values}
/>
<Drawer
width={600}
visible={!!row}
onClose={() => {
setRow(undefined);
}}
closable={false}
>
{row?.name && (
<ProDescriptions<AlertInstanceTableListItem>
column={2}
title={row?.name}
request={async () => ({
data: row || {},
})}
params={{
id: row?.name,
}}
columns={columns}
/>
)}
</Drawer>
</PageContainer>
);
};
export default AlertInstanceTableList;
import {Effect, Reducer} from "umi";
import {AlertInstanceTableListItem} from "@/pages/AlertInstance/data";
export type AlertStateType = {
instance:AlertInstanceTableListItem[],
};
export type AlertModelType = {
namespace: string;
state: AlertStateType;
effects: {
};
reducers: {
saveInstance: Reducer<AlertStateType>;
};
};
const AlertModel: AlertModelType = {
namespace: 'Alert',
state: {
instance:[],
},
effects: {
},
reducers: {
saveInstance(state, {payload}) {
return {
...state,
instance: payload,
};
},
},
};
export default AlertModel;
import {handleAddOrUpdate} from "@/components/Common/crud";
import {AlertInstanceTableListItem} from "@/pages/AlertInstance/data";
export async function createOrModifyAlertInstance(alertInstance: AlertInstanceTableListItem) {
return handleAddOrUpdate('/api/alertInstance', alertInstance);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment