Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
JiangQiming 2023-03-13 17:47:20 +08:00
commit 204a15ebb3
14 changed files with 248 additions and 94 deletions

View File

@ -24,3 +24,5 @@ export const _execute = (id: string) => server.post(`/scene/${id}/_execute`);
export const queryBuiltInParams = (data: any, params?: any) => server.post(`/scene/parse-variables`, data, params); export const queryBuiltInParams = (data: any, params?: any) => server.post(`/scene/parse-variables`, data, params);
export const getParseTerm = (data: Record<string, any>) => server.post(`/scene/parse-term-column`, data) export const getParseTerm = (data: Record<string, any>) => server.post(`/scene/parse-term-column`, data)
export const queryAlarmList = (data: Record<string, any>) => server.post(`/alarm/config/_query/`, data)

View File

@ -1,6 +1,6 @@
<template> <template>
<page-container> <page-container>
<j-advanced-search <pro-search
:columns="columns" :columns="columns"
target="northbound-aliyun" target="northbound-aliyun"
@search="handleSearch" @search="handleSearch"
@ -50,13 +50,17 @@
<div class="card-item-content-text"> <div class="card-item-content-text">
网桥产品 网桥产品
</div> </div>
<div>{{ slotProps?.bridgeProductName }}</div> <Ellipsis>
<div>{{ slotProps?.bridgeProductName }}</div>
</Ellipsis>
</j-col> </j-col>
<j-col :span="12"> <j-col :span="12">
<div class="card-item-content-text"> <div class="card-item-content-text">
<label>说明</label> <label>说明</label>
</div> </div>
<div>{{ slotProps?.description }}</div> <Ellipsis>
<div>{{ slotProps?.description }}</div>
</Ellipsis>
</j-col> </j-col>
</j-row> </j-row>
</template> </template>

View File

@ -1,6 +1,6 @@
<template> <template>
<page-container> <page-container>
<j-advanced-search <pro-search
:columns="columns" :columns="columns"
target="northbound-dueros" target="northbound-dueros"
@search="handleSearch" @search="handleSearch"
@ -48,13 +48,17 @@
<j-row> <j-row>
<j-col :span="12"> <j-col :span="12">
<div class="card-item-content-text">产品</div> <div class="card-item-content-text">产品</div>
<div>{{ slotProps?.productName }}</div> <Ellipsis>
<div>{{ slotProps?.productName }}</div>
</Ellipsis>
</j-col> </j-col>
<j-col :span="12"> <j-col :span="12">
<div class="card-item-content-text"> <div class="card-item-content-text">
设备类型 设备类型
</div> </div>
<div>{{ slotProps?.applianceType?.text }}</div> <Ellipsis>
<div>{{ slotProps?.applianceType?.text }}</div>
</Ellipsis>
</j-col> </j-col>
</j-row> </j-row>
</template> </template>

View File

@ -1,6 +1,6 @@
<!-- 新增编辑弹窗 --> <!-- 新增编辑弹窗 -->
<template> <template>
<a-modal <j-modal
:title="props.title" :title="props.title"
:maskClosable="false" :maskClosable="false"
destroy-on-close destroy-on-close
@ -11,38 +11,38 @@
cancelText="取消" cancelText="取消"
v-bind="layout" v-bind="layout"
> >
<a-form <j-form
layout="vertical" layout="vertical"
ref="formRef" ref="formRef"
:rules="rules" :rules="rules"
:model="formModel" :model="formModel"
> >
<a-form-item label="名称" name="name"> <j-form-item label="名称" name="name">
<a-input <j-input
v-model:value="formModel.name" v-model:value="formModel.name"
:maxlength="64" :maxlength="64"
placeholder="请输入名称" placeholder="请输入名称"
/> />
</a-form-item> </j-form-item>
<a-form-item label="排序" name="sortIndex"> <j-form-item label="排序" name="sortIndex">
<a-input-number <j-input-number
style="width: 100%" style="width: 100%"
id="inputNumber" id="inputNumber"
v-model:value="formModel.sortIndex" v-model:value="formModel.sortIndex"
:min="1" :min="1"
placeholder="请输入排序" placeholder="请输入排序"
/> />
</a-form-item> </j-form-item>
<a-form-item label="说明"> <j-form-item label="说明">
<a-textarea <j-textarea
v-model:value="formModel.description" v-model:value="formModel.description"
show-count show-count
:maxlength="200" :maxlength="200"
placeholder="请输入说明" placeholder="请输入说明"
/> />
</a-form-item> </j-form-item>
</a-form> </j-form>
</a-modal> </j-modal>
</template> </template>
<script setup lang="ts" name="modifyModal"> <script setup lang="ts" name="modifyModal">
import { PropType } from 'vue'; import { PropType } from 'vue';
@ -111,20 +111,20 @@ const submitData = async () => {
if (props.isChild === 1) { if (props.isChild === 1) {
addParams.value = { addParams.value = {
...formModel.value, ...formModel.value,
sortIndex: // sortIndex:
childArr.value[childArr.value.length - 1].sortIndex + 1, // childArr.value[childArr.value.length - 1].sortIndex + 1,
parentId: addObj.value.id, parentId: addObj.value.id,
}; };
} else if (props.isChild === 2) { } else if (props.isChild === 2) {
addParams.value = { addParams.value = {
parentId: addObj.value.id, parentId: addObj.value.id,
...formModel.value, ...formModel.value,
sortIndex: 1, // sortIndex: 1,
}; };
} else if (props.isChild === 3) { } else if (props.isChild === 3) {
addParams.value = { addParams.value = {
...formModel.value, ...formModel.value,
sortIndex: arr.value[arr.value.length - 1].sortIndex + 1, // sortIndex: arr.value[arr.value.length - 1].sortIndex + 1,
}; };
} }
const res = await saveTree(addParams.value); const res = await saveTree(addParams.value);

View File

@ -1,12 +1,12 @@
<!--产品分类 --> <!--产品分类 -->
<template> <template>
<a-card class="product-category"> <page-container>
<Search <pro-search
:columns="query.columns" :columns="query.columns"
target="category" target="category"
@search="handleSearch" @search="handleSearch"
/> />
<JTable <JProTable
ref="tableRef" ref="tableRef"
:columns="table.columns" :columns="table.columns"
:dataSource="dataSource" :dataSource="dataSource"
@ -25,46 +25,38 @@
:loading="tableLoading" :loading="tableLoading"
> >
<template #headerTitle> <template #headerTitle>
<a-button type="primary" @click="add" <PermissionButton
><plus-outlined />新增</a-button type="primary"
@click="add"
hasPermission="device/Category:add"
> >
<template #icon><AIcon type="PlusOutlined" /></template>
新增
</PermissionButton>
</template> </template>
<template #action="slotProps"> <template #action="slotProps">
<a-space :size="16"> <j-space :size="16">
<a-tooltip <template
v-for="i in getActions(slotProps, 'table')" v-for="i in getActions(slotProps, 'table')"
:key="i.key" :key="i.key"
v-bind="i.tooltip"
> >
<a-popconfirm <PermissionButton
v-if="i.popConfirm"
v-bind="i.popConfirm"
:disabled="i.disabled" :disabled="i.disabled"
> :popConfirm="i.popConfirm"
<a-button :hasPermission="'device/Category:' + i.key"
:disabled="i.disabled" :tooltip="{
style="padding: 0" ...i.tooltip,
type="link" }"
><AIcon :type="i.icon" @click="i.onClick"
/></a-button>
</a-popconfirm>
<a-button
style="padding: 0"
type="link" type="link"
v-else style="padding: 0px"
@click="i.onClick && i.onClick(slotProps)"
> >
<a-button <template #icon><AIcon :type="i.icon" /></template>
:disabled="i.disabled" </PermissionButton>
style="padding: 0" </template>
type="link" </j-space>
><AIcon :type="i.icon"
/></a-button>
</a-button>
</a-tooltip>
</a-space>
</template> </template>
</JTable> </JProTable>
<!-- 新增和编辑弹窗 --> <!-- 新增和编辑弹窗 -->
<ModifyModal <ModifyModal
ref="modifyRef" ref="modifyRef"
@ -74,7 +66,7 @@
:isChild="isChild" :isChild="isChild"
@refresh="refresh" @refresh="refresh"
/> />
</a-card> </page-container>
</template> </template>
<script lang="ts" name="Category" setup> <script lang="ts" name="Category" setup>
import { queryTree, deleteTree } from '@/api/device/category'; import { queryTree, deleteTree } from '@/api/device/category';
@ -146,6 +138,7 @@ const getTableData = async () => {
if (res.status === 200) { if (res.status === 200) {
dataSource.value = res.result; dataSource.value = res.result;
} }
tableLoading.value = false;
}; };
getTableData(); getTableData();
/** /**
@ -168,7 +161,7 @@ const getActions = (
if (!data) return []; if (!data) return [];
const actions = [ const actions = [
{ {
key: 'edit', key: 'update',
text: '编辑', text: '编辑',
tooltip: { tooltip: {
title: '编辑', title: '编辑',
@ -238,8 +231,8 @@ const table = reactive({
}, },
{ {
title: '说明', title: '说明',
dataIndex: 'describe', dataIndex: 'description',
key: 'describe', key: 'description',
}, },
{ {
title: '操作', title: '操作',

View File

@ -336,7 +336,7 @@ const setDevMesChartOption = (
grid: { grid: {
top: '2%', top: '2%',
bottom: '5%', bottom: '5%',
left: maxY > 100000 ? '90px' : '50px', left: maxY > 100000 ? '90px' : '60px',
right: '50px', right: '50px',
}, },
series: [ series: [

View File

@ -1,6 +1,6 @@
<template> <template>
<j-card> <j-card>
<j-advanced-search <pro-search
:columns="columns" :columns="columns"
target="device-instance-log" target="device-instance-log"
@search="handleSearch" @search="handleSearch"

View File

@ -1,5 +1,5 @@
<template> <template>
<j-advanced-search class="search" type="simple" :columns="columns" target="device-instance-running-events" @search="handleSearch" /> <pro-search class="search" type="simple" :columns="columns" target="device-instance-running-events" @search="handleSearch" />
<JProTable <JProTable
ref="eventsRef" ref="eventsRef"
:columns="columns" :columns="columns"

View File

@ -191,15 +191,17 @@ watch(
() => route.params.id, () => route.params.id,
(newId) => { (newId) => {
if (newId) { if (newId) {
instanceStore.tabActiveKey = 'Info'; instanceStore.refresh(String(newId));
instanceStore.refresh(newId as string);
getStatus(String(newId)); getStatus(String(newId));
} }
}, },
{ immediate: true, deep: true }, { immediate: true, deep: true },
); );
onMounted(() => {
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info'
})
const onBack = () => { const onBack = () => {
menuStory.jumpPage('device/Instance'); menuStory.jumpPage('device/Instance');
}; };
@ -282,7 +284,7 @@ watchEffect(() => {
tab: 'OPC UA', tab: 'OPC UA',
}); });
} }
if (instanceStore.current.deviceType?.value === 'gateway') { if (instanceStore.current.deviceType?.value === 'gateway' && !keys.includes('ChildDevice')) {
// //
list.value.push({ list.value.push({
key: 'ChildDevice', key: 'ChildDevice',

View File

@ -1,6 +1,6 @@
<template> <template>
<page-container> <page-container>
<j-advanced-search <pro-search
:columns="columns" :columns="columns"
target="device-instance" target="device-instance"
@search="handleSearch" @search="handleSearch"
@ -289,6 +289,7 @@ import { queryTree } from '@/api/device/category';
import { useMenuStore } from '@/store/menu'; import { useMenuStore } from '@/store/menu';
import type { ActionsType } from './typings'; import type { ActionsType } from './typings';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { throttle } from 'lodash-es';
const instanceRef = ref<Record<string, any>>({}); const instanceRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({}); const params = ref<Record<string, any>>({});
@ -315,7 +316,7 @@ const columns = [
key: 'id', key: 'id',
search: { search: {
type: 'string', type: 'string',
defaultTermType: 'eq' defaultTermType: 'eq',
}, },
}, },
{ {
@ -324,7 +325,7 @@ const columns = [
key: 'name', key: 'name',
search: { search: {
type: 'string', type: 'string',
first: true first: true,
}, },
}, },
{ {
@ -524,6 +525,12 @@ const paramsFormat = (
} }
}; };
onMounted(() => {
if(history.state?.params?.type === 'add'){
handleAdd()
}
})
const handleParams = (config: Record<string, any>) => { const handleParams = (config: Record<string, any>) => {
const _terms: Record<string, any> = {}; const _terms: Record<string, any> = {};
paramsFormat(config, _terms); paramsFormat(config, _terms);

View File

@ -16,22 +16,22 @@
}} }}
</div> </div>
<div class="new-alarm-item-content"> <div class="new-alarm-item-content">
<a-tooltip <j-tooltip
:title="item.alarmName" :title="item.alarmName"
placement="topLeft" placement="topLeft"
> >
<a @click="()=>{return jumpDetail(item)}">{{ item.alarmName }}</a> <a @click="()=>{return jumpDetail(item)}">{{ item.alarmName }}</a>
</a-tooltip> </j-tooltip>
</div> </div>
<div class="new-alarm-item-state"> <div class="new-alarm-item-state">
<a-badge <j-badge
:status=" :status="
item.state?.value === 'warning' item.state?.value === 'warning'
? 'error' ? 'error'
: 'default' : 'default'
" "
> >
</a-badge> </j-badge>
<span <span
:class=" :class="
item.state?.value === 'warning' item.state?.value === 'warning'
@ -55,7 +55,7 @@
</ul> </ul>
</div> </div>
<div v-else class="empty-body"> <div v-else class="empty-body">
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE"></a-empty> <j-empty :image="Empty.PRESENTED_IMAGE_SIMPLE"></j-empty>
</div> </div>
</div> </div>
</template> </template>
@ -73,7 +73,7 @@ const props = defineProps({
}); });
const menuStore = useMenuStore(); const menuStore = useMenuStore();
const jumpDetail = (item:any) =>{ const jumpDetail = (item:any) =>{
menuStore.jumpPage(`rule-engine/Alarm/Log/Detail`,{id:item.id},{detail:true}); menuStore.jumpPage(`rule-engine/Alarm/Log/Detail`,{id:item.id,detail:true});
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -1,8 +1,8 @@
<template> <template>
<page-container> <page-container>
<div class="DashBoardBox"> <div class="DashBoardBox">
<a-row :gutter="24"> <j-row :gutter="24">
<a-col :span="6"> <j-col :span="6">
<TopCard <TopCard
title="今日告警" title="今日告警"
:value="state.today" :value="state.today"
@ -10,33 +10,33 @@
> >
<Charts :options="state.fifteenOptions"></Charts> <Charts :options="state.fifteenOptions"></Charts>
</TopCard> </TopCard>
</a-col> </j-col>
<a-col :span="6"> <j-col :span="6">
<TopCard <TopCard
title="告警配置" title="告警配置"
:value="state.config" :value="state.config"
:footer="alarmState" :footer="alarmState"
:img="getImage('/device/device-number.png')" :img="getImage('/device/device-number.png')"
></TopCard> ></TopCard>
</a-col> </j-col>
<a-col :span="12"> <j-col :span="12">
<NewAlarm :alarm-list="state.alarmList"></NewAlarm> <NewAlarm :alarm-list="state.alarmList"></NewAlarm>
</a-col> </j-col>
</a-row> </j-row>
<a-row :gutter="24"> <j-row :gutter="24">
<a-col :span="24"> <j-col :span="24">
<div class="alarm-card"> <div class="alarm-card">
<Guide> <Guide>
<template #title> <template #title>
<span style="margin-right: 24px">告警统计</span> <span style="margin-right: 24px">告警统计</span>
<a-select <j-select
style="width: 40%" style="width: 40%"
v-model:value="queryCodition.targetType" v-model:value="queryCodition.targetType"
:options=" :options="
isNoCommunity ? selectOpt1 : selectOpt2 isNoCommunity ? selectOpt1 : selectOpt2
" "
@change="selectChange" @change="selectChange"
></a-select> ></j-select>
</template> </template>
<template #extra> <template #extra>
<TimeSelect <TimeSelect
@ -63,13 +63,13 @@
</li> </li>
</ul> </ul>
<div v-else class="empty-body"> <div v-else class="empty-body">
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE"></a-empty> <j-empty :image="Empty.PRESENTED_IMAGE_SIMPLE"></j-empty>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</a-col> </j-col>
</a-row> </j-row>
</div> </div>
</page-container> </page-container>
</template> </template>

View File

@ -343,6 +343,11 @@
@cancel="onPropsCancel" @cancel="onPropsCancel"
/> />
</template> </template>
<TriggerAlarm
:id="_data.id"
v-if="triggerVisible"
@close="triggerVisible = false"
/>
</div> </div>
</template> </template>
@ -353,6 +358,12 @@ import { PropType } from 'vue';
import { ActionsType, ParallelType } from '../../../typings'; import { ActionsType, ParallelType } from '../../../typings';
import Modal from '../Modal/index.vue'; import Modal from '../Modal/index.vue';
import ActionTypeComponent from '../Modal/ActionTypeComponent.vue'; import ActionTypeComponent from '../Modal/ActionTypeComponent.vue';
import TriggerAlarm from '../TriggerAlarm/index.vue';
import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia';
const sceneStore = useSceneStore();
const { data: _data } = storeToRefs(sceneStore);
const props = defineProps({ const props = defineProps({
branchesName: { branchesName: {
@ -438,8 +449,8 @@ const onAdd = () => {
}; };
const onType = (_type: string) => { const onType = (_type: string) => {
actionType.value = _type actionType.value = _type;
} };
const onPropsOk = (data: ActionsType, options?: any) => { const onPropsOk = (data: ActionsType, options?: any) => {
emit('update', data, options); emit('update', data, options);

View File

@ -0,0 +1,131 @@
<template>
<j-modal
:width="1000"
@cancel="emit('close')"
@ok="emit('close')"
visible
title="关联此场景的告警"
>
<div style="margin-bottom: 24px">关联告警数量{{ count }}</div>
<JProTable
:columns="columns"
:request="queryAlarmList"
model="TABLE"
:bodyStyle="{ padding: 0 }"
:defaultParams="{
sorts: [{ name: 'createTime', order: 'desc' }],
terms: [
{
terms: [
{
column: 'id',
value: id,
termType: 'rule-bind-alarm',
},
],
},
],
}"
>
<template #level="slotProps">
{{ levelList.find(i => slotProps.level === i.level)?.title || '' }}
</template>
<template #targetType="slotProps">
{{ map[slotProps.targetType] }}
</template>
<template #state="slotProps">
<j-badge
:text="slotProps.state?.text"
:status="
slotProps.state?.value === 'disabled'
? 'error'
: 'success'
"
/>
</template>
</JProTable>
</j-modal>
</template>
<script setup lang="ts">
import { queryAlarmList } from '@/api/rule-engine/scene';
import {
getAlarmLevel,
getAlarmConfigCount,
} from '@/api/rule-engine/dashboard';
const props = defineProps({
id: {
type: String,
default: '',
},
});
const emit = defineEmits(['close']);
const count = ref<number>(0);
const levelList = ref<any[]>([]);
const map = {
product: '产品',
device: '设备',
org: '组织',
other: '其他',
};
const columns = [
{
dataIndex: 'name',
fixed: 'left',
ellipsis: true,
title: '名称',
},
{
dataIndex: 'targetType',
title: '类型',
scopedSlots: true,
},
{
dataIndex: 'level',
title: '告警级别',
scopedSlots: true,
},
{
dataIndex: 'state',
title: '状态',
scopedSlots: true,
},
{
dataIndex: 'description',
title: '说明',
ellipsis: true,
},
];
watch(
() => props.id,
(newId) => {
if (newId) {
getAlarmConfigCount({
terms: [
{
column: 'id$rule-bind-alarm',
value: newId,
},
],
}).then((resp) => {
if (resp.status === 200) {
count.value = (resp.result || 0) as number;
}
});
}
},
{ immediate: true },
);
onMounted(() => {
getAlarmLevel().then((resp) => {
if (resp.status === 200) {
levelList.value = resp.result?.levels || []
}
});
});
</script>