feat: 订阅管理

This commit is contained in:
100011797 2023-06-16 10:24:44 +08:00
parent 0a479ee058
commit 2b740da418
22 changed files with 1214 additions and 819 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -30,3 +30,8 @@ export const checkOldPassword_api = (password:string) => server.post(`/user/me/p
'Content-Type': 'text/plain'
}
});
// 我的订阅
// 查询当前用户可访问的通道配置
export const getAllNotice = () => server.get(`/notify/channel/all`);

View File

@ -1,4 +1,26 @@
import server from '@/utils/request';
// 获取角色列表
export const queryRoleList = (data: any): Promise<any> => server.post(`/role/_query/`, data);
export const queryRoleList = (data: any): Promise<any> => server.post(`/role/_query/`, data);
// 查询所有通道配置
export const queryChannelConfig = (): Promise<any> => server.get(`/notify/channel/all-for-save`);
// 查询通知通道类型
export const queryChannelProviders = (): Promise<any> => server.get(`/notify/channel/providers`);
// 保存通道配置
export const saveChannelConfig = (data: any[]): Promise<any> => server.patch(`/notify/channel`, data);
export const updateChannelConfig = (providerId: string, data: any[]): Promise<any> => server.patch(`/notify/channel/${providerId}`, data);
export const editChannelConfig = (providerId: string, data: any): Promise<any> => server.put(`/notify/channel/${providerId}`, data);
export const actionChannelConfig = (providerId: string, type: 'enable' | 'disable'): Promise<any> => server.post(`/notify/channel/${providerId}/${type}`);
export const deleteChannelConfig = (providerId: string): Promise<any> => server.remove(`/notify/channel/${providerId}`);
export const queryConfigVariables = (providerId: string): Promise<any> => server.get(`/notify/channel/${providerId}/variables`);

View File

@ -1,39 +1,42 @@
<template>
<div class="box">
<div class="content">
<div class="content-item" v-for="item in bindList" :key="item.id">
<div class="content-item-left">
<img
:src="item.logoUrl || getImage(bindIcon[item.provider])"
style="height: 50px; width: 50px"
width="50px"
height="50px"
alt=""
/>
<Ellipsis style="max-width: 200px; font-size: 22px">{{
item?.name
}}</Ellipsis>
<template v-if="bindList.length">
<div class="content-item" v-for="item in bindList" :key="item.id">
<div class="content-item-left">
<img
:src="item.logoUrl || getImage(bindIcon[item.provider])"
style="height: 50px; width: 50px"
width="50px"
height="50px"
alt=""
/>
<Ellipsis style="max-width: 200px; font-size: 22px">{{
item?.name
}}</Ellipsis>
<div>
<j-tag v-if="item.bound">已绑定</j-tag>
<j-tag v-else>未绑定</j-tag>
</div>
<div v-if="item.others?.name">
绑定名{{ item.others?.name }}
</div>
</div>
<div>
<j-tag v-if="item.bound">已绑定</j-tag>
<j-tag v-else>未绑定</j-tag>
</div>
<div v-if="item.others?.name">
绑定名{{ item.others?.name }}
<j-popconfirm
v-if="item.bound"
title="确认解除绑定嘛?"
@confirm="() => unBind(item.id)"
>
<j-button>解除绑定</j-button>
</j-popconfirm>
<j-button v-else type="primary" @click="clickBind(item.id)"
>立即绑定</j-button
>
</div>
</div>
<div>
<j-popconfirm
v-if="item.bound"
title="确认解除绑定嘛?"
@confirm="() => unBind(item.id)"
>
<j-button>解除绑定</j-button>
</j-popconfirm>
<j-button v-else type="primary" @click="clickBind(item.id)"
>立即绑定</j-button
>
</div>
</div>
</template>
<j-empty style="margin: 200px 0;" />
</div>
</div>
</template>

View File

@ -1,8 +1,11 @@
<template>
<div style="margin-top: 24px;">
<j-tabs tab-position="left">
<j-tab-pane v-for="item in tabs" :key="item.key" :tab="item.tab"><NotificationRecord /></j-tab-pane>
<j-tabs tab-position="left" v-if="tabs.length">
<j-tab-pane v-for="item in tabs" :key="item.key" :tab="item.tab">
<NotificationRecord />
</j-tab-pane>
</j-tabs>
<j-empty v-else />
</div>
</template>

View File

@ -1,23 +0,0 @@
<template>
<div>
<div><j-button type="link">取消订阅</j-button></div>
<template v-if="['dingTalk', 'weixin'].includes(type)">
<div><j-button type="link">更换接收账号</j-button></div>
<p>当前绑定的账号名称</p>
</template>
<template v-else>
<div><j-button type="link">更换接收账号</j-button></div>
</template>
</div>
</template>
<script lang="ts" setup>
import { PropType } from "vue";
type Type = 'dingTalk' | 'weixin' | 'email' | 'voice' | 'sms';
const props = defineProps({
type: {
type: String as PropType<Type>,
default: 'dingTalk',
},
});
</script>

View File

@ -1,39 +0,0 @@
<template>
<j-modal visible @cancel="emit('close')">
<template v-if="type === 'dingTalk'">
<p>请先绑定钉钉账号</p>
</template>
<template v-else-if="type === 'weixin'">
<p>请先绑定企业微信账号</p>
</template>
<template v-else-if="type === 'email'">
<p>请先绑定邮箱</p>
</template>
<template v-else>
<p>请先绑定手机号</p>
</template>
<template #footer>
<j-button @click="emit('close')">确定</j-button>
<j-button @click="onBind" type="primary" v-if="['voice', 'sms'].includes(type)">立即绑定</j-button>
</template>
</j-modal>
</template>
<script lang="ts" setup>
import { onlyMessage } from '@/utils/comm';
import { PropType } from 'vue';
type Type = 'dingTalk' | 'weixin' | 'email' | 'voice' | 'sms';
const emit = defineEmits(['close']);
const props = defineProps({
type: {
type: String as PropType<Type>,
default: 'dingTalk',
},
});
const onBind = () => {
onlyMessage('打开详情编辑框')
}
</script>

View File

@ -0,0 +1,286 @@
<template>
<div class="child-item">
<div class="child-item-left">
<div style="font-weight: 600">
{{ data?.name }}
</div>
<div class="child-item-left-auth" v-if="data?.description">
<j-tooltip :title="data.description">
<AIcon
style="font-size: 20px"
type="ExclamationCircleOutlined"
/>
</j-tooltip>
</div>
</div>
<div class="child-item-right">
<MCarousel :data="data?.channels">
<template #card="slotProps">
<div class="box-item">
<div class="box-item-img">
<j-popover
trigger="click"
:visible="show?.[slotProps?.id]"
@visibleChange="onVisibleChange(slotProps)"
>
<img
:src="
getImage(
`/notice/${noticeType.get(
slotProps?.channelProvider,
)}.png`,
)
"
/>
<template
#content
v-if="
notifyChannels?.includes(
slotProps?.id,
) &&
slotProps?.channelProvider !==
'inside-mail'
"
>
<div>
<PermissionButton
type="link"
:hasPermission="true"
@click="onUnSubscribe(slotProps)"
>
取消订阅
</PermissionButton>
</div>
<div>
<PermissionButton
type="link"
:hasPermission="true"
>
更换接收账号
</PermissionButton>
</div>
</template>
</j-popover>
<div class="box-item-checked">
<j-checkbox
:checked="
notifyChannels?.includes(slotProps?.id)
"
></j-checkbox>
</div>
<div
:class="{
disabled: !notifyChannels?.includes(
slotProps?.id,
),
}"
></div>
</div>
<div class="box-item-text">
{{ slotProps?.name }}
</div>
</div>
</template>
</MCarousel>
</div>
</div>
<Unsubscribe
@save="onSubscribe"
v-if="visible"
:data="props.data"
:current="current"
@close="visible = false"
/>
</template>
<script lang="ts" setup>
import { getImage, onlyMessage } from '@/utils/comm';
import MCarousel from '@/components/MCarousel/index.vue';
import Unsubscribe from './Unsubscribe.vue';
import { remove_api, save_api } from '@/api/account/notificationSubscription';
import { useUserInfo } from '@/store/userInfo';
const noticeType = new Map();
noticeType.set('notifier-dingTalk', 'dingtalk');
noticeType.set('notifier-weixin', 'wechat');
noticeType.set('notifier-email', 'email');
noticeType.set('notifier-voice', 'voice');
noticeType.set('notifier-sms', 'sms');
noticeType.set('inside-mail', 'inside-mail');
const current = ref<any>({});
const visible = ref<boolean>(false);
const show = ref<any>()
const user = useUserInfo();
const props = defineProps({
data: {
type: Object,
default: () => {},
},
subscribe: {
type: Object,
default: () => {},
},
});
const emits = defineEmits(['refresh']);
const notifyChannels = computed(() => {
return props.subscribe?.notifyChannels || [];
});
const onSubscribe = async (obj: any) => {
const _obj = {
subscribeName: obj.name,
topicProvider: props.data?.provider,
providerId: obj.providerId,
...props.subscribe,
notifyChannels: [...(props.subscribe?.notifyChannels || []), obj?.id],
};
const resp = await save_api(_obj);
if (resp.status === 200) {
onlyMessage('操作成功');
emits('refresh');
} else {
onlyMessage('操作失败', 'error');
}
};
const onUnSubscribe = async (obj: any) => {
// const resp = await remove_api(id);
// if (resp.status === 200) {
// onlyMessage('');
// emits('refresh');
// }
const _set = new Set(props.subscribe?.notifyChannels || [])
_set.delete(obj?.id)
const _obj = {
subscribeName: obj.name,
topicProvider: props.data?.provider,
providerId: obj.providerId,
...props.subscribe,
notifyChannels: [..._set.values()],
};
const resp = await save_api(_obj);
if (resp.status === 200) {
onlyMessage('操作成功');
emits('refresh');
} else {
onlyMessage('操作失败', 'error');
}
};
const onCheckChange = async (_data: any) => {
let _bind: boolean = false;
if (_data?.channelProvider !== 'inside-mail') {
//
if (
['notifier-voice', 'notifier-sms'].includes(_data?.channelProvider)
) {
if (user.userInfos?.telephone) {
_bind = true;
}
} else if (_data?.channelProvider === 'notifier-email') {
if (user.userInfos?.email) {
_bind = true;
}
} else {
//
}
}
if (_data?.channelProvider === 'inside-mail' || _bind) {
onSubscribe(_data);
} else {
visible.value = true;
current.value = _data;
}
};
const onVisibleChange = (_data: any) => {
show.value = {}
if (notifyChannels.value?.includes(_data?.id)) {
if (_data?.channelProvider === 'inside-mail') {
onUnSubscribe(_data);
} else {
show.value[_data.id] = true
}
} else {
onCheckChange(_data)
}
};
</script>
<style lang="less" scoped>
.child-item {
padding: 10px 20px;
margin: 5px;
background: #f7f7f7;
display: flex;
justify-content: space-between;
align-items: center;
.child-item-left {
display: flex;
align-items: center;
height: 80px;
div {
display: flex;
margin-right: 30px;
flex-direction: column;
justify-content: center;
align-items: center;
}
.child-item-left-auth {
cursor: pointer;
}
}
.child-item-right {
display: flex;
.box-item {
margin-left: 10px;
.box-item-img {
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
img {
width: 100%;
z-index: 1;
}
.box-item-checked {
position: absolute;
top: -10px;
right: -10px;
z-index: 3;
}
.disabled {
background-color: rgba(#000, 0.38);
position: absolute;
width: 48px;
height: 48px;
z-index: 2;
top: 0;
left: 0;
}
}
.box-item-text {
width: 100%;
text-align: center;
height: 20px;
}
}
}
}
</style>

View File

@ -0,0 +1,66 @@
<template>
<j-modal visible @cancel="emit('close')">
<template v-if="getType === 'notifier-dingTalk'">
<div class="tip">请先绑定钉钉账号</div>
</template>
<template v-else-if="getType === 'notifier-weixin'">
<div class="tip">请先绑定企业微信账号</div>
</template>
<template v-else-if="getType === 'notifier-email'">
<div class="tip">请先绑定邮箱</div>
</template>
<template v-else>
<div class="tip">请先绑定手机号</div>
</template>
<template #footer>
<j-button @click="emit('close')">取消</j-button>
<j-button @click="onBind" type="primary" v-if="['notifier-email', 'notifier-voice', 'notifier-sms'].includes(getType)">立即绑定</j-button>
<j-button v-else @click="emit('close')">确定</j-button>
</template>
<EditInfo v-if="editInfoVisible" :data="user.userInfos" @close="editInfoVisible = false" @save="onSave" />
</j-modal>
</template>
<script lang="ts" setup>
import { useUserInfo } from '@/store/userInfo';
import EditInfo from '../../EditInfo/index.vue'
const user = useUserInfo();
const emit = defineEmits(['close', 'save']);
const props = defineProps({
data: { //
type: Object,
default: () => {},
},
current: { //
type: Object,
default: () => {},
},
});
const editInfoVisible = ref<boolean>(false)
const getType = computed(() => {
return props.current?.channelProvider
})
const onBind = () => {
editInfoVisible.value = true
}
const onSave = () => {
editInfoVisible.value = false
emit('save', props.current)
emit('close')
}
</script>
<style lang="less" scoped>
.tip {
width: 100%;
margin: 80px 0;
text-align: center;
font-size: 14px;
color: #7F7F7F;
}
</style>

View File

@ -1,227 +1,164 @@
<template>
<div style="margin-top: 24px">
<div class="alert">
<AIcon type="InfoCircleOutlined" />
你可以在该页面选择需要订阅的主题及接收通知的方式
</div>
<div style="margin-top: 20px">
<j-collapse :bordered="false" v-model:activeKey="activeKey">
<template #expandIcon="{ isActive }">
<AIcon
type="CaretRightOutlined"
:rotate="isActive ? 90 : 0"
/>
</template>
<j-collapse-panel
v-for="item in dataSource"
:key="item.id"
class="custom"
>
<template #header
><h3>{{ item.name }}</h3></template
>
<div class="child">
<template
v-for="child in item.children"
:key="child.id"
>
<div class="child-item">
<div class="child-item-left">
<div style="font-weight: 600">
{{ child.name }}
</div>
<div class="child-item-left-auth">
<j-tooltip
title="当产品类型的告警被触发时,你将在已订阅的方式中收到通知"
>
<AIcon
type="ExclamationCircleOutlined"
/>
</j-tooltip>
</div>
</div>
<div class="child-item-right">
<MCarousel :data="child.children">
<template #card="slotProps">
<div class="box-item">
<j-popover>
<div class="box-item-img">
<img
style="width: 100%"
:src="
getImage(
`/notice/${slotProps?.type}.png`,
)
"
/>
<div
class="box-item-checked"
>
<j-checkbox
:checked="
!slotProps?.type
"
></j-checkbox>
</div>
</div>
<template #content>
<Detail />
<!-- <Error v-else /> -->
</template>
</j-popover>
<div class="box-item-text">
{{ item.name }}
</div>
</div>
</template>
</MCarousel>
</div>
</div>
<j-spin :spinning="loading">
<div style="margin-top: 24px">
<div class="alert">
<AIcon type="InfoCircleOutlined" />
你可以在该页面选择需要订阅的主题及接收通知的方式
</div>
<div style="margin-top: 20px">
<template v-if="dataSource.length">
<j-collapse :bordered="false" v-model:activeKey="activeKey">
<template #expandIcon="{ isActive }">
<AIcon
type="CaretRightOutlined"
:style="{
transform: `rotate(${
isActive ? 90 : 0
}deg)`,
}"
/>
</template>
</div>
</j-collapse-panel>
</j-collapse>
<j-collapse-panel
v-for="item in dataSource"
:key="item.provider"
class="custom"
>
<template #header
><h3>{{ item.name }}</h3></template
>
<div class="child">
<template
v-for="child in item.children"
:key="child.id"
>
<Item
@refresh="handleSearch"
:data="child"
:subscribe="
subscribe.find(
(i) =>
i?.topicProvider ===
child?.provider,
)
"
/>
</template>
</div>
</j-collapse-panel>
</j-collapse>
</template>
<j-empty style="margin: 200px 0" v-else />
</div>
</div>
</div>
</j-spin>
</template>
<script lang="ts" setup>
import { getImage } from '@/utils/comm';
import MCarousel from '@/components/MCarousel/index.vue';
import Detail from './components/Detail.vue';
import Error from './components/Error.vue'
import { getAllNotice } from '@/api/account/center';
import { getNoticeList_api } from '@/api/account/notificationSubscription';
import Item from './components/Item.vue';
const dataSource = ref([
const initData = [
{
id: 'a',
provider: 'alarm',
name: '告警',
children: [
{
id: 'product',
provider: 'alarm-product',
name: '产品告警',
active: true,
children: [
{
id: 'sms9',
name: '站内信',
type: 'sms',
},
{
id: 'dingtalk8',
name: '钉钉',
type: 'dingtalk',
},
{
id: 'wechat7',
name: '微信',
type: 'wechat',
},
{
id: 'email6',
name: '邮箱',
type: 'email',
},
{
id: 'dingtalk5',
name: '钉钉',
type: 'dingtalk',
},
{
id: 'wechat4',
name: '微信',
type: 'wechat',
},
{
id: 'email3',
name: '邮箱',
type: 'email',
},
{
id: 'email2',
name: '邮箱',
type: 'email',
},
{
id: 'email1',
name: '邮箱',
type: 'email',
},
],
description:
'当产品类型的告警被触发时,你将在已订阅的方式中收到通知',
},
{
id: 'device',
provider: 'alarm-device',
name: '设备告警',
active: false,
children: [
{
id: 'sms11',
name: '站内信',
type: 'sms',
},
{
id: 'wechat11',
name: '微信',
type: 'wechat',
},
{
id: 'voice11',
name: '语音',
type: 'voice',
},
],
description:
'当设备类型的告警被触发时,你将在已订阅的方式中收到通知',
},
{
provider: 'alarm-org',
name: '部门告警',
description:
'当部门类型的告警被触发时,你将在已订阅的方式中收到通知',
},
{
provider: 'alarm-other',
name: '其他告警',
description:
'当其他类型的告警被触发时,你将在已订阅的方式中收到通知',
},
],
},
{
id: 'b',
provider: 'system-monitor',
name: '系统监控',
children: [
{
id: 'cache',
name: '缓冲区数据丢弃',
active: false,
children: [
{
id: 'message111',
name: '站内信',
type: 'sms',
},
],
},
{
id: 'mqtt',
name: 'MQTT并发限制',
active: false,
children: [
{
id: 'message22',
name: '站内信',
type: 'sms',
},
],
provider: 'system-event',
name: '系统运行异常',
},
],
},
{
id: 'c',
provider: 'system-business',
name: '业务监控',
children: [
{
id: 'error',
provider: 'device-transparent-codec',
name: '透传消息解析异常',
active: false,
children: [
{
id: 'message333',
name: '站内信',
type: 'sms',
},
],
},
],
},
]);
const activeKey = ['a', 'b', 'c'];
];
const subscribe = ref<any[]>([]);
const dataSource = ref<any[]>([]);
const activeKey = ref<string[]>(['alarm', 'system-monitor', 'system-business']);
const loading = ref<boolean>(false)
const handleSearch = () => {
loading.value = true
getAllNotice().then((resp: any) => {
if (resp.status === 200) {
const arr = initData
.map((item) => {
const _child = item.children.map((i) => {
const _item = (resp.result || []).find(
(t: any) => t?.provider === i?.provider,
);
return {
...i,
..._item,
};
});
return {
...item,
children: _child,
};
})
.filter((it: any) => {
return it.children.filter((lt: any) => lt?.id)?.length;
})
.map((item) => {
return {
...item,
children: item.children.filter((lt: any) => lt?.id),
};
});
dataSource.value = arr;
}
});
getNoticeList_api().then((resp: any) => {
if (resp.status === 200) {
subscribe.value = resp?.result?.data || [];
}
}).finally(() => {
loading.value = false
})
};
onMounted(() => {
handleSearch();
});
</script>
<style lang="less" scoped>
@ -242,59 +179,5 @@ const activeKey = ['a', 'b', 'c'];
.child {
background-color: white;
padding: 10px;
.child-item {
padding: 10px 20px;
margin: 5px;
background: #f7f7f7;
display: flex;
justify-content: space-between;
align-items: center;
.child-item-left {
display: flex;
align-items: center;
div {
display: flex;
margin-right: 30px;
flex-direction: column;
justify-content: center;
align-items: center;
}
.child-item-left-auth {
cursor: pointer;
}
}
.child-item-right {
display: flex;
.box-item {
margin-left: 10px;
.box-item-img {
background-color: #fff;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.box-item-checked {
position: absolute;
top: -10px;
right: -10px;
}
}
.box-item-text {
width: 100%;
text-align: center;
height: 20px;
}
}
}
}
}
</style>

View File

@ -29,7 +29,7 @@
<j-tooltip title="配置后平台可调用高德地图GIS服务">
<img
class="img-style"
:src="getImage('/init-home/mark.png')"
:src="'/images/init-home/mark.png'"
/>
</j-tooltip>
</template>
@ -54,7 +54,7 @@
</template>
<img
class="img-style"
:src="getImage('/init-home/mark.png')"
:src="'/images/init-home/mark.png'"
/>
</j-tooltip>
</template>
@ -141,7 +141,7 @@
<j-tooltip title="浏览器tab页中显示的图片元素">
<img
class="img-style"
:src="getImage('/init-home/mark.png')"
:src="'/images/init-home/mark.png'"
/>
</j-tooltip>
</template>
@ -289,9 +289,9 @@ const form = ref<formState>({
headerTheme: 'light',
apikey: '',
basePath: `${window.location.origin}/api`,
logo: getImage('/logo.png'),
ico: getImage('/favicon.ico'),
background: getImage('/login.png'),
logo: '/logo.png',
ico: '/favicon.ico',
background: '/images/login.png',
});
const rulesFrom = ref({
title: [

View File

@ -70,7 +70,6 @@ const handleOk = async () => {
...form.checkedMenu,
...form.half,
]);
console.log(items)
if (form.checkedSystem) {
if (items && items.length !== 0) {
loading.value = true;

View File

@ -12,19 +12,28 @@
<script lang="ts" setup>
import { onlyMessage } from '@/utils/comm';
import Role from '../Role/index.vue'
import { PropType } from 'vue';
import Role from '../Role/index.vue';
const emit = defineEmits(['close', 'save']);
const props = defineProps({
data: {
type: Array as PropType<string[]>,
default: () => [],
},
});
const _selectedRowKeys = ref<string[]>([]);
const checked = ref<boolean>(false);
watchEffect(() => {
_selectedRowKeys.value = props.data || [];
});
const onSave = () => {
if(_selectedRowKeys.value.length) {
emit('save', _selectedRowKeys.value);
} else {
onlyMessage('请配置角色权限', 'error')
}
// if(_selectedRowKeys.value.length) {
emit('save', _selectedRowKeys.value);
// } else {
// onlyMessage('', 'error')
// }
};
</script>

View File

@ -1,12 +1,24 @@
<template>
<j-modal :width="700" visible title="配置详情" @cancel="emit('close')">
<j-modal :width="800" visible title="配置详情" @cancel="emit('close')">
<j-descriptions bordered :column="2">
<j-descriptions-item label="通知方式">Cloud Database</j-descriptions-item>
<j-descriptions-item label="通知配置">Cloud Database</j-descriptions-item>
<j-descriptions-item label="通知模板">Cloud Database</j-descriptions-item>
<j-descriptions-item label="通知内容">Cloud Database</j-descriptions-item>
<j-descriptions-item label="模板变量">Cloud Database</j-descriptions-item>
<j-descriptions-item label="用户权限">Cloud Database</j-descriptions-item>
<j-descriptions-item label="通知方式">{{
data.name
}}</j-descriptions-item>
<j-descriptions-item label="通知配置">{{
obj.notifier
}}</j-descriptions-item>
<j-descriptions-item label="通知模板">{{
obj.template
}}</j-descriptions-item>
<j-descriptions-item label="模版内容">{{
obj.content
}}</j-descriptions-item>
<j-descriptions-item label="模板变量">
<div v-for="item in obj.variables" :key="item?.value">{{ item?.name }}: {{ item?.value }}</div>
</j-descriptions-item>
<j-descriptions-item label="用户权限">{{
obj.role
}}</j-descriptions-item>
</j-descriptions>
<template #footer>
<j-button type="primary" @click="emit('close')">确定</j-button>
@ -15,6 +27,10 @@
</template>
<script lang="ts" setup>
import ConfigApi from '@/api/notice/config';
import TemplateApi from '@/api/notice/template';
import { getRoleList_api } from '@/api/system/user';
const props = defineProps({
data: {
type: Object,
@ -22,4 +38,71 @@ const props = defineProps({
},
});
const emit = defineEmits(['close', 'save']);
const obj = reactive<{
notifier: string;
template: string;
content: string;
variables: any[],
role: string;
}>({
notifier: '',
template: '',
content: '',
variables: [],
role: '',
});
const handleSearch = async () => {
//
if (props.data?.channelConfiguration?.notifierId) {
const resp = await ConfigApi.detail(
props.data?.channelConfiguration?.notifierId,
);
if (resp.status === 200) {
obj.notifier = resp.result?.name;
}
}
//
if (props.data?.channelConfiguration?.templateId) {
const resp = await TemplateApi.detail(
props.data?.channelConfiguration?.templateId,
);
if (resp.status === 200) {
obj.template = resp.result?.name;
obj.content = resp.result?.template?.message;
if (
resp.result?.variableDefinitions?.length &&
props.data?.channelConfiguration?.variables
) {
obj.variables = (resp.result?.variableDefinitions || []).map(
(item: any) => {
return {
name: item.name,
value:
props.data?.channelConfiguration?.variables?.[
item?.id
]?.value || '',
};
},
);
}
}
}
//
if (props.data?.grant?.role?.idList?.length) {
const resp: any = await getRoleList_api();
if (resp.status === 200) {
obj.role = props.data?.grant?.role?.idList
.map((item: string) => {
return (resp?.result || []).find((i: any) => i?.id === item)?.name
})
?.join(',');
}
}
};
onMounted(() => {
handleSearch();
});
</script>

View File

@ -0,0 +1,315 @@
<template>
<div class="child-item">
<div class="child-item-left">
<div style="font-weight: 600">
{{ data?.name }}
</div>
<div>
<j-switch @change="onSwitchChange" :checked="checked" />
</div>
</div>
<div class="child-item-right" v-if="checked">
<MCarousel :data="data?.channels">
<template #card="slotProps">
<div class="box-item">
<j-dropdown>
<div class="box-item-img">
<img
style="width: 100%"
:src="
getImage(
`/notice/${noticeType.get(
slotProps?.channelProvider,
)}.png`,
)
"
/>
</div>
<template #overlay v-if="slotProps?.channelProvider !== 'inside-mail'">
<j-menu mode="">
<j-menu-item>
<PermissionButton
@click="onView(slotProps)"
type="link"
:hasPermission="true"
>
查看
</PermissionButton>
</j-menu-item>
<j-menu-item>
<PermissionButton
@click="onEdit(slotProps)"
type="link"
:hasPermission="true"
>
编辑
</PermissionButton>
</j-menu-item>
<j-menu-item>
<PermissionButton
@click="onDelete(slotProps.id)"
danger
type="link"
:hasPermission="true"
>
删除
</PermissionButton>
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
<div class="box-item-text">
{{ slotProps?.name }}
</div>
</div>
</template>
<template #add>
<div class="box-item">
<div @click="onAdd" class="box-item-img">
<AIcon
style="font-size: 20px"
type="PlusOutlined"
/>
</div>
<div class="box-item-text"></div>
</div>
</template>
</MCarousel>
<div
class="child-item-right-auth"
:class="{ active: auth.length }"
@click="onAuth"
>
<AIcon type="UserOutlined" />
<span>权限控制</span>
</div>
</div>
</div>
<Save :data="current" v-if="visible" @close="visible = false" @save="onSave" />
<Detail :data="current" v-if="detailVisible" @close="detailVisible = false" />
<Auth
v-if="authVisible"
:data="data?.grant?.role?.idList"
@close="authVisible = false"
@save="onAuthSave"
/>
</template>
<script lang="ts" setup>
import MCarousel from '@/components/MCarousel/index.vue';
import Save from '../Save/index.vue';
import Detail from '../Detail/index.vue';
import Auth from '../Auth/index.vue';
import { noticeType } from '../../data';
import { getImage, onlyMessage } from '@/utils/comm';
import {
saveChannelConfig,
actionChannelConfig,
deleteChannelConfig,
editChannelConfig,
updateChannelConfig,
} from '@/api/system/noticeRule';
import { Modal } from 'jetlinks-ui-components';
const props = defineProps({
data: {
type: Object,
default: () => {},
},
});
const emits = defineEmits(['refresh']);
const visible = ref<boolean>(false);
const detailVisible = ref<boolean>(false);
const authVisible = ref<boolean>(false);
const current = ref<any>({});
const checked = ref<boolean>(false);
const auth = ref<string[]>([]);
watchEffect(() => {
checked.value = props.data?.state?.value === 'enabled';
auth.value = props.data?.grant?.role?.idList || [];
});
const onAdd = () => {
visible.value = true;
current.value = {
providerId: props.data.id
}
};
const onView = (dt: any) => {
current.value = dt
detailVisible.value = true;
};
const onEdit = (dt: any) => {
current.value = dt
visible.value = true;
};
const onDelete = async (id: string) => {
if (id) {
const resp = await deleteChannelConfig(id);
if (resp.status === 200) {
onlyMessage('操作成功!');
emits('refresh');
}
}
};
const onAuth = () => {
authVisible.value = true;
};
const onAuthSave = (_data: string[]) => {
const obj = {
grant: {
role: {
idList: _data || [],
},
permissions: [],
},
};
editChannelConfig(props.data.id, obj).then((resp) => {
if (resp.status === 200) {
onlyMessage('操作成功!', 'success');
authVisible.value = false;
emits('refresh');
}
});
};
const onSwitchChange = (e: boolean) => {
Modal.confirm({
title: e
? '开启后默认平台所有用户都能接收到该通知'
: '关闭后平台所有用户都不能接收到该通知',
cancelText: '取消',
okText: e ? '确认开启' : '确认关闭',
onOk() {
if (e) {
// enable
if (
props.data.id &&
props.data.channels?.length &&
props.data.channels.find(
(item: any) => item.channelProvider === 'inside-mail',
)
) {
actionChannelConfig(props.data.id, 'enable').then(
(resp) => {
if (resp.status === 200) {
onlyMessage('操作成功!');
emits('refresh');
}
},
);
} else {
const obj = {
...props.data,
state: 'enabled',
channels: [
{
name: '站内信',
channelProvider: 'inside-mail',
grant: {},
},
],
};
saveChannelConfig([obj]).then((resp) => {
if (resp.status === 200) {
onlyMessage('操作成功!', 'success');
emits('refresh');
}
});
}
} else {
actionChannelConfig(props.data.id, 'disable').then((resp) => {
if (resp.status === 200) {
onlyMessage('操作成功!');
emits('refresh');
}
});
}
},
onCancel() {},
});
};
const onSave = (_data: any) => {
updateChannelConfig(props.data.id, {...props.data, ..._data}).then((resp) => {
if (resp.status === 200) {
onlyMessage('操作成功!', 'success');
visible.value = false;
emits('refresh');
}
});
};
</script>
<style lang="less" scoped>
.child-item {
padding: 10px 20px;
margin: 5px;
background: #f7f7f7;
display: flex;
justify-content: space-between;
align-items: center;
.child-item-left {
display: flex;
align-items: center;
height: 80px;
div {
display: flex;
margin-right: 30px;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
.child-item-right {
display: flex;
.box-item {
margin-left: 10px;
.box-item-img {
background-color: #fff;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
}
.box-item-text {
width: 100%;
text-align: center;
height: 20px;
}
}
.child-item-right-auth {
margin-left: 20px;
height: 78px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover {
color: @primary-color-hover;
}
&.active {
color: @primary-color;
}
}
}
}
</style>

View File

@ -74,6 +74,7 @@
<script lang="ts" setup>
import ConfigApi from '@/api/notice/config';
import { MSG_TYPE, NOTICE_METHOD } from '@/views/notice/const';
import { noticeType } from '../../../data';
const props = defineProps({
notifyType: {
type: String,
@ -135,7 +136,7 @@ const query = (e: Record<string, any>) =>
{
termType: 'eq',
column: 'type',
value: props.notifyType,
value: noticeType.get(props.notifyType),
},
],
},

View File

@ -6,7 +6,7 @@
v-for="item in options"
class="notify-type-item"
:class="{ active: notifyType === item.value }"
@click="onSelect(item.value)"
@click="onSelect(item.value, item.label)"
>
<div class="notify-type-item-image">
<img :width="106" :src="item.iconUrl" />
@ -18,29 +18,25 @@
</template>
<script lang="ts" setup>
import { getImage } from '@/utils/comm';
import notice from '@/api/notice/config';
const iconMap = new Map();
iconMap.set('dingTalk', getImage('/notice/dingtalk.png'));
iconMap.set('weixin', getImage('/notice/wechat.png'));
iconMap.set('email', getImage('/notice/email.png'));
iconMap.set('voice', getImage('/notice/voice.png'));
iconMap.set('sms', getImage('/notice/sms.png'));
iconMap.set('webhook', getImage('/notice/webhook.png'));
import { queryChannelProviders } from '@/api/system/noticeRule';
import { iconMap } from '../../../data';
const props = defineProps({
value: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['update:value', 'change']);
const emit = defineEmits(['update:value', 'change', 'update:name']);
const loading = ref<boolean>(false);
const notifyType = ref('');
@ -54,18 +50,19 @@ watch(
{ deep: true, immediate: true },
);
const onSelect = (val: string) => {
const onSelect = (val: string, name: string) => {
if (!props.disabled) {
emit('update:value', val);
emit('change', val);
emit('update:name', name);
emit('change', {label: name, value: val});
}
};
onMounted(() => {
loading.value = true;
notice.queryMessageType().then((resp) => {
queryChannelProviders().then((resp) => {
if (resp.status === 200) {
options.value = (resp.result as any[]).filter(i => i.id !== 'webhook').map((item) => {
options.value = (resp.result as any[]).filter(i => i.id !== 'inside-mail').map((item) => {
return {
label: item.name,
value: item.id,

View File

@ -34,6 +34,7 @@
<BuildIn
v-else
:item="item"
:providerId="props.notify?.providerId"
v-model:value="modelRef[item.id]"
/>
</j-form-item>
@ -71,6 +72,7 @@ const formRef = ref();
const modelRef = reactive({});
watchEffect(() => {
console.log(props.notify, '123')
Object.assign(modelRef, props?.value);
});

View File

@ -16,16 +16,8 @@
placeholder="请选择参数"
style="width: calc(100% - 120px)"
:fieldNames="{ label: 'name', value: 'id' }"
@change="(val, label, extra) => itemOnChange(undefined, val, label, extra)"
@change="(val) => itemOnChange(undefined, val)"
>
<template #title="{ fullName, description }">
<j-space>
{{ fullName }}
<span style="color: grey; margin-left: 5px">{{
description
}}</span>
</j-space>
</template>
</j-tree-select>
</template>
<template v-else>
@ -59,12 +51,7 @@
</template>
<script lang="ts" setup name='NotifyBuildIn'>
import { queryBuiltInParams } from '@/api/rule-engine/scene';
import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia';
const sceneStore = useSceneStore();
const { data } = storeToRefs(sceneStore);
import { queryConfigVariables } from '@/api/system/noticeRule';
const props = defineProps({
value: {
@ -85,9 +72,13 @@ const props = defineProps({
type: Number,
default: 0,
},
providerId: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:value', 'change']);
const emit = defineEmits(['update:value']);
const source = computed(() => {
return props.value?.source || 'fixed';
@ -104,53 +95,12 @@ const sourceChange = (val: any) => {
});
};
const itemOnChange = (val: any, _upperKey?: string, label?: any, extra?: any) => {
const item = extra?.triggerNode?.props
let othersColumns = ''
if (item && item.metadata) {
othersColumns = item.column
}
const itemOnChange = (val: any, _upperKey?: string) => {
emit('update:value', {
...props.value,
value: val,
upperKey: _upperKey,
});
emit('change', {
sendTo: label?.[0] || val,
}, othersColumns);
};
const treeDataFilter = (arr: any[], type: string) => {
if (Array.isArray(arr) && arr.length) {
const list: any[] = [];
arr.map((item: any) => {
if (item.children) {
const children = treeDataFilter(item.children, type);
if (children.length) {
list.push({
...item,
title: item.name,
value: item.id,
disabled: true,
children,
});
}
} else {
if (
item.type === type ||
(type === 'double' &&
['int', 'float', 'double', 'long'].includes(item.type))
) {
list.push(item);
}
}
});
return list;
} else {
return [];
}
};
watch(
@ -158,17 +108,16 @@ watch(
(newVal) => {
const v = newVal;
if (v === 'upper') {
const params =
props.name - 1 >= 0 ? { action: props.name - 1 } : undefined;
queryBuiltInParams(unref(data), params).then((resp) => {
queryConfigVariables(props.providerId).then(resp => {
if (resp.status === 200) {
const arr = treeDataFilter(
resp.result as any[],
props.item.expands?.businessType || props.item?.type,
);
builtInList.value = arr;
builtInList.value = (resp.result as any[]).map(item => {
return {
...item,
id: 'detail.' + item.id // 便
}
})
}
});
})
}
},
{ deep: true, immediate: true },

View File

@ -11,30 +11,33 @@
</j-steps>
<div style="margin: 20px">
<template v-if="current === 0">
<NotifyWay v-model:value="formModel.notifyType" />
<NotifyWay
v-model:value="formModel.channelProvider"
v-model:name="formModel.name"
/>
</template>
<template v-if="current === 1">
<NotifyConfig
v-model:value="formModel.notifierId"
:notifyType="formModel.notifyType"
v-model:value="formModel.channelConfiguration.notifierId"
:notifyType="formModel.channelProvider"
/>
</template>
<template v-if="current === 2">
<NotifyTemplate
v-model:value="formModel.templateId"
:notifierId="formModel.notifierId"
v-model:value="formModel.channelConfiguration.templateId"
:notifierId="formModel.channelConfiguration.notifierId"
/>
</template>
<template v-if="current === 3">
<VariableDefinitions
:variableDefinitions="_variableDefinitions"
:value="formModel.variables"
:value="formModel.channelConfiguration.variables"
:notify="formModel"
ref="variableRef"
/>
</template>
<template v-if="current === 4">
<Role v-model="formModel.role" />
<Role v-model="formModel.grant.role.idList" />
</template>
</div>
<template #footer>
@ -63,9 +66,29 @@ import VariableDefinitions from './components/VariableDefinitions.vue';
import Role from '../Role/index.vue';
import { onlyMessage } from '@/utils/comm';
import Template from '@/api/notice/template';
import { variableMap } from '../../data';
type GrantType = {
role: {
idList?: string[];
};
};
type ConfigurationType = {
notifierId: string;
templateId: string;
variables: any;
};
const emit = defineEmits(['close', 'save']);
const props = defineProps({
data: {
type: Object,
default: () => {},
},
});
const stepList = [
'选择通知方式',
'选择通知配置',
@ -75,39 +98,74 @@ const stepList = [
];
const current = ref<number>(0);
const variable = ref([]);
const formModel = reactive({
notifyType: '',
notifierId: '',
templateId: '',
variables: undefined,
role: [],
const formModel = reactive<{
id?: string;
name: string;
channelConfiguration: Partial<ConfigurationType>;
grant: GrantType;
channelProvider: string;
}>({
name: '',
channelProvider: '',
grant: {
role: {},
},
channelConfiguration: {},
});
const variableRef = ref();
const _variableDefinitions = computed(() => {
const arr = ['user', 'org']
const arr = ['user', 'org'];
return variable.value.filter((item: any) => {
const _type = item.expands?.businessType || item.type || ''
return !arr.includes(_type)
})
})
const _type = item.expands?.businessType || item.type || '';
return !arr.includes(_type);
});
});
const handleVariable = (obj: any) => {
const arr = ['user', 'org'];
const _array = variable.value.filter((item: any) => {
const _type = item.expands?.businessType || item.type || '';
return arr.includes(_type);
}).map((i: any) => i?.id);
const _variable = variableMap.get(formModel.channelProvider)
const _obj = {};
[...new Set([..._array, _variable]).values()].map((item: string) => {
_obj[item] = {
source: 'relation',
relation: {
objectType: 'user',
objectSource: {
source: 'upper',
upperKey: 'subscriber',
},
},
};
});
formModel.channelConfiguration.variables = {
..._obj,
...obj,
};
};
const jumpStep = async (val: number) => {
if (val === 1) {
if (formModel.notifyType) {
if (formModel.channelProvider) {
current.value = val;
} else {
onlyMessage('请选择通知方式', 'error');
}
} else if (val === 2) {
if (formModel.notifierId) {
if (formModel.channelConfiguration.notifierId) {
current.value = val;
} else {
onlyMessage('请选择通知配置', 'error');
}
} else if (val === 3) {
if (formModel.templateId) {
const resp = await Template.getTemplateDetail(formModel.templateId);
if (formModel.channelConfiguration.templateId) {
const resp = await Template.getTemplateDetail(
formModel.channelConfiguration.templateId,
);
if (resp.status === 200) {
variable.value = resp.result?.variableDefinitions || [];
current.value = val;
@ -116,12 +174,18 @@ const jumpStep = async (val: number) => {
onlyMessage('请选择通知模板', 'error');
}
} else if (val === 4) {
if (_variableDefinitions.value.length) {
formModel.variables = await variableRef.value.onSave();
if (formModel.variables) {
current.value = val;
if (variable.value.length) {
if (_variableDefinitions.value.length) {
const obj = await variableRef.value.onSave();
if (obj) {
handleVariable(obj);
current.value = val;
} else {
onlyMessage('请配置模版变量', 'error');
}
} else {
onlyMessage('请配置模版变量', 'error');
handleVariable({});
current.value = val;
}
} else {
current.value = val;
@ -129,6 +193,10 @@ const jumpStep = async (val: number) => {
}
};
watchEffect(() => {
Object.assign(formModel, props.data);
});
const onPrev = () => {
current.value -= 1;
};
@ -142,10 +210,6 @@ const onChange = (cur: number) => {
};
const onSave = () => {
if (formModel.role.length) {
emit('save');
} else {
onlyMessage('请配置角色权限', 'error');
}
emit('save', formModel);
};
</script>

View File

@ -0,0 +1,26 @@
import { getImage } from "@/utils/comm";
const iconMap = new Map();
iconMap.set('notifier-dingTalk', getImage('/notice/dingtalk.png'));
iconMap.set('notifier-weixin', getImage('/notice/wechat.png'));
iconMap.set('notifier-email', getImage('/notice/email.png'));
iconMap.set('notifier-voice', getImage('/notice/voice.png'));
iconMap.set('notifier-sms', getImage('/notice/sms.png'));
iconMap.set('inside-mail', getImage('/notice/sms.png'));
const noticeType = new Map();
noticeType.set('notifier-dingTalk', 'dingtalk');
noticeType.set('notifier-weixin', 'wechat');
noticeType.set('notifier-email', 'email');
noticeType.set('notifier-voice', 'voice');
noticeType.set('notifier-sms', 'sms');
noticeType.set('inside-mail', 'inside-mail');
const variableMap = new Map();
variableMap.set('notifier-dingTalk', 'userIdList');
variableMap.set('notifier-weixin', 'toUser');
variableMap.set('notifier-email', 'sendTo');
variableMap.set('notifier-voice', 'calledNumber');
variableMap.set('notifier-sms', 'phoneNumber');
export { iconMap, noticeType, variableMap }

View File

@ -1,26 +1,28 @@
<template>
<page-container>
<FullPage>
<div style="padding: 24px">
<div class="alert">
<AIcon type="InfoCircleOutlined" />
你可以为每种通知类型配置不同的通知方式与通知模版
<div class="content">
<div>
<div class="alert">
<AIcon type="InfoCircleOutlined" />
启用通知类型后你可以为每种通知类型配置不同的通知方式通知模板接收人
</div>
</div>
<div class="alert">
<AIcon type="InfoCircleOutlined" />
默认平台中所有用户都能接收到通知如需限制接收权限可以在配置通知方式时完成或在通知类型后方的权限控制处配置外层权限
</div>
<div style="margin-top: 20px">
<div class="content-collapse">
<j-collapse :bordered="false" v-model:activeKey="activeKey">
<template #expandIcon="{ isActive }">
<AIcon
type="CaretRightOutlined"
:rotate="isActive ? 90 : 0"
:style="{
transform: `rotate(${
isActive ? 90 : 0
}deg)`,
}"
/>
</template>
<j-collapse-panel
v-for="item in dataSource"
:key="item.id"
:key="item.provider"
class="custom"
>
<template #header
@ -29,130 +31,12 @@
<div class="child">
<template
v-for="child in item.children"
:key="child.id"
:key="child.provider"
>
<div class="child-item">
<div class="child-item-left">
<div style="font-weight: 600">
{{ child.name }}
</div>
<div
class="child-item-left-auth"
:class="{
active: child.active,
}"
@click="onAuth(item, child)"
>
<AIcon type="UserOutlined" />
<span>权限控制</span>
</div>
</div>
<div class="child-item-right">
<MCarousel :data="child.children">
<template #card="slotProps">
<div class="box-item">
<j-dropdown>
<div
class="box-item-img"
>
<img
style="
width: 100%;
"
:src="
getImage(
`/notice/${slotProps?.type}.png`,
)
"
/>
</div>
<template #overlay>
<j-menu mode="">
<j-menu-item>
<PermissionButton
@click="
onView(
item,
child,
)
"
type="link"
:hasPermission="
true
"
>
查看
</PermissionButton>
</j-menu-item>
<j-menu-item>
<PermissionButton
@click="
onEdit(
item,
child,
)
"
type="link"
:hasPermission="
true
"
>
编辑
</PermissionButton>
</j-menu-item>
<j-menu-item>
<PermissionButton
@click="
onDelete(
item,
child,
)
"
danger
type="link"
:hasPermission="
true
"
>
删除
</PermissionButton>
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
<div
class="box-item-text"
>
{{ item.name }}
</div>
</div>
</template>
<template #add>
<div class="box-item">
<div
@click="
onAdd(
item,
child,
)
"
class="box-item-img"
>
<AIcon
style="
font-size: 20px;
"
type="PlusOutlined"
/>
</div>
<div
class="box-item-text"
></div>
</div>
</template>
</MCarousel>
</div>
</div>
<Item
:data="data.find(i => i?.provider === child?.provider)"
@refresh="onRefresh"
/>
</template>
</div>
</j-collapse-panel>
@ -160,196 +44,109 @@
</div>
</div>
</FullPage>
<Save v-if="visible" @close="visible = false" @save="onSave" />
<Detail v-if="detailVisible" @close="detailVisible = false" />
<Auth
v-if="authVisible"
@close="authVisible = false"
@save="onAuthSave"
/>
</page-container>
</template>
<script lang="ts" setup>
import MCarousel from '@/components/MCarousel/index.vue';
import { getImage } from '@/utils/comm';
import Save from './components/Save/index.vue';
import Detail from './components/Detail/index.vue';
import Auth from './components/Auth/index.vue';
import { queryChannelConfig } from '@/api/system/noticeRule';
import Item from './components/Item/index.vue';
const dataSource = ref([
const dataSource = [
{
id: 'a',
provider: 'alarm',
name: '告警',
children: [
{
id: 'product',
provider: 'alarm-product',
name: '产品告警',
active: true,
children: [
{
id: 'sms9',
name: '站内信',
type: 'sms',
},
{
id: 'dingtalk8',
name: '钉钉',
type: 'dingtalk',
},
{
id: 'wechat7',
name: '微信',
type: 'wechat',
},
{
id: 'email6',
name: '邮箱',
type: 'email',
},
{
id: 'dingtalk5',
name: '钉钉',
type: 'dingtalk',
},
{
id: 'wechat4',
name: '微信',
type: 'wechat',
},
{
id: 'email3',
name: '邮箱',
type: 'email',
},
{
id: 'email2',
name: '邮箱',
type: 'email',
},
{
id: 'email1',
name: '邮箱',
type: 'email',
},
],
},
{
id: 'device',
provider: 'alarm-device',
name: '设备告警',
active: false,
children: [
{
id: 'sms11',
name: '站内信',
type: 'sms',
},
{
id: 'wechat11',
name: '微信',
type: 'wechat',
},
{
id: 'voice11',
name: '语音',
type: 'voice',
},
],
},
{
provider: 'alarm-org',
name: '部门告警',
},
{
provider: 'alarm-other',
name: '其他告警',
},
],
},
{
id: 'b',
provider: 'system-monitor',
name: '系统监控',
children: [
{
id: 'cache',
name: '缓冲区数据丢弃',
active: false,
children: [
{
id: 'message111',
name: '站内信',
type: 'sms',
},
],
},
{
id: 'mqtt',
name: 'MQTT并发限制',
active: false,
children: [
{
id: 'message22',
name: '站内信',
type: 'sms',
},
],
provider: 'system-event',
name: '系统运行异常',
},
],
},
{
id: 'c',
provider: 'system-business',
name: '业务监控',
children: [
{
id: 'error',
provider: 'device-transparent-codec',
name: '透传消息解析异常',
active: false,
children: [
{
id: 'message333',
name: '站内信',
type: 'sms',
},
],
},
],
},
]);
];
const activeKey = ref<string[]>(['alarm', 'system-monitor', 'system-business']);
const activeKey = ['a', 'b', 'c'];
const visible = ref<boolean>(false);
const detailVisible = ref<boolean>(false);
const authVisible = ref<boolean>(false);
const current = reactive({
item: {},
child: {},
const dataMap = new Map();
const data = ref<any[]>([]);
const handleSearch = () => {
queryChannelConfig().then((resp) => {
if (resp.status === 200) {
(resp?.result || []).map((item: any) => {
dataMap.set(item.provider, item);
});
data.value = Array.from(dataMap).map((item) => {
return item?.[1];
});
}
});
};
const onRefresh = () => {
handleSearch()
}
onMounted(() => {
dataMap.clear();
dataSource.forEach((item) => {
item.children.map((i) => {
dataMap.set(i.provider, i);
});
});
data.value = Array.from(dataMap).map((item) => {
return item?.[1];
});
handleSearch();
});
const onAdd = (_item: any, _child: any) => {
(current.child = _child), (current.item = _item);
visible.value = true;
};
const onView = (_item: any, _child: any) => {
(current.child = _child), (current.item = _item);
detailVisible.value = true;
};
const onEdit = (_item: any, _child: any) => {
(current.child = _child), (current.item = _item);
visible.value = true;
};
const onDelete = (_item: any, _child: any) => {};
const onSave = () => {
visible.value = false;
};
const onAuth = (_item: any, _child: any) => {
(current.child = _child), (current.item = _item);
authVisible.value = true;
};
const onAuthSave = (_data: string[]) => {
console.log(_data);
authVisible.value = false;
};
</script>
<style lang="less" scoped>
.content {
padding: 24px;
display: flex;
flex-direction: column;
// height: 100%;
box-sizing: border-box;
justify-content: space-between;
.btn {
padding: 24px 0;
width: 100%;
background-color: #fff;
}
}
.alert {
height: 40px;
padding-left: 10px;
@ -367,58 +164,5 @@ const onAuthSave = (_data: string[]) => {
.child {
background-color: white;
padding: 10px;
.child-item {
padding: 10px 20px;
margin: 5px;
background: #f7f7f7;
display: flex;
justify-content: space-between;
align-items: center;
.child-item-left {
display: flex;
align-items: center;
div {
display: flex;
margin-right: 30px;
flex-direction: column;
justify-content: center;
align-items: center;
}
.child-item-left-auth {
cursor: pointer;
&:hover {
color: @primary-color-hover;
}
&.active {
color: @primary-color;
}
}
}
.child-item-right {
display: flex;
.box-item {
margin-left: 10px;
.box-item-img {
background-color: #fff;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
}
.box-item-text {
width: 100%;
text-align: center;
height: 20px;
}
}
}
}
}
</style>