[Feature][UI Next] Security alarm group manage (#8069)

* [Feature][UI Next] Add security alarm group manage.

* [Feature][UI Next] Add security alarm group manage license.
This commit is contained in:
songjianet 2022-01-16 19:17:55 +08:00 committed by GitHub
parent a5f4e3de46
commit ec7898deea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 771 additions and 9 deletions

View File

@ -29,13 +29,14 @@ specifiers:
'@vueuse/core': ^7.5.3
axios: ^0.24.0
dart-sass: ^1.25.0
date-fns: ^2.28.0
date-fns: ^2.27.0
echarts: ^5.2.2
eslint: ^8.6.0
eslint-config-prettier: ^8.3.0
eslint-plugin-prettier: ^4.0.0
eslint-plugin-vue: ^8.2.0
lodash: ^4.17.21
monaco-editor: ^0.31.1
naive-ui: 2.23.2
nprogress: ^0.2.0
pinia: ^2.0.9
@ -60,6 +61,7 @@ dependencies:
date-fns: 2.28.0
echarts: 5.2.2
lodash: 4.17.21
monaco-editor: 0.31.1
naive-ui: 2.23.2_vue@3.2.26
nprogress: 0.2.0
pinia: 2.0.9_typescript@4.5.4+vue@3.2.26
@ -2263,6 +2265,10 @@ packages:
hasBin: true
dev: true
/monaco-editor/0.31.1:
resolution: {integrity: sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==}
dev: false
/ms/2.0.0:
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
dev: true

View File

@ -281,6 +281,23 @@ const security = {
edit: 'Edit',
delete: 'Delete'
},
alarm_group: {
create_alarm_group: 'Create Alarm Group',
edit_alarm_group: 'Edit Alarm Group',
search_tips: 'Please enter keywords',
alert_group_name_tips: 'Please enter your alert group name',
alarm_plugin_instance: 'Alarm Plugin Instance',
alarm_plugin_instance_tips: 'Please select alert plugin instance',
alarm_group_description_tips: 'Please enter your alarm group description',
alert_group_name: 'Alert Group Name',
alarm_group_description: 'Alarm Group Description',
create_time: 'Create Time',
update_time: 'Update Time',
operation: 'Operation',
delete_confirm: 'Delete?',
edit: 'Edit',
delete: 'Delete'
},
yarn_queue: {
create_queue: 'Create Queue',
edit_queue: 'Edit Queue',
@ -314,6 +331,7 @@ const security = {
worker_group_tips: 'Please select worker group'
}
}
const datasource = {
datasource: 'DataSource',
create_datasource: 'Create DataSource',

View File

@ -281,6 +281,24 @@ const security = {
edit: '编辑',
delete: '删除'
},
alarm_group: {
create_alarm_group: '创建告警组',
edit_alarm_group: '编辑告警组',
search_tips: '请输入关键词',
alert_group_name_tips: '请输入告警组名称',
alarm_plugin_instance: '告警组实例',
alarm_plugin_instance_tips: '请选择告警组实例',
alarm_group_description_tips: '请输入告警组描述',
alert_group_name: '告警组名称',
alarm_group_description: '告警组描述',
create_time: '创建时间',
update_time: '更新时间',
actions: '操作',
operation: '操作',
delete_confirm: '确定删除吗?',
edit: '编辑',
delete: '删除'
},
yarn_queue: {
create_queue: '创建队列',
edit_queue: '编辑队列',
@ -314,6 +332,7 @@ const security = {
worker_group_tips: '请选择Worker分组'
}
}
const datasource = {
datasource: '数据源',
create_datasource: '创建数据源',

View File

@ -45,6 +45,14 @@ export default {
title: '用户管理'
}
},
{
path: '/security/alarm-group-manage',
name: 'alarm-group-manage',
component: components['alarm-group-manage'],
meta: {
title: '告警组管理'
}
},
{
path: '/security/yarn-queue-manage',
name: 'yarn-queue-manage',

View File

@ -59,7 +59,7 @@ export function verifyGroupName(params: GroupNameReq): any {
export function updateAlertGroup(data: GroupReq, id: IdReq): any {
return axios({
url: `/alert-groups/${id}`,
url: `/alert-groups/${id.id}`,
method: 'put',
data
})
@ -67,7 +67,8 @@ export function updateAlertGroup(data: GroupReq, id: IdReq): any {
export function delAlertGroupById(id: IdReq): any {
return axios({
url: `/alert-groups/${id}`,
method: 'delete'
url: `/alert-groups/${id.id}`,
method: 'delete',
params: id
})
}

View File

@ -34,4 +34,23 @@ interface GroupReq extends GroupNameReq {
description?: string
}
export { ListReq, GroupNameReq, IdReq, GroupReq }
interface AlarmGroupItem {
id: number
groupName: string
alertInstanceIds: string
description: string
createTime: string
updateTime: string
createUserId: number
}
interface AlarmGroupRes {
totalList: AlarmGroupItem[]
total: number
totalPage: number
pageSize: number
currentPage: number
start: number
}
export { ListReq, GroupNameReq, IdReq, GroupReq, AlarmGroupItem, AlarmGroupRes }

View File

@ -41,10 +41,21 @@ interface UpdatePluginInstanceReq {
pluginInstanceParams: string
}
interface AlertPluginItem {
id: number
pluginDefineId: number
instanceName: string
pluginInstanceParams: string
createTime: string
updateTime: string
alertPluginName: string
}
export {
ListReq,
PluginInstanceReq,
InstanceNameReq,
IdReq,
UpdatePluginInstanceReq
UpdatePluginInstanceReq,
AlertPluginItem
}

View File

@ -32,7 +32,9 @@ const JsonHighlight = defineComponent({
props,
render(props: { rowData: { connectionParams: string } }) {
return (
<pre class={styles['json-highlight']}>{syntaxHighlight(props.rowData.connectionParams)}</pre>
<pre class={styles['json-highlight']}>
{syntaxHighlight(props.rowData.connectionParams)}
</pre>
)
}
})

View File

@ -63,8 +63,7 @@ export function useColumns(onCallback: Function) {
default: () => t('datasource.click_to_view')
}
),
default: () =>
h(JsonHighlight, { rowData })
default: () => h(JsonHighlight, { rowData })
}
)
}

View File

@ -0,0 +1,166 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineComponent, PropType, toRefs, watch } from 'vue'
import Modal from '@/components/modal'
import { NForm, NFormItem, NInput, NSelect } from 'naive-ui'
import { useModal } from './use-modal'
import { useI18n } from 'vue-i18n'
const AlertGroupModal = defineComponent({
name: 'YarnQueueModal',
props: {
showModalRef: {
type: Boolean as PropType<boolean>,
default: false
},
statusRef: {
type: Number as PropType<number>,
default: 0
},
row: {
type: Object as PropType<any>,
default: {}
}
},
emits: ['cancelModal', 'confirmModal'],
setup(props, ctx) {
const { variables, handleValidate, getListData } = useModal(props, ctx)
const { t } = useI18n()
const cancelModal = () => {
if (props.statusRef === 0) {
variables.model.groupName = ''
variables.model.alertInstanceIds = []
variables.model.description = ''
}
ctx.emit('cancelModal', props.showModalRef)
}
const confirmModal = () => {
handleValidate(props.statusRef)
}
watch(
() => props.showModalRef,
() => {
props.showModalRef && getListData()
}
)
watch(
() => props.statusRef,
() => {
if (props.statusRef === 0) {
variables.model.groupName = ''
variables.model.alertInstanceIds = []
variables.model.description = ''
} else {
variables.model.id = props.row.id
variables.model.groupName = props.row.groupName
variables.model.alertInstanceIds = props.row.alertInstanceIds
.split(',')
.map((item: string) => Number(item))
variables.model.description = props.row.description
}
}
)
watch(
() => props.row,
() => {
variables.model.id = props.row.id
variables.model.groupName = props.row.groupName
variables.model.alertInstanceIds = props.row.alertInstanceIds
.split(',')
.map((item: string) => Number(item))
variables.model.description = props.row.description
}
)
return { t, ...toRefs(variables), cancelModal, confirmModal }
},
render() {
const { t } = this
return (
<div>
<Modal
title={
this.statusRef === 0
? t('security.alarm_group.create_alarm_group')
: t('security.alarm_group.edit_alarm_group')
}
show={this.showModalRef}
onCancel={this.cancelModal}
onConfirm={this.confirmModal}
confirmDisabled={
!this.model.groupName || this.model.alertInstanceIds.length < 1
}
>
{{
default: () => (
<NForm
model={this.model}
rules={this.rules}
ref='alertGroupFormRef'
>
<NFormItem
label={t('security.alarm_group.alert_group_name')}
path='groupName'
>
<NInput
placeholder={t(
'security.alarm_group.alert_group_name_tips'
)}
v-model={[this.model.groupName, 'value']}
/>
</NFormItem>
<NFormItem
label={t('security.alarm_group.alarm_plugin_instance')}
path='alertInstanceIds'
>
<NSelect
multiple
placeholder={t(
'security.alarm_group.alarm_plugin_instance_tips'
)}
options={this.model.generalOptions}
v-model={[this.model.alertInstanceIds, 'value']}
/>
</NFormItem>
<NFormItem
label={t('security.alarm_group.alarm_group_description')}
path='description'
>
<NInput
type='textarea'
placeholder={t(
'security.alarm_group.alarm_group_description_tips'
)}
v-model={[this.model.description, 'value']}
/>
</NFormItem>
</NForm>
)
}}
</Modal>
</div>
)
}
})
export default AlertGroupModal

View File

@ -0,0 +1,130 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { reactive, ref, SetupContext } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAsyncState } from '@vueuse/core'
import { queryAlertPluginInstanceList } from '@/service/modules/alert-plugin'
import {
verifyGroupName,
createAlertGroup,
updateAlertGroup
} from '@/service/modules/alert-group'
import type { AlertPluginItem } from '@/service/modules/alert-plugin/types'
export function useModal(
props: any,
ctx: SetupContext<('cancelModal' | 'confirmModal')[]>
) {
const { t } = useI18n()
const variables = reactive({
alertGroupFormRef: ref(),
model: {
id: ref<number>(-1),
groupName: ref(''),
alertInstanceIds: ref<Array<number>>([]),
description: ref(''),
generalOptions: []
},
rules: {
groupName: {
required: true,
trigger: ['input', 'blur'],
validator() {
if (variables.model.groupName === '') {
return new Error(t('security.alarm_group.alert_group_name_tips'))
}
}
},
alertInstanceIds: {
required: true,
trigger: ['input', 'blur'],
validator() {
if (variables.model.alertInstanceIds.length < 1) {
return new Error(
t('security.alarm_group.alarm_plugin_instance_tips')
)
}
}
}
}
})
const getListData = () => {
const { state } = useAsyncState(
queryAlertPluginInstanceList().then((res: Array<AlertPluginItem>) => {
variables.model.generalOptions = res.map(
(item): { label: string; value: number } => {
return {
label: item.instanceName,
value: item.id
}
}
) as any
}),
{}
)
return state
}
const handleValidate = (statusRef: number) => {
variables.alertGroupFormRef.validate((errors: any) => {
if (!errors) {
statusRef === 0 ? submitAlertGroupModal() : updateAlertGroupModal()
} else {
return
}
})
}
const submitAlertGroupModal = () => {
verifyGroupName({ groupName: variables.model.groupName }).then(() => {
const data = {
groupName: variables.model.groupName,
alertInstanceIds: variables.model.alertInstanceIds.toString(),
description: variables.model.description
}
createAlertGroup(data).then(() => {
variables.model.groupName = ''
variables.model.alertInstanceIds = []
variables.model.description = ''
ctx.emit('confirmModal', props.showModalRef)
})
})
}
const updateAlertGroupModal = () => {
const data = {
groupName: variables.model.groupName,
alertInstanceIds: variables.model.alertInstanceIds.toString(),
description: variables.model.description
}
updateAlertGroup(data, { id: variables.model.id }).then(() => {
ctx.emit('confirmModal', props.showModalRef)
})
}
return {
variables,
handleValidate,
getListData
}
}

View File

@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.search-card {
display: flex;
justify-content: space-between;
align-items: center;
.box {
display: flex;
justify-content: flex-end;
align-items: center;
width: 300px;
button {
margin-left: 10px;
}
}
}
.table-card {
margin-top: 8px;
.pagination {
margin-top: 20px;
display: flex;
justify-content: center;
}
}

View File

@ -0,0 +1,158 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineComponent, onMounted, toRefs, watch } from 'vue'
import {
NButton,
NCard,
NDataTable,
NIcon,
NInput,
NPagination
} from 'naive-ui'
import { SearchOutlined } from '@vicons/antd'
import { useI18n } from 'vue-i18n'
import { useTable } from './use-table'
import Card from '@/components/card'
import AlertGroupModal from './components/alert-group-modal'
import styles from './index.module.scss'
const alarmGroupManage = defineComponent({
name: 'alarm-group-manage',
setup() {
const { t } = useI18n()
const { variables, getTableData, createColumns } = useTable()
const requestData = () => {
getTableData({
pageSize: variables.pageSize,
pageNo: variables.page,
searchVal: variables.searchVal
})
}
const onUpdatePageSize = () => {
variables.page = 1
requestData()
}
const onSearch = () => {
variables.page = 1
requestData()
}
const handleModalChange = () => {
variables.showModalRef = true
variables.statusRef = 0
}
const onCancelModal = () => {
variables.showModalRef = false
}
const onConfirmModal = () => {
variables.showModalRef = false
requestData()
}
onMounted(() => {
createColumns(variables)
requestData()
})
watch(useI18n().locale, () => {
createColumns(variables)
})
return {
t,
...toRefs(variables),
requestData,
onCancelModal,
onConfirmModal,
onUpdatePageSize,
handleModalChange,
onSearch
}
},
render() {
const {
t,
requestData,
onUpdatePageSize,
onCancelModal,
onConfirmModal,
handleModalChange,
onSearch
} = this
return (
<div>
<NCard>
<div class={styles['search-card']}>
<div>
<NButton size='small' type='primary' onClick={handleModalChange}>
{t('security.alarm_group.create_alarm_group')}
</NButton>
</div>
<div class={styles.box}>
<NInput
size='small'
clearable
v-model={[this.searchVal, 'value']}
placeholder={t('security.alarm_group.search_tips')}
/>
<NButton size='small' type='primary' onClick={onSearch}>
{{
icon: () => (
<NIcon>
<SearchOutlined />
</NIcon>
)
}}
</NButton>
</div>
</div>
</NCard>
<Card class={styles['table-card']}>
<NDataTable columns={this.columns} data={this.tableData} />
<div class={styles.pagination}>
<NPagination
v-model:page={this.page}
v-model:page-size={this.pageSize}
page-count={this.totalPage}
show-size-picker
page-sizes={[10, 30, 50]}
show-quick-jumper
onUpdatePage={requestData}
onUpdatePageSize={onUpdatePageSize}
/>
</div>
</Card>
<AlertGroupModal
showModalRef={this.showModalRef}
statusRef={this.statusRef}
row={this.row}
onCancelModal={onCancelModal}
onConfirmModal={onConfirmModal}
/>
</div>
)
}
})
export default alarmGroupManage

View File

@ -0,0 +1,182 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useAsyncState } from '@vueuse/core'
import { reactive, h, ref } from 'vue'
import { format } from 'date-fns'
import { NButton, NPopconfirm, NSpace, NTooltip } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import {
queryAlertGroupListPaging,
delAlertGroupById
} from '@/service/modules/alert-group'
import { DeleteOutlined, EditOutlined } from '@vicons/antd'
import type { AlarmGroupRes } from '@/service/modules/alert-group/types'
export function useTable() {
const { t } = useI18n()
const handleEdit = (row: any) => {
variables.showModalRef = true
variables.statusRef = 1
variables.row = row
}
const createColumns = (variables: any) => {
variables.columns = [
{
title: '#',
key: 'index'
},
{
title: t('security.alarm_group.alert_group_name'),
key: 'groupName'
},
{
title: t('security.alarm_group.alarm_group_description'),
key: 'description'
},
{
title: t('security.alarm_group.create_time'),
key: 'createTime'
},
{
title: t('security.alarm_group.update_time'),
key: 'updateTime'
},
{
title: t('security.alarm_group.operation'),
key: 'operation',
render(row: any) {
return h(NSpace, null, {
default: () => [
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'info',
size: 'small',
onClick: () => {
handleEdit(row)
}
},
{
icon: () => h(EditOutlined)
}
),
default: () => t('security.alarm_group.edit')
}
),
h(
NPopconfirm,
{
onPositiveClick: () => {
handleDelete(row)
}
},
{
trigger: () =>
h(
NTooltip,
{},
{
trigger: () =>
h(
NButton,
{
circle: true,
type: 'error',
size: 'small'
},
{
icon: () => h(DeleteOutlined)
}
),
default: () => t('security.alarm_group.delete')
}
),
default: () => t('security.alarm_group.delete_confirm')
}
)
]
})
}
}
]
}
const variables = reactive({
columns: [],
tableData: [],
page: ref(1),
pageSize: ref(10),
searchVal: ref(null),
totalPage: ref(1),
showModalRef: ref(false),
statusRef: ref(0),
row: {}
})
const handleDelete = (row: any) => {
delAlertGroupById({ id: row.id }).then(() => {
getTableData({
pageSize: variables.pageSize,
pageNo:
variables.tableData.length === 1 && variables.page > 1
? variables.page - 1
: variables.page,
searchVal: variables.searchVal
})
})
}
const getTableData = (params: any) => {
const { state } = useAsyncState(
queryAlertGroupListPaging({ ...params }).then((res: AlarmGroupRes) => {
variables.tableData = res.totalList.map((item, index) => {
item.createTime = format(
new Date(item.createTime),
'yyyy-MM-dd HH:mm:ss'
)
item.updateTime = format(
new Date(item.updateTime),
'yyyy-MM-dd HH:mm:ss'
)
return {
index: index + 1,
...item
}
}) as any
variables.totalPage = res.totalPage
}),
{}
)
return state
}
return {
variables,
getTableData,
createColumns
}
}