fix: merge

This commit is contained in:
100011797 2023-03-14 13:44:11 +08:00
commit d488752e20
21 changed files with 418 additions and 293 deletions

View File

@ -8,10 +8,13 @@
:breadcrumb="{ routes: breadcrumb }"
>
<template #breadcrumbRender="slotProps">
<a v-if="slotProps.route.index !== 0">{{
slotProps.route.breadcrumbName
}}</a>
<span v-else>{{ slotProps.route.breadcrumbName }}</span>
<a
v-if="slotProps.route.index !== 0 && !slotProps.route.isLast"
@click='() => jumpPage(slotProps.route.path)'
>
{{ slotProps.route.breadcrumbName }}
</a>
<span v-else >{{ slotProps.route.breadcrumbName }}</span>
</template>
<template #rightContentRender>
<div class="right-content">
@ -32,6 +35,7 @@ import Notice from './components/Notice.vue';
import DefaultSetting from '../../../config/config';
import { useMenuStore } from '@/store/menu';
import { clearMenuItem } from 'jetlinks-ui-components/es/ProLayout/util';
import { AccountMenu } from '@/router/menu'
type StateType = {
collapsed: boolean;
@ -50,7 +54,8 @@ const layoutConf = reactive({
siderWidth: DefaultSetting.layout.siderWidth,
logo: DefaultSetting.layout.logo,
title: DefaultSetting.layout.title,
menuData: clearMenuItem(menu.siderMenus),
menuData: [...clearMenuItem(menu.siderMenus), AccountMenu],
// menuData: menu.siderMenus,
splitMenus: true,
});
@ -61,26 +66,49 @@ const state = reactive<StateType>({
selectedKeys: [],
});
const findRouteMeta = (code: string) => {
let meta = []
let menuItem: any = menu.menus[code]
while (menuItem) {
meta.unshift(menuItem)
if (menuItem.parentName) {
menuItem = menu.menus[menuItem.parentName]
} else {
menuItem = false
}
}
return meta
}
const jumpPage = (path: string) => {
console.log(path)
router.push(path)
}
const breadcrumb = computed(() =>
router.currentRoute.value.matched.concat().map((item, index) => {
return {
index,
path: item.path,
breadcrumbName: item.meta.title || '',
};
}),
{
const paths = router.currentRoute.value.name as string
const metas = findRouteMeta(paths)
return metas.map((item, index) => {
return {
index,
isLast: index === (metas.length - 1),
path: item.path,
breadcrumbName: item.title || '',
};
})
}
);
watchEffect(() => {
if (router.currentRoute) {
const matched = router.currentRoute.value.matched.concat();
state.selectedKeys = matched.map((r) => r.path);
state.openKeys = matched
.filter((r) => r.path !== router.currentRoute.value.path)
.map((r) => r.path);
console.log(state.selectedKeys);
const paths = router.currentRoute.value.name as string
if (paths) {
const _metas = findRouteMeta(paths)
state.selectedKeys = _metas.map(item => item.path)
state.openKeys = _metas.filter((r) => r !== router.currentRoute.value.path).map(item => item.path)
}
}
// TODO pure
});
watchEffect(() => {

View File

@ -5,6 +5,7 @@ export const AccountMenu = {
component: () => import('@/components/Layout/BasicLayoutPage.vue'),
redirect: '/account/center',
name: 'account',
code: 'account',
meta: {
title: '个人中心',
icon: '',
@ -14,6 +15,7 @@ export const AccountMenu = {
{
path: '/account/center',
name: 'account/center',
code: 'account/center',
meta: {
title: '基本设置',
icon: '',
@ -24,6 +26,7 @@ export const AccountMenu = {
{
path: '/account/NotificationSubscription',
name: 'account/NotificationSubscription',
code: 'account/NotificationSubscription',
meta: {
title: '通知订阅',
icon: '',
@ -34,6 +37,7 @@ export const AccountMenu = {
{
path: '/account/NotificationRecord',
name: 'account/NotificationRecord',
code: 'account/NotificationRecord',
meta: {
title: '通知记录',
icon: '',

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia'
import { queryOwnThree } from '@/api/system/menu'
import { filterAsnycRouter, MenuItem } from '@/utils/menu'
import { filterAsyncRouter, findCodeRoute, MenuItem } from '@/utils/menu'
import { isArray } from 'lodash-es'
import { usePermissionStore } from './permission'
import router from '@/router'
@ -33,6 +33,8 @@ type MenuStateType = {
menus: {
[key: string]: {
buttons?: string[]
title: string
parentName: string
path: string
}
}
@ -98,24 +100,15 @@ export const useMenuStore = defineStore({
if (resp.success) {
const permission = usePermissionStore()
permission.permissions = {}
const { menusData, silderMenus } = filterAsnycRouter(resp.result)
this.menus = {}
const handleMenuItem = (menu: any) => {
if (isArray(menu)) {
menu.forEach(menuItem => {
this.menus[menuItem.name] = {
path: menuItem.path,
buttons: menuItem.meta.buttons
}
permission.permissions[menuItem.name] = menuItem.meta.buttons
if (menuItem.children && menuItem.children.length) {
handleMenuItem(menuItem.children)
}
})
const { menusData, silderMenus } = filterAsyncRouter(resp.result)
this.menus = findCodeRoute([...resp.result, AccountMenu])
Object.keys(this.menus).forEach((item) => {
const _item = this.menus[item]
if (_item.buttons?.length) {
permission.permissions[item] = _item.buttons
}
}
})
handleMenuItem(menusData)
menusData.push({
path: '/',
redirect: menusData[0]?.path,
@ -124,10 +117,7 @@ export const useMenuStore = defineStore({
}
})
menusData.push(AccountMenu)
silderMenus.push(AccountMenu)
this.siderMenus = silderMenus
console.log('menusData', menusData)
console.log('silderMenus', silderMenus)
res(menusData)
}
})

View File

@ -3,11 +3,12 @@ import { defineStore } from "pinia";
export const usePermissionStore = defineStore({
id: 'permission',
state: () => ({
permissions: {} as {[key: string]: string},
permissions: {} as {[key: string]: string[]},
}),
getters: {
check(state) {
return (permissionCode: string) => {
if (!permissionCode) {
return true
}
@ -16,6 +17,7 @@ export const usePermissionStore = defineStore({
}
const code = permissionCode.split(":")[0]
const value = permissionCode.split(":")[1]
const _buttonArray = state.permissions[code]
if (!_buttonArray) {
return false

View File

@ -151,7 +151,6 @@ const extraRouteObj = {
const resolveComponent = (name: any) => {
const importPage = pagesComponent[`../views/${name}/index.vue`];
if (!importPage) {
console.warn(`Unknown page ${name}. Is it located under Pages with a .vue extension?`)
return undefined
} else {
const res = () => importPage()
@ -201,7 +200,52 @@ const findDetailRoutes = (routes: any[]): any[] => {
return newRoutes
}
export function filterAsnycRouter(asyncRouterMap: any, parentCode = '', level = 1): { menusData: any, silderMenus: any } {
export const findCodeRoute = (asyncRouterMap: any[]) => {
const routeMeta = {}
function findChildren (data: any[], code: string = '') {
data.forEach(route => {
routeMeta[route.code] = {
path: route.url || route.path,
title: route.meta?.title || route.name,
parentName: code,
buttons: route.buttons?.map((b: any) => b.id) || []
}
const detail = findDetailRouteItem(route.code, route.url)
if (detail) {
routeMeta[(detail as MenuItem).code] = {
path: detail.url,
title: detail.name,
parentName: route.code,
buttons: detail.buttons?.map((b: any) => b.id) || []
}
}
const otherRoutes = extraRouteObj[route.code]
if (otherRoutes) {
otherRoutes.children.map((item: any) => {
const _code = `${route.code}/${item.code}`
routeMeta[_code] = {
path: `${route.url}/${item.code}`,
title: item.name,
parentName: route.code,
buttons: item.buttons?.map((b: any) => b.id) || []
}
})
}
if (route.children) {
findChildren(route.children, route.code)
}
})
}
findChildren(asyncRouterMap)
return routeMeta
}
export function filterAsyncRouter(asyncRouterMap: any, parentCode = '', level = 1): { menusData: any, silderMenus: any} {
const _asyncRouterMap = cloneDeep(asyncRouterMap)
const menusData: any[] = []
const silderMenus: any[] = []
@ -224,7 +268,7 @@ export function filterAsnycRouter(asyncRouterMap: any, parentCode = '', level =
route.children = findDetailRoutes(route.children)
if (route.children && route.children.length) {
// TODO 查看是否具有详情页
const { menusData: _menusData, silderMenus: _silderMenus } = filterAsnycRouter(route.children, `${parentCode}/${route.code}`, level + 1)
const { menusData: _menusData, silderMenus: _silderMenus } = filterAsyncRouter(route.children, `${parentCode}/${route.code}`, level + 1)
_route.children = _menusData
silder.children = _silderMenus
const showChildren = _route.children.some((r: any) => !r.meta.hideInMenu)
@ -251,6 +295,6 @@ export function filterAsnycRouter(asyncRouterMap: any, parentCode = '', level =
})
return {
menusData,
silderMenus
silderMenus,
}
}

View File

@ -95,6 +95,7 @@ import { queryFlow, list } from '@/api/iot-card/home';
import * as echarts from 'echarts';
import { useMenuStore } from '@/store/menu';
import { usePermissionStore } from '@/store/permission';
import { message } from 'jetlinks-ui-components'
const { proxy } = <any>getCurrentInstance();
@ -178,11 +179,10 @@ const pieChartData = ref<any[]>([
]);
const jumpPage = (data: GuideItemProps) => {
// if (data.url && data.auth) {
// router.push({ path: `${data.url}`, ...data.param });
// } else {
// message.warning('');
// }
if (!data.auth){
message.warning('暂无权限,请联系管理员');
return
}
if (data.key === 'EQUIPMENT') {
menuStory.jumpPage(data.url, { id: 'add' });
} else {

View File

@ -9,32 +9,50 @@
:confirmLoading="btnLoading"
width="660px"
>
<j-form layout="vertical">
<j-form-item label="产品名称" v-bind="validateInfos.name">
<j-form ref="formRef" :model="formData" layout="vertical">
<j-form-item
label="产品名称"
name="name"
:rules="{ required: true, message: '请输入产品名称' }"
>
<j-input
v-model:value="formData.name"
placeholder="请输入名称"
/>
</j-form-item>
<template v-if="channel === 'gb28181-2016' && formData.accessId">
<template v-for="(item, index) in extendFormItem" :key="index">
<j-form-item
label="接入密码"
v-bind="validateInfos['configuration.access_pwd']"
:name="item.name"
:label="item.label"
:rules="{
required: item.required,
message: item.message,
trigger: 'change',
}"
>
<j-input-password
v-model:value="formData.configuration.access_pwd"
placeholder="请输入接入密码"
/>
</j-form-item>
<j-form-item label="流传输模式">
<j-select
v-model:value="formData.configuration.stream_mode"
placeholder="请选择流传输模式"
:options="streamMode"
v-if="item.type === 'enum'"
v-model:value="formData[item.name[0]][item.name[1]]"
:options="item.options"
:placeholder="item.message"
/>
<j-input-password
v-else-if="item.type === 'password'"
v-model:value="formData[item.name[0]][item.name[1]]"
:placeholder="item.message"
/>
<j-input
v-else
v-model:value="formData[item.name[0]][item.name[1]]"
:placeholder="item.message"
/>
</j-form-item>
</template>
<j-form-item label="接入网关" v-bind="validateInfos.accessId">
<j-form-item
label="接入网关"
name="accessId"
:rules="{ required: true, message: '请选择接入网关' }"
>
<div class="gateway-box">
<div v-if="!gatewayList.length">
暂无数据请先
@ -119,16 +137,12 @@
</template>
<script setup lang="ts">
import { Form, message } from 'ant-design-vue';
import { PropType } from 'vue';
import { streamMode } from '@/views/media/Device/const';
import { message } from 'ant-design-vue';
import DeviceApi from '@/api/media/device';
import { getImage } from '@/utils/comm';
import { gatewayType } from '@/views/media/Device/typings';
import { providerType } from '../const';
const useForm = Form.useForm;
type Emits = {
(e: 'update:visible', data: boolean): void;
(e: 'update:productId', data: string): void;
@ -168,6 +182,7 @@ const getGatewayList = async () => {
* @param e
*/
const _selectedRowKeys = ref<string[]>([]);
const extendFormItem = ref<any[]>();
const handleClick = async (e: any) => {
_selectedRowKeys.value = [e.id];
formData.value.accessId = e.id;
@ -181,7 +196,22 @@ const handleClick = async (e: any) => {
e.protocol,
e.transport,
);
console.log('result: ', result);
extendFormItem.value = result.properties.map((item: any) => ({
name: ['configuration', item.property],
label: item.name,
type: item.type?.type,
value: item.type.expands?.defaultValue,
options: item.type.elements?.map((e: any) => ({
label: e.text,
value: e.value,
})),
required: !!item.type.expands?.required,
message:
item.type?.type === 'enum'
? `请选择${item.name}`
: `请输入${item.name}`,
}));
};
watch(
@ -189,10 +219,6 @@ watch(
(val) => {
if (val) {
getGatewayList();
formRules.value['configuration.access_pwd'][0].required =
props.channel === 'gb28181-2016';
validate();
} else {
emit('close');
}
@ -200,6 +226,7 @@ watch(
);
//
const formRef = ref();
const formData = ref({
accessId: '',
accessName: '',
@ -215,25 +242,13 @@ const formData = ref({
transportProtocol: '',
});
//
const formRules = ref({
name: [{ required: true, message: '请输入产品名称' }],
'configuration.access_pwd': [{ required: true, message: '请输入接入密码' }],
accessId: [{ required: true, message: '请选择接入网关' }],
});
const { resetFields, validate, validateInfos, clearValidate } = useForm(
formData.value,
formRules.value,
);
/**
* 提交
*/
const btnLoading = ref(false);
const handleOk = () => {
// console.log('formData.value: ', formData.value);
validate()
formRef.value
?.validate()
.then(async () => {
btnLoading.value = true;
const res = await DeviceApi.saveProduct(formData.value);
@ -249,14 +264,14 @@ const handleOk = () => {
}
btnLoading.value = false;
})
.catch((err) => {
.catch((err: any) => {
console.log('err: ', err);
});
};
const handleCancel = () => {
_vis.value = false;
resetFields();
formRef.value.resetFields();
};
</script>

View File

@ -50,34 +50,50 @@
<template v-else>
<j-form-item
:name="['templateDetailTable', index, 'value']"
:rules="{
required: record.required,
message: '该字段为必填字段',
}"
:rules="[
{
required: record.required,
message: '该字段为必填字段',
},
...record.otherRules,
]"
>
<ToUser
v-if="record.type === 'user'"
v-model:toUser="record.value"
:type="data.type"
:config-id="formData.configId"
/>
<ToOrg
v-else-if="record.type === 'org'"
:type="data.type"
:config-id="formData.configId"
v-model:toParty="record.value"
/>
<ToTag
v-else-if="record.type === 'tag'"
:type="data.type"
:config-id="formData.configId"
v-model:toTag="record.value"
/>
<ValueItem
v-else
v-model:modelValue="record.value"
:itemType="record.type"
/>
<template
v-if="
data.type === 'dingTalk' ||
data.type === 'weixin'
"
>
<ToUser
v-if="record.type === 'user'"
v-model:toUser="record.value"
:type="data.type"
:config-id="formData.configId"
/>
<ToOrg
v-else-if="record.type === 'org'"
:type="data.type"
:config-id="formData.configId"
v-model:toParty="record.value"
/>
<ToTag
v-else-if="record.type === 'tag'"
:type="data.type"
:config-id="formData.configId"
v-model:toTag="record.value"
/>
<ValueItem
v-else
v-model:modelValue="record.value"
:itemType="record.type"
/>
</template>
<template v-else>
<ValueItem
v-model:modelValue="record.value"
:itemType="record.type"
/>
</template>
</j-form-item>
</template>
</template>
@ -100,6 +116,8 @@ import { message } from 'ant-design-vue';
import ToUser from '../Detail/components/ToUser.vue';
import ToOrg from '../Detail/components/ToOrg.vue';
import ToTag from '../Detail/components/ToTag.vue';
import type { Rule } from 'ant-design-vue/es/form';
import { phoneRegEx } from '@/utils/validate';
type Emits = {
(e: 'update:visible', data: boolean): void;
@ -156,6 +174,26 @@ const getTemplateDetail = async () => {
...m,
type: m.expands ? m.expands.businessType : m.type,
value: undefined,
//
otherRules:
m.id === 'calledNumber'
? [
{
max: 64,
message: '最多可输入64个字符',
trigger: 'change',
},
{
trigger: 'change',
validator(_rule: Rule, value: string) {
if (!value) return Promise.resolve();
if (!phoneRegEx(value))
return Promise.reject('请输入有效号码');
return Promise.resolve();
},
},
]
: '',
}),
);
};

View File

@ -85,7 +85,7 @@
>
<template #label>
<span>
AgentID
AgentId
<j-tooltip title="应用唯一标识">
<AIcon
type="QuestionCircleOutlined"
@ -98,7 +98,7 @@
v-model:value="
formData.template.agentId
"
placeholder="请输入AppSecret"
placeholder="请输入AgentId"
/>
</j-form-item>
<j-row :gutter="10">
@ -271,7 +271,7 @@
</template>
<j-input
v-model:value="formData.template.agentId"
placeholder="请输入agentId"
placeholder="请输入AgentId"
/>
</j-form-item>
<j-row :gutter="10">
@ -664,7 +664,6 @@
<j-radio :value="false">自定义</j-radio>
</j-radio-group>
<j-textarea
v-model:value="formData.template.body"
placeholder="请求体中的数据来自于发送通知时指定的所有变量"
v-if="formData.template.contextAsBody"
disabled
@ -902,7 +901,10 @@ const formRules = ref({
provider: [{ required: true, message: '请选择类型' }],
configId: [{ required: true, message: '请选择绑定配置' }],
//
'template.agentId': [{ required: true, message: '请输入agentId' }],
'template.agentId': [
{ required: true, message: '请输入AgentId' },
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
],
'template.messageType': [{ required: true, message: '请选择消息类型' }],
'template.markdown.title': [
{ required: true, message: '请输入标题', trigger: 'change' },
@ -914,7 +916,7 @@ const formRules = ref({
],
// 'template.url': [{ required: true, message: 'WebHook' }],
//
// 'template.agentId': [{ required: true, message: 'agentId' }],
// 'template.agentId': [{ required: true, message: 'AgentId' }],
//
'template.subject': [
{ required: true, message: '请输入标题' },

View File

@ -1,27 +1,27 @@
<template>
<div>
<a-form layout="vertical" :rules="rule" :model="form" ref="formRef">
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="名称" name="name">
<a-input
<j-form layout="vertical" :rules="rule" :model="form" ref="formRef">
<j-row :gutter="24">
<j-col :span="12">
<j-form-item label="名称" name="name">
<j-input
placeholder="请输入名称"
v-model:value="form.name"
></a-input> </a-form-item
></a-col>
<a-col :span="12">
<a-form-item label="类型" name="targetType">
<a-select
></j-input> </j-form-item
></j-col>
<j-col :span="12">
<j-form-item label="类型" name="targetType">
<j-select
:options="options"
v-model:value="form.targetType"
:disabled="selectDisable"
></a-select>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="级别" name="level">
<a-radio-group v-model:value="form.level">
<a-radio-button
></j-select>
</j-form-item>
</j-col>
</j-row>
<j-form-item label="级别" name="level">
<j-radio-group v-model:value="form.level">
<j-radio-button
v-for="(item, index) in levelOption"
:key="index"
:value="item.value"
@ -40,14 +40,14 @@
alt=""
/>{{ item.label }}
</div>
</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="说明" name="description">
<a-textarea v-model:value="form.description"></a-textarea>
</a-form-item>
</j-radio-button>
</j-radio-group>
</j-form-item>
<j-form-item label="说明" name="description">
<j-textarea v-model:value="form.description"></j-textarea>
</j-form-item>
<PermissionButton type="primary" @click="handleSave" :hasPermission="['rule-engine/Alarm/Configuration:add','rule-engine/Alarm/Configuration:update']">保存</PermissionButton>
</a-form>
</j-form>
</div>
</template>
@ -178,7 +178,7 @@ const handleSave = async () => {
const res = await save(form);
loading.value = false;
if (res.status === 200) {
message.success('操作成功');
message.success('操作成功,请配置关联的场景联动');
menuStory.jumpPage(
'rule-engine/Alarm/Configuration/Save',
{},

View File

@ -1,5 +1,5 @@
<template>
<a-modal
<j-modal
visible
title="新增"
okText="确定"
@ -8,7 +8,7 @@
@cancel="closeModal"
@ok="saveCorrelation"
>
<Search :columns="columns" @search="handleSearch"></Search>
<pro-search :columns="columns" @search="handleSearch"/>
<div style="height: 500px; overflow-y: auto">
<JProTable
model="CARD"
@ -78,7 +78,7 @@
</template>
</JProTable>
</div>
</a-modal>
</j-modal>
</template>
<script lang="ts" setup>
@ -213,7 +213,7 @@ const saveCorrelation = async () => {
const list = _selectedRowKeys.value.map((item: any) => {
return {
alarmId: props.id,
releId: item,
ruleId: item,
};
});
const res = await bindScene([...list]);

View File

@ -10,12 +10,12 @@
ref="actionRef"
>
<template #headerTitle>
<a-space>
<j-space>
<PermissionButton type="primary" @click="showModal" hasPermission="rule-engine/Alarm/Configuration:add">
<template #icon><AIcon type="PlusOutlined" /></template>
新增
</PermissionButton>
</a-space>
</j-space>
</template>
<template #card="slotProps">
<SceneCard

View File

@ -1,19 +1,19 @@
<template>
<page-container>
<j-card>
<j-tabs :activeKey="activeKey" @change="changeTabs">
<j-tab-pane key="1" tab="基础配置">
<Base/>
</j-tab-pane>
<j-tab-pane key="2" tab="关联场景联动">
<Scene></Scene>
</j-tab-pane>
<j-tab-pane key="3" tab="告警记录">
<Log/>
</j-tab-pane>
</j-tabs>
</j-card>
</page-container>
<page-container>
<j-card>
<j-tabs :activeKey="activeKey" @change="changeTabs">
<j-tab-pane key="1" tab="基础配置">
<Base />
</j-tab-pane>
<j-tab-pane key="2" tab="关联场景联动">
<Scene></Scene>
</j-tab-pane>
<j-tab-pane key="3" tab="告警记录">
<Log v-if="activeKey === '3'" />
</j-tab-pane>
</j-tabs>
</j-card>
</page-container>
</template>
<script lang="ts" setup>
@ -23,13 +23,13 @@ import Log from './Log/indev.vue';
import { useRoute } from 'vue-router';
import { message } from 'ant-design-vue';
const route = useRoute();
const changeTabs = (e:any) =>{
if(route.query?.id){
const changeTabs = (e: any) => {
if (route.query?.id) {
activeKey.value = e;
}else{
} else {
message.error('请先保存基础配置');
}
}
};
const activeKey = ref('1');
</script>
<style lang="less" scoped>

View File

@ -56,8 +56,8 @@
{{ slotProps.name }}
</span>
</Ellipsis>
<a-row>
<a-col :span="12">
<j-row>
<j-col :span="12">
<div class="content-des-title">
关联场景联动
</div>
@ -66,8 +66,8 @@
{{ (slotProps?.scene || []).map((item: any) => item?.name).join(',') || '' }}
</div></Ellipsis
>
</a-col>
<a-col :span="12">
</j-col>
<j-col :span="12">
<div class="content-des-title">
告警级别
</div>
@ -75,8 +75,8 @@
{{ (Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
slotProps.level }}
</div>
</a-col>
</a-row>
</j-col>
</j-row>
</template>
<template #actions="item">
<PermissionButton
@ -109,7 +109,7 @@
<span>{{ map[slotProps.targetType] }}</span>
</template>
<template #level="slotProps">
<a-tooltip
<j-tooltip
placement="topLeft"
:title="(Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
slotProps.level"
@ -118,7 +118,7 @@
{{ (Store.get('default-level') || []).find((item: any) => item?.level === slotProps.level)?.title ||
slotProps.level }}
</div>
</a-tooltip>
</j-tooltip>
</template>
<template #sceneId="slotProps">
<span
@ -126,7 +126,7 @@
>
</template>
<template #state="slotProps">
<a-badge
<j-badge
:text="
slotProps.state?.value === 'enabled'
? '正常'
@ -140,7 +140,7 @@
/>
</template>
<template #action="slotProps">
<a-space :size="16">
<j-space :size="16">
<template
v-for="i in getActions(slotProps, 'table')"
:key="i.key"
@ -168,7 +168,7 @@
/></template>
</PermissionButton>
</template>
</a-space>
</j-space>
</template>
</JProTable>
</div>

View File

@ -1,10 +1,10 @@
<template>
<page-container>
<Search
<pro-search
:columns="columns"
target="alarm-log-detail"
@search="handleSearch"
></Search>
/>
<JProTable
:columns="columns"
model="TABLE"
@ -19,7 +19,7 @@
moment(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')
}}</template>
<template #action="slotProps">
<a-space
<j-space
><template
v-for="i in getActions(slotProps, 'table')"
:key="i.key"
@ -37,7 +37,7 @@
<template #icon><AIcon :type="i.icon"/></template>
</PermissionButton>
</template>
</a-space>
</j-space>
</template>
</JProTable>
<Info v-if="visiable" :data="current" @close="close"/>

View File

@ -1,16 +1,16 @@
<template>
<a-modal visible title="详情" okText="确定" cancelText="取消" :width="1000" @ok="closeModal" @cancel="closeModal">
<a-descriptions bordered :column="2">
<a-descriptions-item v-if="props.data.targetType==='device'" label="告警设备" :span="1">{{props.data?.targetName || ''}}</a-descriptions-item>
<a-descriptions-item v-if="props.data.targetType==='device'" label="设备ID" :span="1">{{props.data?.targetId || ''}}</a-descriptions-item>
<a-descriptions-item label="告警名称" :span="1">{{
<j-modal visible title="详情" okText="确定" cancelText="取消" :width="1000" @ok="closeModal" @cancel="closeModal">
<j-descriptions bordered :column="2">
<j-descriptions-item v-if="props.data.targetType==='device'" label="告警设备" :span="1">{{props.data?.targetName || ''}}</j-descriptions-item>
<j-descriptions-item v-if="props.data.targetType==='device'" label="设备ID" :span="1">{{props.data?.targetId || ''}}</j-descriptions-item>
<j-descriptions-item label="告警名称" :span="1">{{
props.data?.alarmConfigName
}}</a-descriptions-item>
<a-descriptions-item label="告警时间" :span="1">{{
}}</j-descriptions-item>
<j-descriptions-item label="告警时间" :span="1">{{
moment(data?.alarmTime).format('YYYY-MM-DD HH:mm:ss')
}}</a-descriptions-item>
<a-descriptions-item label="告警级别" :span="1">
<a-tooltip
}}</j-descriptions-item>
<j-descriptions-item label="告警级别" :span="1">
<j-tooltip
placement="topLeft"
:title="(Store.get('default-level') || []).find((item: any) => item?.level === data?.level)
?.title || props.data?.level"
@ -21,10 +21,10 @@
?.title || props.data?.level}}
</span>
</Ellipsis>
</a-tooltip>
</a-descriptions-item>
<a-descriptions-item label="告警说明" :span="1"
><a-tooltip
</j-tooltip>
</j-descriptions-item>
<j-descriptions-item label="告警说明" :span="1"
><j-tooltip
placement="topLeft"
:title="data?.description || ''"
>
@ -33,14 +33,14 @@
{{ data?.description || '' }}
</span> </Ellipsis
>
</a-tooltip></a-descriptions-item
</j-tooltip></j-descriptions-item
>
<a-descriptions-item
<j-descriptions-item
label="告警流水"
:span="2"
><div style="max-height: 500px; overflow-y: auto;"><JsonViewer :value="JSON.parse(data?.alarmInfo || '{}')" :expand-depth="5"></JsonViewer></div></a-descriptions-item>
</a-descriptions>
</a-modal>
><div style="max-height: 500px; overflow-y: auto;"><JsonViewer :value="JSON.parse(data?.alarmInfo || '{}')" :expand-depth="5"></JsonViewer></div></j-descriptions-item>
</j-descriptions>
</j-modal>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<a-modal
<j-modal
title="告警处理"
okText="确定"
cancelText="取消"
@ -9,18 +9,18 @@
destroyOnClose
:confirmLoading="loading"
>
<a-form :rules="rules" layout="vertical" ref="formRef" :model="form">
<a-form-item label="处理结果" name="describe">
<a-textarea
<j-form :rules="rules" layout="vertical" ref="formRef" :model="form">
<j-form-item label="处理结果" name="describe">
<j-textarea
:rows="8"
:maxlength="200"
showCount
placeholder="请输入处理结果"
v-model:value="form.describe"
></a-textarea>
</a-form-item>
</a-form>
</a-modal>
></j-textarea>
</j-form-item>
</j-form>
</j-modal>
</template>
<script lang="ts" setup>
@ -64,6 +64,7 @@ const handleSave = () => {
});
if (res.status === 200) {
onlyMessage('操作成功!');
emit('closeSolve');
} else {
onlyMessage('操作失败!', 'error');
}

View File

@ -1,5 +1,5 @@
<template>
<a-modal
<j-modal
visible
title="处理记录"
:width="1200"
@ -8,11 +8,11 @@
@ok="clsoeModal"
@cancel="clsoeModal"
>
<Search
<pro-search
:columns="columns"
target="bind-channel"
@search="handleSearch"
></Search>
/>
<JProTable
model="TABLE"
:columns="columns"
@ -48,7 +48,7 @@
</span>
</template>
</JProTable>
</a-modal>
</j-modal>
</template>
<script lang="ts" setup>

View File

@ -1,29 +1,29 @@
<template>
<div class="alarm-log-card">
<Search
<pro-search
:columns="columns"
target="alarm-log"
v-if="['all', 'detail'].includes(props.type)"
@search="search"
></Search>
<Search
/>
<pro-search
:columns="produtCol"
target="alarm-log"
v-if="['product', 'other'].includes(props.type)"
@search="search"
></Search>
<Search
/>
<pro-search
:columns="deviceCol"
target="alarm-log"
v-if="props.type === 'device'"
@search="search"
></Search>
<Search
/>
<pro-search
:columns="orgCol"
target="alarm-log"
v-if="props.type === 'org'"
@search="search"
></Search>
/>
<JProTable
:columns="columns"
:request="handleSearch"
@ -31,6 +31,7 @@
:gridColumns="[1, 1, 2]"
:gridColumn="2"
model="CARD"
ref="tableRef"
>
<template #card="slotProps">
<CardBox
@ -52,8 +53,8 @@
{{ slotProps.alarmName }}
</span>
</Ellipsis>
<a-row :gutter="24">
<a-col :span="8">
<j-row :gutter="24">
<j-col :span="8">
<div class="content-des-title">
{{ titleMap.get(slotProps.targetType) }}
</div>
@ -62,8 +63,8 @@
{{ slotProps?.targetName }}
</div></Ellipsis
>
</a-col>
<a-col :span="8">
</j-col>
<j-col :span="8">
<div class="content-des-title">
最近告警时间
</div>
@ -76,17 +77,17 @@
}}
</div></Ellipsis
>
</a-col>
<a-col :span="8">
</j-col>
<j-col :span="8">
<div class="content-des-title">状态</div>
<a-badge
<j-badge
:status="
slotProps.state.value === 'warning'
? 'error'
: 'default'
"
>
</a-badge
</j-badge
><span
:style="
slotProps.state.value === 'warning'
@ -96,8 +97,8 @@
>
{{ slotProps.state.text }}
</span>
</a-col>
</a-row>
</j-col>
</j-row>
</template>
<template #actions="item">
<PermissionButton
@ -105,7 +106,6 @@
item.key === 'solve' &&
slotProps.state.value === 'normal'
"
:popConfirm="item.popConfirm"
:tooltip="{
...item.tooltip,
}"
@ -152,11 +152,11 @@ import { Store } from 'jetlinks-store';
import moment from 'moment';
import type { ActionsType } from '@/components/Table';
import SolveComponent from '../SolveComponent/index.vue';
import SolveLog from '../SolveLog/index.vue'
import SolveLog from '../SolveLog/index.vue';
import { useMenuStore } from '@/store/menu';
import { usePermissionStore } from '@/store/permission';
const menuStory = useMenuStore();
const tableRef = ref();
const alarmStore = useAlarmStore();
const { data } = storeToRefs(alarmStore);
const getDefaulitLevel = () => {
@ -339,7 +339,7 @@ watchEffect(() => {
},
];
}
if(props.type === 'all'){
if (props.type === 'all') {
params.value.terms = [];
}
});
@ -347,24 +347,20 @@ watchEffect(() => {
const search = (data: any) => {
params.value.terms = [...data?.terms];
if (props.type !== 'all' && !props.id) {
params.value.terms.push(
{
termType: 'eq',
column: 'targetType',
value: props.type,
type: 'and',
},
);
params.value.terms.push({
termType: 'eq',
column: 'targetType',
value: props.type,
type: 'and',
});
}
if (props.id) {
params.value.terms.push (
{
termType: 'eq',
column: 'alarmConfigId',
value: props.id,
type: 'and',
},
);
params.value.terms.push({
termType: 'eq',
column: 'alarmConfigId',
value: props.id,
type: 'and',
});
}
};
@ -381,17 +377,19 @@ const getActions = (
title: '告警处理',
},
icon: 'ToolOutlined',
onClick: () =>{
onClick: () => {
data.value.current = currentData;
data.value.solveVisible = true;
},
popConfirm:{
title: !usePermissionStore().hasPermission('rule-engine/Alarm/Log:action')
? '暂无权限,请联系管理员'
: data.state?.value === 'normal'
? '无告警'
: ''
}
// popConfirm: {
// title: !usePermissionStore().hasPermission(
// 'rule-engine/Alarm/Log:action',
// )
// ? ''
// : data.state?.value === 'normal'
// ? ''
// : '',
// },
},
{
key: 'log',
@ -400,9 +398,11 @@ const getActions = (
title: '告警日志',
},
icon: 'FileOutlined',
onClick: () =>{
menuStory.jumpPage(`rule-engine/Alarm/Log/Detail`,{id:currentData.id});
}
onClick: () => {
menuStory.jumpPage(`rule-engine/Alarm/Log/Detail`, {
id: currentData.id,
});
},
},
{
key: 'detail',
@ -411,10 +411,10 @@ const getActions = (
title: '处理记录',
},
icon: 'FileTextOutlined',
onClick:() =>{
onClick: () => {
data.value.current = currentData;
data.value.logVisible = true;
}
},
},
];
return actions;
@ -422,15 +422,16 @@ const getActions = (
/**
* 关闭告警日志
*/
const closeSolve = () =>{
const closeSolve = () => {
data.value.solveVisible = false;
}
tableRef.value.reload(params.value);
};
/**
* 关闭处理记录
*/
const closeLog = () =>{
const closeLog = () => {
data.value.logVisible = false;
}
};
</script>
<style lang="less" scoped>
</style>

View File

@ -1,5 +1,5 @@
<template>
<a-modal
<j-modal
:maskClosable="false"
width="650px"
destroyOnClose
@ -12,30 +12,30 @@
:confirmLoading="loading"
>
<div style="margin-top: 10px">
<a-form
<j-form
:layout="'vertical'"
ref="formRef"
:rules="rules"
:model="modelRef"
>
<a-form-item label="名称" name="name">
<a-input
<j-form-item label="名称" name="name">
<j-input
v-model:value="modelRef.name"
placeholder="请输入名称"
/>
</a-form-item>
<a-form-item label="说明" name="describe">
<a-textarea
</j-form-item>
<j-form-item label="说明" name="describe">
<j-textarea
v-model:value="modelRef.description"
placeholder="请输入说明"
showCount
:maxlength="200"
:rows="4"
/>
</a-form-item>
</a-form>
</j-form-item>
</j-form>
</div>
</a-modal>
</j-modal>
</template>
<script lang="ts" setup>

View File

@ -1,11 +1,11 @@
<template>
<page-container>
<div>
<Search
<pro-search
:columns="query.columns"
target="device-instance"
@search="handleSearch"
></Search>
/>
<JProTable
:columns="columns"
:request="queryList"
@ -53,15 +53,15 @@
{{ slotProps.name }}
</span>
</Ellipsis>
<a-row>
<a-col :span="12">
<j-row>
<j-col :span="12">
<Ellipsis>
<div>
{{ slotProps.description }}
</div>
</Ellipsis>
</a-col>
</a-row>
</j-col>
</j-row>
</template>
<template #actions="item">
<PermissionButton
@ -88,7 +88,7 @@
</CardBox>
</template>
<template #state="slotProps">
<a-badge
<j-badge
:text="
slotProps.state?.value === 'started'
? '正常'
@ -102,7 +102,7 @@
/>
</template>
<template #action="slotProps">
<a-space :size="16">
<j-space :size="16">
<template
v-for="i in getActions(slotProps, 'table')"
:key="i.key"
@ -123,7 +123,7 @@
/></template>
</PermissionButton>
</template>
</a-space>
</j-space>
</template>
</JProTable>
<!-- 新增编辑 -->