feat(sj_1.0.0): 新增通知人配置

This commit is contained in:
opensnail 2024-04-17 23:20:57 +08:00
parent 78696ca88e
commit d13f42b2eb
15 changed files with 490 additions and 10 deletions

View File

@ -161,6 +161,8 @@ const local: App.I18n.Schema = {
manage: 'System Manage',
manage_user: 'User Manage',
notify: 'notify',
notify_recipient: 'Notify recipient',
notify_scene: 'Notify scene',
'manage_user-detail': 'User Detail',
manage_role: 'Role Manage',
manage_menu: 'Menu Manage',
@ -520,6 +522,21 @@ const local: App.I18n.Schema = {
},
addNotifyConfig: 'Add Alarm notification',
editNotifyConfig: 'Add Alarm notification'
},
notifyRecipient: {
title: 'NotifyRecipient List',
recipientName: 'Recipient name',
notifyType: 'Notification type',
notifyAttribute: 'Attribute information',
description: 'Describe',
form: {
description: 'Please enter Describe',
notifyAttribute: 'Please enter Attribute information',
recipientName: 'Please enter Recipient name',
notifyType: 'lease select Notification type'
},
addNotifyRecipient: 'Add Notify recipients',
editNotifyRecipient: 'Add Notify recipients'
}
},
form: {

View File

@ -161,6 +161,8 @@ const local: App.I18n.Schema = {
manage: '系统管理',
manage_user: '用户管理',
notify: '告警通知',
notify_recipient: '通知人',
notify_scene: '通知场景',
'manage_user-detail': '用户详情',
manage_role: '角色管理',
manage_menu: '菜单管理',
@ -516,6 +518,21 @@ const local: App.I18n.Schema = {
},
addNotifyConfig: '新增告警通知',
editNotifyConfig: '编辑告警通知'
},
notifyRecipient: {
title: 'NotifyRecipient 列表',
recipientName: '接收人名称',
notifyType: '通知类型',
notifyAttribute: '属性信息',
description: '描述',
form: {
description: '请输入描述',
notifyAttribute: '请输入属性信息',
recipientName: '请输入接收人名称',
notifyType: '请选择通知类型'
},
addNotifyRecipient: '新增通知接收人',
editNotifyRecipient: '编辑通知接收人'
}
},
form: {

View File

@ -36,7 +36,8 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
"multi-menu_first_child": () => import("@/views/multi-menu/first_child/index.vue"),
"multi-menu_second_child_home": () => import("@/views/multi-menu/second_child_home/index.vue"),
namepase: () => import("@/views/namepase/index.vue"),
notify: () => import("@/views/notify/index.vue"),
notify_recipient: () => import("@/views/notify/recipient/index.vue"),
notify_scene: () => import("@/views/notify/scene/index.vue"),
pods: () => import("@/views/pods/index.vue"),
"user-center": () => import("@/views/user-center/index.vue"),
};

View File

@ -335,11 +335,31 @@ export const generatedRoutes: GeneratedRoute[] = [
{
name: 'notify',
path: '/notify',
component: 'layout.base$view.notify',
component: 'layout.base',
meta: {
title: 'notify',
i18nKey: 'route.notify'
}
},
children: [
{
name: 'notify_recipient',
path: '/notify/recipient',
component: 'view.notify_recipient',
meta: {
title: 'notify_recipient',
i18nKey: 'route.notify_recipient'
}
},
{
name: 'notify_scene',
path: '/notify/scene',
component: 'view.notify_scene',
meta: {
title: 'notify_scene',
i18nKey: 'route.notify_scene'
}
}
]
},
{
name: 'pods',

View File

@ -114,14 +114,14 @@ function transformElegantRouteToVueRoute(
}
}
// add redirect to child
if (children?.length && !vueRoute.redirect) {
vueRoute.redirect = {
name: children[0].name
};
}
if (children?.length) {
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
@ -176,6 +176,8 @@ const routeMap: RouteMap = {
"multi-menu_second_child_home": "/multi-menu/second/child/home",
"namepase": "/namepase",
"notify": "/notify",
"notify_recipient": "/notify/recipient",
"notify_scene": "/notify/scene",
"pods": "/pods",
"user-center": "/user-center"
};

View File

@ -26,3 +26,30 @@ export function fetchEditNotify(data: Api.NotifyConfig.NotifyConfig) {
data
});
}
/** get notify recipient list */
export function fetchGetNotifyRecipientList(params?: Api.NotifyRecipient.NotifyRecipientParams) {
return request<Api.NotifyRecipient.NotifyRecipientList>({
url: '/notify-recipient/page/list',
method: 'get',
params
});
}
/** add notify recipient */
export function fetchAddNotifyRecipient(data: Api.NotifyRecipient.NotifyRecipient) {
return request<boolean>({
url: '/notify-config',
method: 'post',
data
});
}
/** edit notify recipient */
export function fetchEditNotifyRecipient(data: Api.NotifyRecipient.NotifyRecipient) {
return request<boolean>({
url: '/notify-config',
method: 'put',
data
});
}

30
src/typings/api.d.ts vendored
View File

@ -475,4 +475,34 @@ declare namespace Api {
} & NotifyConfig
>;
}
/**
* namespace Notify-recipient
*
* backend api module: "notify-recipient"
*/
namespace NotifyRecipient {
type CommonSearchParams = Pick<Common.PaginatingCommonParams, 'page' | 'size'>;
/** notifyRecipient */
type NotifyRecipient = Common.CommonRecord<{
/** 接收人名称 */
recipientName: string;
/** 通知类型 */
notifyType: string;
/** 属性信息 */
notifyAttribute: string;
/** 描述 */
description: string;
}>;
/** notifyRecipient search params */
type NotifyRecipientParams = CommonType.RecordNullable<
Pick<Api.NotifyRecipient.NotifyRecipient, 'recipientName' | 'notifyType' | 'notifyAttribute' | 'description'> &
CommonSearchParams
>;
/** notifyRecipient list */
type NotifyRecipientList = Common.PaginatingQueryRecord<NotifyRecipient>;
}
}

15
src/typings/app.d.ts vendored
View File

@ -694,6 +694,21 @@ declare namespace App {
addNotifyConfig: string;
editNotifyConfig: string;
};
notifyRecipient: {
title: string;
recipientName: string;
notifyType: string;
notifyAttribute: string;
description: string;
form: {
description: string;
notifyAttribute: string;
recipientName: string;
notifyType: string;
};
addNotifyRecipient: string;
editNotifyRecipient: string;
};
};
form: {
required: string;

View File

@ -50,6 +50,8 @@ declare module "@elegant-router/types" {
"multi-menu_second_child_home": "/multi-menu/second/child/home";
"namepase": "/namepase";
"notify": "/notify";
"notify_recipient": "/notify/recipient";
"notify_scene": "/notify/scene";
"pods": "/pods";
"user-center": "/user-center";
};
@ -91,7 +93,6 @@ declare module "@elegant-router/types" {
| "404"
| "500"
| "about"
| "demo-route"
| "function"
| "home"
| "login"
@ -123,7 +124,6 @@ declare module "@elegant-router/types" {
| "500"
| "login"
| "about"
| "demo-route_child"
| "function_hide-child_one"
| "function_hide-child_three"
| "function_hide-child_two"
@ -140,7 +140,8 @@ declare module "@elegant-router/types" {
| "multi-menu_first_child"
| "multi-menu_second_child_home"
| "namepase"
| "notify"
| "notify_recipient"
| "notify_scene"
| "pods"
| "user-center"
>;

View File

@ -0,0 +1,157 @@
<script setup lang="tsx">
import { NButton, NPopconfirm } from 'naive-ui';
import { fetchGetNotifyRecipientList } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
import NotifyRecipientOperateDrawer from './modules/notify-recipient-operate-drawer.vue';
import NotifyRecipientSearch from './modules/notify-recipient-search.vue';
const appStore = useAppStore();
const { columns, columnChecks, data, getData, loading, mobilePagination, searchParams, resetSearchParams } = useTable({
apiFn: fetchGetNotifyRecipientList,
apiParams: {
page: 1,
size: 10,
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
// the value can not be undefined, otherwise the property in Form will not be reactive
recipientName: '',
notifyType: null
},
columns: () => [
{
type: 'selection',
align: 'center',
width: 48
},
{
key: 'index',
title: $t('common.index'),
align: 'center',
width: 64
},
{
key: 'recipientName',
title: $t('page.notifyRecipient.recipientName'),
align: 'left',
minWidth: 120
},
{
key: 'notifyType',
title: $t('page.notifyRecipient.notifyType'),
align: 'left',
minWidth: 120
},
{
key: 'notifyAttribute',
title: $t('page.notifyRecipient.notifyAttribute'),
align: 'left',
minWidth: 120
},
{
key: 'description',
title: $t('page.notifyRecipient.description'),
align: 'left',
minWidth: 120
},
{
key: 'operate',
title: $t('common.operate'),
align: 'center',
width: 130,
render: row => (
<div class="flex-center gap-8px">
<NButton type="primary" ghost size="small" onClick={() => edit(row.id!)}>
{$t('common.edit')}
</NButton>
<NPopconfirm onPositiveClick={() => handleDelete(row.id!)}>
{{
default: () => $t('common.confirmDelete'),
trigger: () => (
<NButton type="error" ghost size="small">
{$t('common.delete')}
</NButton>
)
}}
</NPopconfirm>
</div>
)
}
]
});
const {
drawerVisible,
operateType,
editingData,
handleAdd,
handleEdit,
checkedRowKeys,
onBatchDeleted,
onDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleBatchDelete() {
// request
console.log(checkedRowKeys.value);
onBatchDeleted();
}
function handleDelete(id: string) {
// request
console.log(id);
onDeleted();
}
function edit(id: string) {
handleEdit(id);
}
</script>
<template>
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<NotifyRecipientSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
<NCard
:title="$t('page.notifyRecipient.title')"
:bordered="false"
size="small"
class="sm:flex-1-hidden card-wrapper"
header-class="view-card-header"
>
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
/>
</template>
<NDataTable
v-model:checked-row-keys="checkedRowKeys"
:columns="columns"
:data="data"
:flex-height="!appStore.isMobile"
:scroll-x="962"
:loading="loading"
remote
:row-key="row => row.id"
:pagination="mobilePagination"
class="sm:h-full"
/>
<NotifyRecipientOperateDrawer
v-model:visible="drawerVisible"
:operate-type="operateType"
:row-data="editingData"
@submitted="getData"
/>
</NCard>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,120 @@
<script setup lang="ts">
import { computed, reactive, watch } from 'vue';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import OperateDrawer from '@/components/common/operate-drawer.vue';
import { $t } from '@/locales';
import { fetchAddNotifyRecipient, fetchEditNotifyRecipient } from '@/service/api';
defineOptions({
name: 'NotifyRecipientOperateDrawer'
});
interface Props {
/** the type of operation */
operateType: NaiveUI.TableOperateType;
/** the edit row data */
rowData?: Api.NotifyRecipient.NotifyRecipient | null;
}
const props = defineProps<Props>();
interface Emits {
(e: 'submitted'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
const { formRef, validate, restoreValidation } = useNaiveForm();
const { defaultRequiredRule } = useFormRules();
const title = computed(() => {
const titles: Record<NaiveUI.TableOperateType, string> = {
add: $t('page.notifyRecipient.addNotifyRecipient'),
edit: $t('page.notifyRecipient.editNotifyRecipient')
};
return titles[props.operateType];
});
type Model = Pick<
Api.NotifyRecipient.NotifyRecipient,
'id' | 'recipientName' | 'notifyType' | 'notifyAttribute' | 'description'
>;
const model: Model = reactive(createDefaultModel());
function createDefaultModel(): Model {
return {
recipientName: '',
notifyType: '',
notifyAttribute: '',
description: ''
};
}
type RuleKey = Extract<keyof Model, 'recipientName' | 'notifyType' | 'notifyAttribute' | 'description'>;
const rules: Record<RuleKey, App.Global.FormRule> = {
recipientName: defaultRequiredRule,
notifyType: defaultRequiredRule,
notifyAttribute: defaultRequiredRule,
description: defaultRequiredRule
};
function handleUpdateModelWhenEdit() {
if (props.operateType === 'add') {
Object.assign(model, createDefaultModel());
return;
}
if (props.operateType === 'edit' && props.rowData) {
Object.assign(model, props.rowData);
}
}
function closeDrawer() {
visible.value = false;
}
async function handleSubmit() {
await validate();
// request
if (props.operateType === 'add') {
const { recipientName, notifyAttribute, notifyType, description } = model;
fetchAddNotifyRecipient({ recipientName, notifyAttribute, notifyType, description });
}
if (props.operateType === 'edit') {
const { id, recipientName, notifyAttribute, notifyType, description } = model;
fetchEditNotifyRecipient({ id, recipientName, notifyAttribute, notifyType, description });
}
window.$message?.success($t('common.updateSuccess'));
closeDrawer();
emit('submitted');
}
watch(visible, () => {
if (visible.value) {
handleUpdateModelWhenEdit();
restoreValidation();
}
});
</script>
<template>
<OperateDrawer v-model="visible" :title="title" @handle-submit="handleSubmit">
<NForm ref="formRef" :model="model" :rules="rules">
<NFormItem :label="$t('page.notifyRecipient.recipientName')" path="recipientName">
<NInput v-model:value="model.recipientName" :placeholder="$t('page.notifyRecipient.form.recipientName')" />
</NFormItem>
<NFormItem :label="$t('page.notifyRecipient.notifyType')" path="recipientName">
<NSelect v-model:value="model.notifyType" :placeholder="$t('page.notifyRecipient.form.notifyType')" clearable />
</NFormItem>
</NForm>
</OperateDrawer>
</template>
<style scoped></style>

View File

@ -0,0 +1,73 @@
<script setup lang="ts">
import { ref } from 'vue';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useNaiveForm } from '@/hooks/common/form';
defineOptions({
name: 'NotifyRecipientSearch'
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
const appStore = useAppStore();
const title = ref(appStore.isMobile ? $t('common.search') : undefined);
const { formRef, validate, restoreValidation } = useNaiveForm();
const model = defineModel<Api.NotifyRecipient.NotifyRecipientParams>('model', { required: true });
async function reset() {
await restoreValidation();
emit('reset');
}
async function search() {
await validate();
emit('search');
}
</script>
<template>
<NCard :title="title" :bordered="false" size="small" class="card-wrapper">
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80" :show-feedback="appStore.isMobile">
<NGrid responsive="screen" item-responsive>
<NFormItemGi
span="24 s:12 m:6"
:label="$t('page.notifyRecipient.recipientName')"
path="userName"
class="pr-24px"
>
<NInput v-model:value="model.recipientName" :placeholder="$t('page.notifyRecipient.form.recipientName')" />
</NFormItemGi>
<NFormItemGi span="24 s:12 m:6" :label="$t('page.notifyRecipient.notifyType')" path="userName" class="pr-24px">
<NSelect v-model:value="model.notifyType" :placeholder="$t('page.notifyRecipient.notifyType')" clearable />
</NFormItemGi>
<NFormItemGi span="24 m:12" class="pr-24px">
<NSpace class="w-full" justify="end">
<NButton @click="reset">
<template #icon>
<icon-ic-round-refresh class="text-icon" />
</template>
{{ $t('common.reset') }}
</NButton>
<NButton type="primary" ghost @click="search">
<template #icon>
<icon-ic-round-search class="text-icon" />
</template>
{{ $t('common.search') }}
</NButton>
</NSpace>
</NFormItemGi>
</NGrid>
</NForm>
</NCard>
</template>
<style scoped></style>

View File

@ -4,8 +4,8 @@ import { fetchGetNotifyConfigList } from '@/service/api';
import { $t } from '@/locales';
import { useAppStore } from '@/store/modules/app';
import { useTable, useTableOperate } from '@/hooks/common/table';
import NotifyConfigOperateDrawer from './modules/notify-config-operate-drawer.vue';
import NotifyConfigSearch from './modules/notify-config-search.vue';
import NotifyConfigOperateDrawer from '@/views/notify/scene/modules/notify-config-operate-drawer.vue';
import NotifyConfigSearch from '@/views/notify/scene/modules/notify-config-search.vue';
const appStore = useAppStore();