diff --git a/dolphinscheduler-ui-next/src/components/form/fields.ts b/dolphinscheduler-ui-next/src/components/form/fields.ts new file mode 100644 index 0000000000..1fe4388ce3 --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/form/fields.ts @@ -0,0 +1,54 @@ +/* + * 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 { h } from 'vue' +import { NInput, NRadio, NRadioGroup, NSpace } from 'naive-ui' +import type { IFieldParams } from './types' + +// TODO Support other widgets later +// Input +export function renderInput(params: IFieldParams) { + const { props, fields, field } = params + return h(NInput, { + ...props, + value: fields[field], + onUpdateValue: (value) => void (fields[field] = value) + }) +} + +// Radio && RadioGroup +export function renderRadio(params: IFieldParams) { + const { props, fields, field, options } = params + if (!options || options.length === 0) { + return h(NRadio, { + ...props, + value: fields[field], + onUpdateChecked: (checked) => void (fields[field] = checked) + }) + } + return h( + NRadioGroup, + { + value: fields[field], + onUpdateValue: (value) => void (fields[field] = value) + }, + () => + h(NSpace, null, () => + options.map((option) => h(NRadio, option, () => option.label)) + ) + ) +} diff --git a/dolphinscheduler-ui-next/src/components/form/get-elements-by-json.ts b/dolphinscheduler-ui-next/src/components/form/get-elements-by-json.ts new file mode 100644 index 0000000000..e88037000d --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/form/get-elements-by-json.ts @@ -0,0 +1,60 @@ +/* + * 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 * as Field from './fields' +import { formatValidate } from './utils' +import type { FormRules } from 'naive-ui' +import type { IJsonItem } from './types' + +export default function getElementByJson( + json: IJsonItem[], + fields: { [field: string]: any }, + t: Function, + prefix: string +) { + const rules: FormRules = {} + const initialValues: { [field: string]: any } = {} + const elements = [] + + const getElement = (item: IJsonItem) => { + const { type, props = {}, field, options } = item + // TODO Support other widgets later + if (type === 'radio') { + return Field.renderRadio({ + field, + fields, + props, + options + }) + } + + return Field.renderInput({ field, fields, props }) + } + + for (let item of json) { + fields[item.field] = item.value + initialValues[item.field] = item.value + if (item.validate) rules[item.field] = formatValidate(item.validate) + elements.push({ + label: t(prefix + '.' + item.field), + path: item.field, + widget: () => getElement(item) + }) + } + + return { rules, elements, initialValues } +} diff --git a/dolphinscheduler-ui-next/src/components/form/index.tsx b/dolphinscheduler-ui-next/src/components/form/index.tsx new file mode 100644 index 0000000000..f3151418ab --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/form/index.tsx @@ -0,0 +1,71 @@ +/* + * 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, h } from 'vue' +import { NSpin, NGrid, NForm, NFormItemGi } from 'naive-ui' +import { useForm } from './use-form' +import type { GridProps, IMeta } from './types' + +const props = { + meta: { + type: Object as PropType, + default: {}, + required: true + }, + layout: { + type: Object as PropType + }, + loading: { + type: Boolean as PropType, + default: false + } +} + +const Form = defineComponent({ + name: 'Form', + props, + setup(props, { expose }) { + const { state, ...rest } = useForm() + expose({ + ...rest + }) + return { ...toRefs(state) } + }, + render(props: { meta: IMeta; layout?: GridProps; loading?: boolean }) { + const { loading, layout, meta } = props + const { elements, ...restFormProps } = meta + return ( + + + + {elements && + elements.map((element) => { + const { span = 24, path, widget, ...formItemProps } = element + return ( + + {h(widget)} + + ) + })} + + + + ) + } +}) + +export default Form diff --git a/dolphinscheduler-ui-next/src/components/form/types.ts b/dolphinscheduler-ui-next/src/components/form/types.ts new file mode 100644 index 0000000000..95babad420 --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/form/types.ts @@ -0,0 +1,68 @@ +/* + * 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 type { + GridProps, + FormProps, + FormItemGiProps, + FormItemRule, + FormRules, + SelectOption +} from 'naive-ui' + +type IType = 'input' | 'radio' + +type IOption = SelectOption + +interface IFormItem extends FormItemGiProps { + widget: any +} + +interface IMeta extends Omit { + elements?: IFormItem[] + model: object +} + +interface IFieldParams { + field: string + props: object + fields: { [field: string]: any } + options?: IOption[] +} + +interface IJsonItem { + field: string + name?: string + props?: object + title?: string + type?: IType + validate?: FormItemRule + value?: any + options?: IOption[] +} + +export { + IMeta, + IType, + IJsonItem, + IOption, + FormItemRule, + FormRules, + IFormItem, + GridProps, + IFieldParams +} diff --git a/dolphinscheduler-ui-next/src/components/form/use-form.ts b/dolphinscheduler-ui-next/src/components/form/use-form.ts new file mode 100644 index 0000000000..a5e5e8c1b4 --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/form/use-form.ts @@ -0,0 +1,45 @@ +/* + * 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 } from 'vue' + +export function useForm() { + const state = reactive({ + formRef: ref() + }) + + const validate = () => { + state.formRef.validate() + } + + const setValues = (initialValues: { [field: string]: any }) => { + for (let [key, value] of Object.entries(initialValues)) { + state.formRef.model[key] = value + } + } + + const restoreValidation = () => { + state.formRef.restoreValidation() + } + + return { + state, + validate, + setValues, + restoreValidation + } +} diff --git a/dolphinscheduler-ui-next/src/components/form/utils.ts b/dolphinscheduler-ui-next/src/components/form/utils.ts new file mode 100644 index 0000000000..20c5df1a0f --- /dev/null +++ b/dolphinscheduler-ui-next/src/components/form/utils.ts @@ -0,0 +1,37 @@ +/* + * 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 type { FormRules, FormItemRule } from './types' + +export function formatLabel(label?: string): string { + if (!label) return '' + const match = label.match(/^\$t\('(\S*)'\)/) + return match ? match[1] : label +} + +export function formatValidate( + validate?: FormItemRule | FormRules +): FormItemRule { + if (!validate) return {} + if (Array.isArray(validate)) { + validate.map((item: FormItemRule) => { + if (!item?.message) delete item.message + return item + }) + } + if (!validate.message) delete validate.message + return validate +} diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 709966e1ba..0ed8489c65 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -482,6 +482,61 @@ const security = { delete: 'Delete', save_error_msg: 'Failed to save, please retry', delete_error_msg: 'Failed to delete, please retry' + }, + alarm_instance: { + search_input_tips: 'Please input the keywords', + alarm_instance_manage: 'Alarm instance manage', + alarm_instance: 'Alarm Instance', + serial_number: '#', + alarm_instance_name: 'Alarm instance name', + alarm_instance_name_tips: 'Please enter alarm plugin instance name', + alarm_plugin_name: 'Alarm plugin name', + create_time: 'Create Time', + update_time: 'Update Time', + operation: 'Operation', + edit: 'Edit', + delete: 'Delete', + confirm: 'Confirm', + cancel: 'Cancel', + submit: 'Submit', + create: 'Create', + select_plugin: 'Select plugin', + select_plugin_tips: 'Select Alarm plugin', + instance_parameter_exception: 'Instance parameter exception', + WebHook: 'WebHook', + webHook: 'WebHook', + IsEnableProxy: 'Enable Proxy', + Proxy: 'Proxy', + Port: 'Port', + User: 'User', + corpId: 'CorpId', + secret: 'Secret', + Secret: 'Secret', + users: 'Users', + userSendMsg: 'UserSendMsg', + agentId: 'AgentId', + showType: 'Show Type', + receivers: 'Receivers', + receiverCcs: 'ReceiverCcs', + serverHost: 'SMTP Host', + serverPort: 'SMTP Port', + sender: 'Sender', + enableSmtpAuth: 'SMTP Auth', + Password: 'Password', + starttlsEnable: 'SMTP STARTTLS Enable', + sslEnable: 'SMTP SSL Enable', + smtpSslTrust: 'SMTP SSL Trust', + url: 'URL', + requestType: 'Request Type', + headerParams: 'Headers', + bodyParams: 'Body', + contentField: 'Content Field', + Keyword: 'Keyword', + userParams: 'User Params', + path: 'Script Path', + type: 'Type', + sendType: 'Send Type', + username: 'Username' } } diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index fab3be993a..273c60fbe2 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -479,6 +479,61 @@ const security = { delete: '删除', save_error_msg: '保存失败,请重试', delete_error_msg: '删除失败,请重试' + }, + alarm_instance: { + search_input_tips: '请输入关键字', + alarm_instance_manage: '告警实例管理', + alarm_instance: '告警实例', + serial_number: '编号', + alarm_instance_name: '告警实例名称', + alarm_instance_name_tips: '请输入告警实例名称', + alarm_plugin_name: '告警插件名称', + create_time: '创建时间', + update_time: '更新时间', + operation: '操作', + edit: '编辑', + delete: '删除', + confirm: '确定', + cancel: '取消', + submit: '提交', + create: '创建', + select_plugin: '选择插件', + select_plugin_tips: '请选择告警插件', + instance_parameter_exception: '实例参数异常', + WebHook: 'Web钩子', + webHook: 'Web钩子', + IsEnableProxy: '启用代理', + Proxy: '代理', + Port: '端口', + User: '用户', + corpId: '企业ID', + secret: '密钥', + Secret: '密钥', + users: '群员', + userSendMsg: '群员信息', + agentId: '应用ID', + showType: '内容展示类型', + receivers: '收件人', + receiverCcs: '抄送人', + serverHost: 'SMTP服务器', + serverPort: 'SMTP端口', + sender: '发件人', + enableSmtpAuth: '请求认证', + Password: '密码', + starttlsEnable: 'STARTTLS连接', + sslEnable: 'SSL连接', + smtpSslTrust: 'SSL证书信任', + url: 'URL', + requestType: '请求方式', + headerParams: '请求头', + bodyParams: '请求体', + contentField: '内容字段', + Keyword: '关键词', + userParams: '自定义参数', + path: '脚本路径', + type: '类型', + sendType: '发送类型', + username: '用户名' } } diff --git a/dolphinscheduler-ui-next/src/router/modules/security.ts b/dolphinscheduler-ui-next/src/router/modules/security.ts index ab6d069116..6750b813cd 100644 --- a/dolphinscheduler-ui-next/src/router/modules/security.ts +++ b/dolphinscheduler-ui-next/src/router/modules/security.ts @@ -91,6 +91,15 @@ export default { title: '令牌管理管理', showSide: true } + }, + { + path: '/security/alarm-instance-manage', + name: 'alarm-instance-manage', + component: components['security-alarm-instance-manage'], + meta: { + title: '告警实例管理', + showSide: true + } } ] } diff --git a/dolphinscheduler-ui-next/src/service/modules/alert-plugin/index.ts b/dolphinscheduler-ui-next/src/service/modules/alert-plugin/index.ts index 68c03f3c77..c6f5f30d9a 100644 --- a/dolphinscheduler-ui-next/src/service/modules/alert-plugin/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/alert-plugin/index.ts @@ -40,13 +40,6 @@ export function createAlertPluginInstance(data: PluginInstanceReq): any { }) } -export function queryAlertPluginInstanceList(): any { - return axios({ - url: '/alert-plugin-instances/list', - method: 'get' - }) -} - export function verifyAlertInstanceName(params: InstanceNameReq): any { return axios({ url: '/alert-plugin-instances/verify-name', diff --git a/dolphinscheduler-ui-next/src/service/modules/alert-plugin/types.ts b/dolphinscheduler-ui-next/src/service/modules/alert-plugin/types.ts index 74d890519d..dca86a4501 100644 --- a/dolphinscheduler-ui-next/src/service/modules/alert-plugin/types.ts +++ b/dolphinscheduler-ui-next/src/service/modules/alert-plugin/types.ts @@ -31,10 +31,6 @@ interface InstanceNameReq { alertInstanceName: string } -interface IdReq { - id: number -} - interface UpdatePluginInstanceReq { alertPluginInstanceId: number instanceName: string @@ -51,6 +47,8 @@ interface AlertPluginItem { alertPluginName: string } +type IdReq = number + export { ListReq, PluginInstanceReq, diff --git a/dolphinscheduler-ui-next/src/service/modules/ui-plugins/index.ts b/dolphinscheduler-ui-next/src/service/modules/ui-plugins/index.ts index 553cc486ec..1a9e99ae52 100644 --- a/dolphinscheduler-ui-next/src/service/modules/ui-plugins/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/ui-plugins/index.ts @@ -16,7 +16,7 @@ */ import { axios } from '@/service/service' -import { PluginTypeReq, IdReq } from './types' +import { PluginTypeReq, IPluginId } from './types' export function queryUiPluginsByType(params: PluginTypeReq): any { return axios({ @@ -26,7 +26,7 @@ export function queryUiPluginsByType(params: PluginTypeReq): any { }) } -export function queryUiPluginDetailById(id: IdReq): any { +export function queryUiPluginDetailById(id: IPluginId): any { return axios({ url: `/ui-plugins/${id}`, method: 'get' diff --git a/dolphinscheduler-ui-next/src/service/modules/ui-plugins/types.ts b/dolphinscheduler-ui-next/src/service/modules/ui-plugins/types.ts index 82bec60714..785abcbe11 100644 --- a/dolphinscheduler-ui-next/src/service/modules/ui-plugins/types.ts +++ b/dolphinscheduler-ui-next/src/service/modules/ui-plugins/types.ts @@ -19,8 +19,6 @@ interface PluginTypeReq { pluginType: 'ALERT' | 'REGISTER' | 'TASK' } -interface IdReq { - id: number -} +type IPluginId = number -export { PluginTypeReq, IdReq } +export { PluginTypeReq, IPluginId } diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/detail.tsx b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/detail.tsx new file mode 100644 index 0000000000..a42648437e --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/detail.tsx @@ -0,0 +1,196 @@ +/* + * 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, onMounted, ref } from 'vue' +import { NSelect, NInput } from 'naive-ui' +import Modal from '@/components/modal' +import Form from '@/components/form' +import { useI18n } from 'vue-i18n' +import { useForm } from './use-form' +import { useDetail } from './use-detail' +import getElementByJson from '@/components/form/get-elements-by-json' +import type { IRecord, FormRules, IFormItem } from './types' + +const props = { + show: { + type: Boolean as PropType, + default: false + }, + currentRecord: { + type: Object as PropType, + default: {} + } +} + +const DetailModal = defineComponent({ + name: 'DetailModal', + props, + emits: ['cancel', 'update'], + setup(props, ctx) { + const { t } = useI18n() + + const rules = ref({}) + const elements = ref([]) + + const { + meta, + state, + setDetail, + initForm, + resetForm, + getFormValues, + changePlugin + } = useForm() + + const { status, createOrUpdate } = useDetail(getFormValues) + + const onCancel = () => { + resetForm() + ctx.emit('cancel') + } + + const onSubmit = async () => { + await state.detailFormRef.validate() + const res = await createOrUpdate(props.currentRecord, state.json) + if (res) { + onCancel() + ctx.emit('update') + } + } + + const onChangePlugin = changePlugin + + watch( + () => props.show, + async () => { + props.show && props.currentRecord && setDetail(props.currentRecord) + } + ) + watch( + () => state.json, + () => { + const { rules: fieldsRules, elements: fieldsElements } = + getElementByJson( + state.json, + state.detailForm, + t, + 'security.alarm_instance' + ) + rules.value = fieldsRules + elements.value = fieldsElements + } + ) + + onMounted(() => { + initForm() + }) + + return { + t, + ...toRefs(state), + ...toRefs(status), + meta, + rules, + elements, + onChangePlugin, + onSubmit, + onCancel + } + }, + render(props: { currentRecord: IRecord }) { + const { + show, + t, + meta, + rules, + elements, + detailForm, + uiPlugins, + pluginsLoading, + loading, + saving, + onChangePlugin, + onCancel, + onSubmit + } = this + const { currentRecord } = props + return ( + void onCancel()} + > + {{ + default: () => ( +
+ ) + }, + { + path: 'pluginDefineId', + label: t('security.alarm_instance.select_plugin'), + widget: ( + + ) + }, + ...elements + ] + }} + layout={{ + cols: 24 + }} + /> + ) + }} + + ) + } +}) + +export default DetailModal diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/index.module.scss b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/index.module.scss new file mode 100644 index 0000000000..fc5f4cc948 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/index.module.scss @@ -0,0 +1,32 @@ +/* + * 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. + */ + +.conditions { + display: flex; + justify-content: space-between; + align-items: center; +} +.conditions-search-input { + width: 250px; +} +.pagination { + margin-top: 20px; + justify-content: center; +} +.mt-8 { + margin-top: 8px; +} diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/index.tsx b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/index.tsx new file mode 100644 index 0000000000..0c3b08649b --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/index.tsx @@ -0,0 +1,170 @@ +/* + * 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, ref, toRefs } from 'vue' +import { + NButton, + NInput, + NIcon, + NDataTable, + NPagination, + NSpace +} from 'naive-ui' +import Card from '@/components/card' +import DetailModal from './detail' +import { SearchOutlined } from '@vicons/antd' +import { useI18n } from 'vue-i18n' +import { useUserInfo } from './use-userinfo' +import { useColumns } from './use-columns' +import { useTable } from './use-table' +import styles from './index.module.scss' +import type { IRecord } from './types' + +const AlarmInstanceManage = defineComponent({ + name: 'alarm-instance-manage', + setup() { + const { t } = useI18n() + const showDetailModal = ref(false) + const currentRecord = ref() + + const { IS_ADMIN } = useUserInfo() + + const { columnsRef } = useColumns( + (record: IRecord, type: 'edit' | 'delete') => { + if (type === 'edit') { + showDetailModal.value = true + currentRecord.value = record + } else { + deleteRecord(record.id) + } + } + ) + + const { data, changePage, changePageSize, deleteRecord, updateList } = + useTable() + + const onCreate = () => { + currentRecord.value = null + showDetailModal.value = true + } + + const onCloseModal = () => { + showDetailModal.value = false + currentRecord.value = {} + } + + onMounted(() => { + changePage(1) + }) + + return { + t, + IS_ADMIN, + showDetailModal, + currentRecord: currentRecord, + columnsRef, + ...toRefs(data), + changePage, + changePageSize, + onCreate, + onCloseModal, + onUpdatedList: updateList + } + }, + render() { + const { + t, + IS_ADMIN, + currentRecord, + showDetailModal, + columnsRef, + list, + page, + pageSize, + itemCount, + loading, + changePage, + changePageSize, + onCreate, + onUpdatedList, + onCloseModal + } = this + + return ( + <> + + {{ + default: () => ( +
+ {IS_ADMIN && ( + {`${t( + 'security.alarm_instance.create' + )} ${t('security.alarm_instance.alarm_instance')}`} + )} + +
+ +
+ + + + + +
+
+ ) + }} +
+ + + + + {IS_ADMIN && ( + + )} + + ) + } +}) +export default AlarmInstanceManage diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/types.ts b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/types.ts new file mode 100644 index 0000000000..12188f326d --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/types.ts @@ -0,0 +1,51 @@ +/* + * 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 type { IPluginId } from '@/service/modules/ui-plugins/types' +import type { TableColumns } from 'naive-ui/es/data-table/src/interface' +import type { IMeta, IJsonItem, IFormItem } from '@/components/form/types' +import type { FormRules } from 'naive-ui' + +interface IRecord { + alertPluginName?: string + createTime?: string + id: number + instanceName: string + pluginDefineId: number + pluginInstanceParams?: string + updateTime?: string +} + +interface IPlugin { + id: number + pluginName: string + pluginParams?: string + pluginType?: string + createTime?: string + updateTime?: string +} + +export { + IPluginId, + IRecord, + IPlugin, + IJsonItem, + IMeta, + IFormItem, + TableColumns, + FormRules +} diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-columns.ts b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-columns.ts new file mode 100644 index 0000000000..f87574b491 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-columns.ts @@ -0,0 +1,100 @@ +/* + * 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 { h } from 'vue' +import { useI18n } from 'vue-i18n' +import { NButton, NIcon, NPopconfirm, NSpace } from 'naive-ui' +import { EditOutlined, DeleteOutlined } from '@vicons/antd' +import { TableColumns } from './types' + +export function useColumns(onCallback: Function) { + const { t } = useI18n() + + const columnsRef: TableColumns = [ + { + title: t('security.alarm_instance.serial_number'), + key: 'index', + render: (rowData, rowIndex) => rowIndex + 1 + }, + { + title: t('security.alarm_instance.alarm_instance_name'), + key: 'instanceName' + }, + { + title: t('security.alarm_instance.alarm_plugin_name'), + key: 'alertPluginName' + }, + { + title: t('security.alarm_instance.create_time'), + key: 'createTime' + }, + { + title: t('security.alarm_instance.update_time'), + key: 'updateTime' + }, + { + title: t('security.alarm_instance.operation'), + key: 'operation', + width: 150, + render: (rowData, rowIndex) => { + return h(NSpace, null, { + default: () => [ + h( + NButton, + { + circle: true, + type: 'info', + onClick: () => void onCallback(rowData, 'edit') + }, + { + default: () => + h(NIcon, null, { default: () => h(EditOutlined) }) + } + ), + h( + NPopconfirm, + { + onPositiveClick: () => void onCallback(rowData, 'delete'), + negativeText: t('security.alarm_instance.cancel'), + positiveText: t('security.alarm_instance.confirm') + }, + { + trigger: () => + h( + NButton, + { + circle: true, + type: 'error' + }, + { + default: () => + h(NIcon, null, { default: () => h(DeleteOutlined) }) + } + ), + default: () => t('security.alarm_instance.delete') + } + ) + ] + }) + } + } + ] + + return { + columnsRef + } +} diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-detail.ts b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-detail.ts new file mode 100644 index 0000000000..05efe48180 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-detail.ts @@ -0,0 +1,78 @@ +/* + * 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 } from 'vue' +import { + createAlertPluginInstance, + updateAlertPluginInstance, + verifyAlertInstanceName +} from '@/service/modules/alert-plugin' +import type { IJsonItem, IRecord } from './types' + +export function useDetail(getFormValues: Function) { + const status = reactive({ + saving: false, + loading: false + }) + + const formatParams = ( + json?: IJsonItem[], + values: { [field: string]: any } = {} + ): string => { + json?.forEach((item) => { + item.value = values[item.field] + }) + return JSON.stringify(json) + } + + const createOrUpdate = async (currentRecord: IRecord, json?: IJsonItem[]) => { + const values = getFormValues() + if (status.saving) return false + status.saving = true + try { + if (currentRecord?.instanceName !== values.instanceName) { + await verifyAlertInstanceName({ + alertInstanceName: values.instanceName + }) + } + + currentRecord?.id + ? await updateAlertPluginInstance( + { + alertPluginInstanceId: values.pluginDefineId, + instanceName: values.instanceName, + pluginInstanceParams: formatParams(json, values) + }, + currentRecord.id + ) + : await createAlertPluginInstance({ + instanceName: values.instanceName, + pluginDefineId: values.pluginDefineId, + pluginInstanceParams: formatParams(json, values) + }) + + status.saving = false + return true + } catch (e) { + window.$message.error((e as Error).message) + status.saving = false + return false + } + } + + return { status, createOrUpdate } +} diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-form.ts b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-form.ts new file mode 100644 index 0000000000..956f3029ed --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-form.ts @@ -0,0 +1,131 @@ +/* + * 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, Ref } from 'vue' +import { useI18n } from 'vue-i18n' +import { + queryUiPluginsByType, + queryUiPluginDetailById +} from '@/service/modules/ui-plugins' +import type { + IPluginId, + IPlugin, + FormRules, + IMeta, + IJsonItem, + IRecord +} from './types' + +export function useForm() { + const { t } = useI18n() + + const initialValues = { + instanceName: '', + pluginDefineId: null + } + + const state = reactive({ + detailFormRef: ref(), + detailForm: { ...initialValues }, + uiPlugins: [], + pluginsLoading: false, + json: [] + } as { detailFormRef: Ref; json: IJsonItem[]; detailForm: { instanceName: string; pluginDefineId: number | null }; pluginsLoading: boolean; uiPlugins: [] }) + + const meta = { + model: state.detailForm, + requireMarkPlacement: 'left', + labelPlacement: 'left', + labelWidth: 180, + rules: { + instanceName: { + trigger: 'input', + required: true, + message: t('security.alarm_instance.alarm_instance_name_tips') + }, + pluginDefineId: { + trigger: ['blur', 'change'], + required: true, + validator(validte, value) { + if (!value && value !== 0) { + return new Error(t('security.alarm_instance.select_plugin_tips')) + } + } + } + } as FormRules + } as IMeta + + const getUiPluginsByType = async () => { + if (state.pluginsLoading) return + state.pluginsLoading = true + try { + const plugins = await queryUiPluginsByType({ pluginType: 'ALERT' }) + state.uiPlugins = plugins.map((plugin: IPlugin) => ({ + label: plugin.pluginName, + value: plugin.id + })) + state.pluginsLoading = false + } catch (err) { + state.uiPlugins = [] + state.pluginsLoading = false + } + } + + const changePlugin = async (pluginId: IPluginId) => { + if (state.pluginsLoading) return + state.pluginsLoading = true + state.detailForm.pluginDefineId = pluginId + try { + const { pluginParams } = await queryUiPluginDetailById(pluginId) + if (pluginParams) { + state.json = JSON.parse(pluginParams) + } + state.pluginsLoading = false + } catch (e) { + window.$message.error((e as Error).message) + state.pluginsLoading = false + } + } + + const initForm = () => { + getUiPluginsByType() + } + + const resetForm = () => { + state.detailFormRef.setValues({ ...initialValues }) + state.json = [] + } + + const getFormValues = () => state.detailForm + + const setDetail = (record: IRecord) => { + state.detailForm.instanceName = record.instanceName + state.detailForm.pluginDefineId = record.pluginDefineId + if (record.pluginInstanceParams) + state.json = JSON.parse(record.pluginInstanceParams) + } + + return { + meta, + state, + setDetail, + initForm, + resetForm, + getFormValues, + changePlugin + } +} diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-table.ts b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-table.ts new file mode 100644 index 0000000000..fcfe7b29a1 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-table.ts @@ -0,0 +1,95 @@ +/* + * 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 } from 'vue' +import { + queryAlertPluginInstanceListPaging, + deleteAlertPluginInstance +} from '@/service/modules/alert-plugin' +import { format } from 'date-fns' +import type { IRecord } from './types' + +export function useTable() { + const data = reactive({ + page: 1, + pageSize: 10, + itemCount: 0, + searchVal: '', + list: [], + loading: false + }) + + const getList = async () => { + if (data.loading) return + data.loading = true + + try { + const { totalList, total } = await queryAlertPluginInstanceListPaging({ + pageNo: data.page, + pageSize: data.pageSize, + searchVal: data.searchVal + }) + data.loading = false + if (!totalList) throw Error() + data.list = totalList.map((record: IRecord) => { + record.createTime = record.createTime + ? format(new Date(record.createTime), 'yyyy-MM-dd HH:mm:ss') + : '' + record.updateTime = record.updateTime + ? format(new Date(record.updateTime), 'yyyy-MM-dd HH:mm:ss') + : '' + return record + }) + + data.itemCount = total + } catch (e) { + if ((e as Error).message) window.$message.error((e as Error).message) + data.loading = false + data.list = [] + data.itemCount = 0 + } + } + + const updateList = () => { + if (data.list.length === 1 && data.page > 1) { + --data.page + } + getList() + } + + const deleteRecord = async (id: number) => { + try { + const res = await deleteAlertPluginInstance(id) + updateList() + } catch (e) { + window.$message.error((e as Error).message) + } + } + + const changePage = (page: number) => { + data.page = page + getList() + } + + const changePageSize = (pageSize: number) => { + data.page = 1 + data.pageSize = pageSize + getList() + } + + return { data, changePage, changePageSize, deleteRecord, updateList } +} diff --git a/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-userinfo.ts b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-userinfo.ts new file mode 100644 index 0000000000..968d83040b --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/alarm-instance-manage/use-userinfo.ts @@ -0,0 +1,28 @@ +/* + * 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 { useUserStore } from '@/store/user/user' +import type { UserInfoRes } from '@/service/modules/users/types' + +export function useUserInfo() { + const userStore = useUserStore() + const userInfo = userStore.getUserInfo as UserInfoRes + + const IS_ADMIN = userInfo.userType === 'ADMIN_USER' + + return { IS_ADMIN } +}