Compare commits

...

21 Commits

Author SHA1 Message Date
xuziqiang
6137aaa4b6 fix: 修改同项目登录信息污染 2024-07-11 15:53:41 +08:00
xuziqiang
4d49ef4e42 Merge branch 'temp' of http://123.60.103.97:3000/xuziqiang/SaaS-lib into temp 2024-07-11 11:55:53 +08:00
xuziqiang
131913e52d fix: 调整验证码校验 2024-07-11 11:55:52 +08:00
fks-xuxinyue
5ed7449392 taskId:083 remark:"commit" 2024-07-11 09:17:20 +08:00
fks-yangshouda
a698c69b7c 设备中心 - 能耗监测 - 图表 左侧树和右侧组件联动 2024-07-11 08:45:20 +08:00
xuziqiang
5fde3175f1 fix: tree组件显示异常 2024-07-10 15:17:14 +08:00
xuziqiang
149f44a8dd feat: 调整树样式 2024-07-10 14:51:42 +08:00
fks-xuxinyue
060ac6d486 taskid:067 remark:commit 2024-07-10 11:01:46 +08:00
xuziqiang
7b624f6549 Merge branch 'temp' of http://123.60.103.97:3000/xuziqiang/SaaS-lib into temp 2024-07-09 18:04:10 +08:00
xuziqiang
717b2aad72 feat: 分组管理设备树 2024-07-09 18:04:09 +08:00
fks-yangshouda
f98a8be0b9 Merge branch 'temp' of http://123.60.103.97:3000/xuziqiang/SaaS-lib into temp 2024-07-09 17:35:59 +08:00
fks-yangshouda
de2202e360 1.监控中心 - 设备监测 对接接口 2024-07-09 17:35:45 +08:00
xuziqiang
6b93422b08 Merge branch 'temp' of http://123.60.103.97:3000/xuziqiang/SaaS-lib into temp 2024-07-09 16:27:22 +08:00
xuziqiang
30286253e7 feat: 分组管理 2024-07-09 16:27:21 +08:00
fks-yangshouda
a0d928feaa 1.监控中心 - 设备监测 左侧树与右侧组件联动
2.对接设备类型接口
2024-07-09 16:08:31 +08:00
zhaohy
50ca1daca4 add:对接设备告警 对接设备告警 设备类型 设备名称 设备点位 2024-07-09 15:56:18 +08:00
fks-yangshouda
fb1980d73a 1.设备监控 图表下载 时间选择改为三天
2.能耗监控 - 图表前台页面
2024-07-09 10:10:17 +08:00
xuziqiang
ab17c4a1f9 fix: 获取字典数据方法 2024-07-08 17:44:07 +08:00
xuziqiang
aab4c9a600 fix: 高度调整 2024-07-08 17:32:10 +08:00
fks-xuxinyue
0855659d74 taskId:067 remark:"commit" 2024-07-08 11:45:01 +08:00
zhaohy
3abdde797b fix: 还原工作组件 修改告警设置页面 2024-07-08 11:24:24 +08:00
64 changed files with 4625 additions and 1583 deletions

View File

@@ -21,12 +21,12 @@
// watch(
// () => router.currentRoute.value,
// (e) => {
// // if (Cookies.get('nervsid') === undefined && e.fullPath !== '/login') {
// // if (Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`) === undefined && e.fullPath !== '/login') {
// // message.warn('登陆信息已过期,请重新登录!', 1);
// // router.push('/login');
// // }
// // } else {
// // if (Cookies.get('nervsid') !== undefined && e === '/login') {
// // if (Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`) !== undefined && e === '/login') {
// // let url = window.sessionStorage.getItem('url');
// // url === null ? window.sessionStorage.setItem('url', '/doorway/visitor/audit') : '';
// // router.push(url === null ? '/doorway' : url);
@@ -43,6 +43,20 @@
</script>
<style lang="less" scoped>
:deep(.ns-content-main) {
.ant-tabs-content {
height: 100%;
}
}
// :deep(.ant-menu-item),
// :deep(.ant-menu-submenu-title) {
// &:not(.ant-menu-item-selected) {
// // &:not(.ant-menu-item-active) {
// .anticon {
// color: rgb(141, 150, 163);
// }
// }
// }
// :deep(.ns-content) {
// // padding-top: 80px !important;
// }

View File

@@ -0,0 +1,5 @@
export enum deviceAlarms {
getTableList = '/carbon-smart/api/AlarmEquipment/selectAlarmEquipment', //设备告警分页
addOrUpNewData = '/carbon-smart/api/AlarmEquipment/creatOrUpdate', //设备告警添加 修海
del = '/carbon-smart/api/AlarmEquipment/delete', //设备告警删除
}

View File

@@ -1,12 +1,15 @@
// 碳排因子库接口
export enum carbonEmissionFactorLibrary {
getTableList = '/carbon/emission/factor/queryCarbonFactorPage',
addNewData = '/carbon/emission/factor/creatOrUpdate',
editUser = '/carbon-smart/api/user/edit',
frozen = '/carbon-smart/api/user/frozen',
resetPwd = '/carbon-smart/api/user/resetPwd',
del = '/carbon-smart/api/user/del',
batchDel = '/carbon-smart/api/user/batchDel',
getCarbonFactorTree = '/carbon/emission/type/getCarbonFactorTree',
queryDeptTree = '/carbon-smart/api/user/queryDeptTree',
queryUserPerList = '/carbon-smart/api/user/queryUserPerList',
getTableList = '/carbon-smart/api/carbon/emission/factor/queryCarbonFactorPage',
creatOrUpdate = '/carbon-smart/api/carbon/emission/factor/creatOrUpdate',
del = '/carbon-smart/api/carbon/emission/factor/del',
getEmissionProcess = '/carbon-smart/api/carbon/emission/factor/getEmissionProcess',
findById = '/carbon-smart/api/carbon/emission/factor/findById',
getCarbonFactorTree = '/carbon-smart/api/carbon/emission/type/getCarbonFactorTree',
creat = '/carbon-smart/api/carbon/emission/type/creatOrUpdate',
delTreeNode = '/carbon-smart/api/carbon/emission/type/del',
}
// 碳排管理-碳排统计接口
export enum energyConsumption {
pageList = '/carbon-smart/api/carbon/stats/pageList',
}

View File

@@ -3,9 +3,16 @@ export enum device {
queryDeviceTree = `${BASE_URL}/deviceInfo/queryDeviceTree`, // 左侧树
queryDevicePage = `${BASE_URL}/deviceInfo/queryDevicePage`, // 列表
dropArea = `${BASE_URL}/deviceInfo/dropArea`, // 查询下拉区域
queryDevicePoint = `${BASE_URL}/deviceInfo/queryDevicePoint`, // 获取设备点位
}
export enum group {
queryDeviceGroupTree = `${BASE_URL}/deviceGroup/queryDeviceGroupTree`, // 左侧树
creatOrUpdate = `${BASE_URL}/deviceGroup/creatOrUpdate`, // 左侧树节点新增编辑
del = `${BASE_URL}/deviceGroup/del`,
move = `${BASE_URL}/deviceGroup/move`,
queryEditGroup = `${BASE_URL}/deviceGroup/queryEditGroup`, // 分组树
queryGroupPage = `${BASE_URL}/deviceGroup/queryGroupPage`, // 分组列表
saveGroupList = `${BASE_URL}/deviceGroup/saveGroupList`, // 分组保存
delGroupList = `${BASE_URL}/deviceGroup/delGroupList`, // 分组删除
}

View File

@@ -1,3 +1,6 @@
import { get } from 'lodash-es';
import { http } from '/nerv-lib/util/http';
/***
*配置接口 格式 module:Array<resource>
*/
@@ -6,4 +9,32 @@ export const apiModule = {
};
export const BASE_URL = '/carbon-smart';
export const dict = `${BASE_URL}/client/dict/listByKey`;
interface dictHttpConfig {
api?: string;
keyField?: string;
params: object;
transform?: Function;
}
/**
* 获取字典数据(首次获取,后续读缓存)
*/
export const dict = async ({
api = `${BASE_URL}/client/dict/listByKey`,
params = {},
keyField = 'dicKey',
transform = (res: any) => res,
}: dictHttpConfig) => {
const dictMap = JSON.parse(sessionStorage.getItem('dictMap') || '{}') as Object;
const key = get(params, keyField) as keyof typeof dictMap;
if (!dictMap.hasOwnProperty(key)) {
const res = await http.post(api, params);
const options = get(transform(res), `data.${key}`);
dictMap[key] = options;
sessionStorage.setItem('dictMap', JSON.stringify(dictMap));
}
return Promise.resolve({ data: { data: get(dictMap, key) } });
};

View File

@@ -1,13 +1,14 @@
<template>
<ns-modal
ref="modalRef"
centered
v-bind="extraModalConfig"
destroyOnClose
v-model:visible="visible"
:title="title"
:okButtonProps="buttonProps"
@ok="handleOk">
<ns-form ref="formRef" :schemas="schemas" :model="formData" formLayout="formVertical" />
<ns-form ref="formRef" :schemas="schemas" :model="formData" formLayout="vertical" />
</ns-modal>
</template>
@@ -21,6 +22,7 @@
api: string | object | Function;
data?: object;
extraModalConfig?: object;
success?: Function;
};
const route = useRoute();
const { httpRequest } = useApi();
@@ -59,14 +61,15 @@
formRef.value
.triggerSubmit()
.then((data: any) => {
const { api } = props;
const { api, success } = props;
const requestConfig: HttpRequestConfig = { method: 'POST' };
const { params } = route;
httpRequest({ api, params: data, pathParams: params, requestConfig })
.then(() => {
.then((res) => {
NsMessage.success('操作成功', 1, () => {
toggle();
success && success(res);
});
})
.finally(() => {

View File

@@ -71,7 +71,7 @@ export const appConfig = {
// 解决初始化登录select无初始值的问题
selectDefaultValue.value = finalId;
const userInfo = JSON.parse(sessionStorage.getItem('userInfo')!);
const userInfo = JSON.parse(sessionStorage.getItem(import.meta.env.VITE_PUBLIC_PATH)!);
const ownOrgInfo = userInfo.linkList?.filter(({ orgId: id }) => id === finalId)[0];
return http.post('/carbon-smart/user/login/logInPermission', ownOrgInfo).then((res) => {

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="8.98974609375" height="8.14453125" viewBox="0 0 8.98974609375 8.14453125" fill="none">
<path d="M7.93101 4.53462L6.23624 4.53462L5.52032 3.46466L6.49265 1.79796L5.44381 0L3.32621 0L2.27236 1.79796L3.29704 3.54617L2.62652 4.54987L1.05875 4.54987L0 6.34726L1.05875 8.1452L3.17144 8.1452L4.06128 6.62695L4.93729 6.62695L5.81886 8.13029L7.93102 8.13029L8.98978 6.33258L7.93101 4.53462ZM3.19734 4.59392L3.8643 3.59569L5.00553 3.59569L5.73168 4.68208L4.88461 6.1274L4.09644 6.1274L3.19734 4.59392Z" fill="url(#linear_fill_60_2513)" >
</path>
<defs>
<linearGradient id="linear_fill_60_2513" x1="4.494873046875" y1="0" x2="4.494873046875" y2="8.14453125" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#4DACE6" />
<stop offset="1" stop-color="#2A93D5" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 888 B

View File

@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1720061440927" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11363" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M659.925333 128a74.666667 74.666667 0 0 1 71.338667 52.618667L754.56 256H821.333333c64.8 0 117.333333 52.533333 117.333334 117.333333v426.666667c0 64.8-52.533333 117.333333-117.333334 117.333333H202.666667c-64.8 0-117.333333-52.533333-117.333334-117.333333V373.333333c0-64.8 52.533333-117.333333 117.333334-117.333333h66.773333l23.296-75.381333A74.666667 74.666667 0 0 1 364.074667 128h295.850666zM512 405.333333c-88.362667 0-160 71.637333-160 160 0 88.362667 71.637333 160 160 160 88.362667 0 160-71.637333 160-160 0-88.362667-71.637333-160-160-160z m0 256a96 96 0 1 0 0-192 96 96 0 0 0 0 192z" fill="#000000" p-id="11364"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1720061440927" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11363" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M659.925333 128a74.666667 74.666667 0 0 1 71.338667 52.618667L754.56 256H821.333333c64.8 0 117.333333 52.533333 117.333334 117.333333v426.666667c0 64.8-52.533333 117.333333-117.333334 117.333333H202.666667c-64.8 0-117.333333-52.533333-117.333334-117.333333V373.333333c0-64.8 52.533333-117.333333 117.333334-117.333333h66.773333l23.296-75.381333A74.666667 74.666667 0 0 1 364.074667 128h295.850666zM512 405.333333c-88.362667 0-160 71.637333-160 160 0 88.362667 71.637333 160 160 160 88.362667 0 160-71.637333 160-160 0-88.362667-71.637333-160-160-160z m0 256a96 96 0 1 0 0-192 96 96 0 0 0 0 192z" p-id="11364"></path></svg>

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 954 B

View File

@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1720061108318" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10061" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M512 12L94.71 138v314.92C94.71 657.7 263.86 880.16 512 1012c248.14-131.84 417.29-354.3 417.29-559.08V138z m209.91 510H562v159.91H462V522H302.09V422H462V262.09h100V422h159.91z" fill="#2c2c2c" p-id="10062"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1720061108318" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10061" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M512 12L94.71 138v314.92C94.71 657.7 263.86 880.16 512 1012c248.14-131.84 417.29-354.3 417.29-559.08V138z m209.91 510H562v159.91H462V522H302.09V422H462V262.09h100V422h159.91z" p-id="10062"></path></svg>

Before

Width:  |  Height:  |  Size: 549 B

After

Width:  |  Height:  |  Size: 535 B

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="9.1005859375" height="9.099609375" viewBox="0 0 9.1005859375 9.099609375" fill="none">
<path d="M7.01762 4.69157L2.30136 4.69157C2.04227 4.66303 1.84615 4.44409 1.84615 4.18343C1.84615 3.92276 2.04227 3.70384 2.30136 3.67525L7.01762 3.67525C7.27673 3.70384 7.47285 3.92276 7.47285 4.18343C7.47285 4.44409 7.27673 4.66303 7.01762 4.69157ZM7.01762 6.69587L2.30136 6.69587C2.04227 6.6673 1.84615 6.44837 1.84615 6.18771C1.84615 5.92704 2.04227 5.70809 2.30136 5.67952L7.01762 5.67952C7.27673 5.70809 7.47285 5.92704 7.47285 6.18771C7.47285 6.44837 7.27673 6.6673 7.01762 6.69587ZM9.09498 8.33346L9.09498 1.98149C9.09498 1.21918 8.46167 1.27564 8.46167 1.27564L4.88941 1.27564C4.77006 1.27634 4.65399 1.23653 4.56021 1.16271C4.56021 1.16271 4.40816 0.880585 4.12926 0.428757C3.87605 -0.0795288 3.54708 0.00531769 3.54708 0.00531769L0.785301 0.00531769C0 0.00531769 0 0.824379 0 0.824379L0 8.27722C0 9.2087 0.633301 9.09628 0.633301 9.09628L8.53744 9.09628C9.19643 9.09547 9.09498 8.33345 9.09498 8.33345L9.09498 8.33346Z" fill="url(#linear_fill_60_2485)" >
</path>
<defs>
<linearGradient id="linear_fill_60_2485" x1="4.55029296875" y1="0" x2="4.55029296875" y2="9.099609375" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#4DACE6" />
<stop offset="1" stop-color="#2A93D5" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14.5244140625" height="14.5294189453125" viewBox="0 0 14.5244140625 14.5294189453125" fill="none">
<path d="M6.61912 12.2301C6.6032 12.5346 6.44482 12.8138 6.19166 12.9837L3.73703 14.4259C3.46877 14.5639 3.15036 14.5639 2.8821 14.4259L0.427467 12.9837C0.174295 12.8138 0.0159187 12.5346 0 12.2301L0 9.34584C0.0162582 9.04147 0.174555 8.76241 0.427467 8.59227L2.88209 7.15012C3.15031 7.01189 3.46881 7.01189 3.73703 7.15012L6.19165 8.59337C6.44482 8.76326 6.6032 9.04247 6.61912 9.34694L6.61912 12.2301ZM14.5239 12.2301C14.508 12.5346 14.3497 12.8138 14.0965 12.9837L11.6419 14.4259C11.3736 14.5639 11.0552 14.5639 10.7869 14.4259L8.33231 12.9837C8.079 12.814 7.92058 12.5347 7.90484 12.2301L7.90484 9.34584C7.92075 9.04137 8.07913 8.76217 8.33231 8.59227L10.7869 7.14902C11.0551 7.01079 11.3736 7.01079 11.6419 7.14902L14.0965 8.59117C14.3495 8.76118 14.5079 9.04031 14.524 9.34475L14.5239 12.2301ZM10.5842 5.18465C10.5683 5.48912 10.4099 5.76833 10.1567 5.93822L7.70211 7.38147C7.43394 7.51988 7.11535 7.51988 6.84718 7.38147L4.39256 5.93822C4.13938 5.76833 3.981 5.48912 3.96508 5.18465L3.96508 2.30036C3.98117 1.99594 4.13951 1.7168 4.39256 1.54678L6.84718 0.103527C7.11544 -0.0345116 7.43386 -0.0345116 7.70212 0.103527L10.1567 1.54568C10.41 1.71546 10.5685 1.99473 10.5842 2.29926L10.5842 5.18465Z" fill="currentColor" >
</path>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1720430904326" class="icon" viewBox="0 0 1354 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3403" xmlns:xlink="http://www.w3.org/1999/xlink" width="21.15625" height="16"><path d="M1034.83248 645.406063l-191.508238 191.508238a53.622507 53.622507 0 0 1-75.964301 0l-191.508238-191.508238a53.622507 53.622507 0 0 1 0-75.964301 53.622507 53.622507 0 0 1 75.965301 0l102.775054 102.775054V303.884205a53.622507 53.622507 0 1 1 107.245014 0v368.332611l95.753119-102.775054a53.622507 53.622507 0 0 1 75.965301 0 53.622507 53.622507 0 0 1 1.276988 75.964301z m262.364587-175.548385a365.141641 365.141641 0 0 0-97.030108-98.94609 378.546518 378.546518 0 0 0-29.364729-123.202867 397.697341 397.697341 0 0 0-89.369178-127.671825 404.719277 404.719277 0 0 0-133.418773-91.28516A408.549242 408.549242 0 0 0 792.894706 0.026a400.251318 400.251318 0 0 0-225.977921 67.665377 410.464224 410.464224 0 0 0-97.669102 91.285161 261.088598 261.088598 0 0 0-63.836412-7.65993 247.044727 247.044727 0 0 0-176.18738 70.857348 245.129745 245.129745 0 0 0-72.134336 202.360139 324.925011 324.925011 0 0 0-84.263225 72.77333A308.965158 308.965158 0 0 0 0.053 702.22054a310.242146 310.242146 0 0 0 97.029107 228.531898 331.307952 331.307952 0 0 0 105.969025 69.58136 330.030964 330.030964 0 0 0 127.670826 23.619782h656.233963A360.034688 360.034688 0 0 0 1242.298572 919.261544a360.034688 360.034688 0 0 0 81.071254-116.181932 354.288741 354.288741 0 0 0 27.449748-138.523725 344.713829 344.713829 0 0 0-54.260501-194.699209z" fill="#5656FB" p-id="3404"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -2,13 +2,13 @@ const Base = () => import('/nerv-lib/saas/view/system/layout/content.vue');
const equipment = {
path: '/carbonEmissionManage',
name: 'CarbonEmissionManage',
meta: { title: '碳排管理', icon: 'shebeiguanli', index: 1 },
meta: { title: '碳排管理', icon: 'tanpaiguanli', index: 1 },
redirect: { name: 'CarbonEmissionStatistics' },
children: [
{
path: 'carbonEmissionStatistics',
name: 'CarbonEmissionStatistics',
meta: { title: '碳排统计', hideChildren: true, icon: 'shebeiguanli' },
meta: { title: '碳排统计', hideChildren: true, icon: 'tanpaiguanli' },
component: Base,
redirect: { name: 'CarbonEmissionStatisticsIndex' },
children: [
@@ -28,7 +28,7 @@ const equipment = {
{
path: 'carbonEmissionFactorLibrary',
name: 'CarbonEmissionFactorLibrary',
meta: { title: '碳排因子库', hideChildren: true, icon: 'shebeiguanli' },
meta: { title: '碳排因子库', hideChildren: true, icon: 'tanpaiguanli' },
component: Base,
redirect: { name: 'CarbonEmissionFactorLibraryIndex' },
children: [

View File

@@ -43,6 +43,25 @@ const equipment = {
},
],
},
{
path: 'energyMonitor',
name: 'EnergyMonitor',
meta: { title: '能耗监测', hideChildren: true, icon: 'huanjingjiance' },
component: Base,
redirect: { name: 'EnergyMonitorIndex' },
children: [
{
path: 'index',
name: 'EnergyMonitorIndex',
component: () => import('/@/view/monitor/energyMonitor/index.vue'),
meta: {
title: '能耗监测',
keepAlive: true,
// backApi: [],
},
},
],
},
// {
// path: 'group',
// name: 'Group',

View File

@@ -1,170 +0,0 @@
<template>
<a-form class="ns-form">
<div class="ns-form-title ns-title-extra-box">
<span>查询</span>
<a-button type="link" class="ns-operate-expand" @click="expandAll = !expandAll">
<template v-if="expandAll">
收起筛选
<UpOutlined />
</template>
<template v-else>
展开筛选
<DownOutlined />
</template>
</a-button>
</div>
<template v-if="expandAll">
<a-row :gutter="24">
<a-col :span="6">
<a-select v-model:value="select.yx" style="width: 100%" placeholder="请选择告警优先级">
<a-select-option v-for="(val, index) in 60" :key="index" :value="index">{{
index
}}</a-select-option>
</a-select>
</a-col>
<a-col :span="6">
<a-input v-model:value="select.tilte" placeholder="请输入告警标题关键字" />
</a-col>
<a-col :span="6">
<a-input v-model:value="select.tilte" placeholder="请输入告警标题关键字" />
</a-col>
<a-col :span="6">
<a-select
v-model:value="select.yx"
style="width: 100%"
:autoClearSearchValue="true"
placeholder="请选择启用状态">
<a-select-option :key="1" :value="1"> 启用 </a-select-option>
<a-select-option :key="0" :value="0"> 关闭 </a-select-option>
</a-select>
</a-col>
</a-row>
<a-row :span="24">
<a-col :span="24" class="ns-operate">
<a-button @click="reset">重置</a-button>
<a-button type="primary" @click="search">搜索</a-button>
</a-col>
</a-row>
</template>
</a-form>
<div class="ns-table-header">
<div class="ns-table-title ns-title-extra-box">告警规则</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, provide, ref, toRefs, watch } from 'vue';
import { DownOutlined, UpOutlined } from '@ant-design/icons-vue';
export default {
components: { DownOutlined, UpOutlined },
setup() {
const expandAll = ref(true);
const loading = ref(false);
const select = ref({
title: '',
yx: null,
});
const reset = () => {
console.log(select);
};
const search = () => {
console.log(select);
};
return {
expandAll,
select,
loading,
search,
reset,
};
},
};
</script>
<style lang="less" scoped>
@gap: 16px;
.ns-form {
padding: 16px;
background-color: white;
// border-radius: 8px;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
.ant-row {
flex: 1;
}
.ns-operate {
text-align: right;
margin-left: auto;
margin-top: 16px;
.ns-operate-expand {
display: inline-block;
padding: 4px 2px;
border: unset !important;
.anticon {
margin-left: 4px;
}
&:hover {
border: unset !important;
}
&:focus {
border: unset !important;
}
}
.ant-btn {
margin-left: 6px;
}
}
.ns-form-title {
text-align: left;
height: 22px;
// line-height: 32px;
//font-size: 16px;
font-weight: bold;
user-select: text;
margin-bottom: calc(@gap - 0px);
display: flex;
justify-content: space-between;
align-items: center;
:deep(.ant-btn) {
padding: 0;
}
}
}
.ns-table-header {
min-width: fit-content;
user-select: none;
// padding: 16px 0;
padding-top: 16px;
text-align: right;
position: relative;
height: 48px;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20px;
padding: 0 16px;
display: flex;
.ns-table-header::before {
position: absolute;
top: -20px; /* 与 margin-top 的值相反,以覆盖 margin-top 的区域 */
left: 0;
width: 100%;
height: 20px; /* 与 margin-top 的值相同 */
background-color: gray; /* 设置灰色背景 */
}
.ns-table-title {
text-align: left;
height: 32px;
line-height: 32px;
//font-size: 16px;
font-weight: bold;
user-select: text;
}
.ant-btn {
margin-left: 6px;
}
:first-child.ant-btn {
margin-left: 0;
}
}
</style>

View File

@@ -0,0 +1,287 @@
<!-- 配置设备告警 -->
<template>
<ns-view-list-table v-if="show" ref="mainRef" class="table" v-bind="tableConfig">
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'enableRules'">
<a-switch
:checked="record.enableRules === 1 ? true : false"
:class="{
'blue-background': record.enableRules === 1 ? true : false,
'grey-background': record.enableRules === 1 ? false : true,
}"
@click="clickSwitch({ enableRules: record.enableRules, record: record })" />
</template>
<template v-if="column.dataIndex === 'equipmentInfo'">
{{
record?.enableRules
? record.enableRules + '>' + record.deviceType + '>' + record.deviceName
: '-'
}}
</template>
</template>
</ns-view-list-table>
<!-- 新增or编辑界面 -->
<editConfigureEnergyAlarm ref="editConfigureEnergyAlarms" />
</template>
<script lang="ts">
import { ref, createVNode } from 'vue';
import { http } from '/nerv-lib/util';
import data from '../notificationManagementMock.json';
import { NsMessage, NsModal } from '/nerv-lib/component';
import editConfigureEnergyAlarm from './editConfigureEnergyAlarm.vue';
import { deviceAlarms } from '/@/api/alarmSettings/deviceAlarms';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
export default {
components: { editConfigureEnergyAlarm },
setup() {
//设备告警数据
const configureDeviceAlarmsData = ref({});
const show = ref(false);
const tableConfig = ref({});
const mainRef = ref({});
const editConfigureEnergyAlarms = ref({});
const mockData = ref(data.listData);
const clickSwitch = (data: any) => {
NsModal.confirm({
title: '启用状态',
icon: createVNode(ExclamationCircleOutlined),
content: '确定' + (data.record.enableRules === 1 ? '关闭' : '启用') + '?',
onOk: () => {
http
.post(deviceAlarms.addOrUpNewData, {
id: data.record.id,
enableRules: data.record.enableRules === 1 ? 0 : 1,
})
.then(() => {
NsMessage.success('操作成功');
mainRef.value?.nsTableRef.reload();
});
},
});
};
const doWnload = (url: any) => {
const a = document.createElement('a');
document.body.appendChild(a);
a.href = encodeURI(url);
//设置下载的文件名
// a.download = fileName.value;
//触发a标签的点击事件进行下载
a.click();
};
const setconfigureDeviceAlarmsData = (value: any) => {
configureDeviceAlarmsData.value = value;
show.value = true;
tableConfig.value = {
title: '告警规则',
api: deviceAlarms.getTableList,
value: mockData.value,
headerActions: [
{
label: '新增',
name: 'RoleTypeAdd',
type: 'primary',
handle: () => {
editConfigureEnergyAlarms.value.toggle();
},
},
{
label: '导入',
name: 'groupImport',
type: 'primary',
extra: {
// api: props.postImportApi, // 导入接口名
title: '设备信息', // 弹窗title
templateName: 'whiteListUser', // 所使用的文件名称
indexName: '设备id', // 匹配类型字段
message: [
{ label: '1若必填项未填写则不能进行导入操作' },
{ label: `2、当重复时则更新数据。` },
{ label: '3数据将从模版的第五行进行导入' },
{ label: '4文件导入勿超过5MB' },
],
},
},
{
label: '导出',
name: 'groupExports',
type: 'primary',
handle: () => {
doWnload('/hx-ai-intelligent/asset/file/whiteListUser.xlsx');
},
},
{
label: '批量删除',
type: 'primary',
name: 'userBatchDel',
dynamicDisabled: (data: any) => {
return data.list.length === 0;
},
confirm: true,
isReload: true,
isClearCheck: true,
// api: origanizemanage.batchDel,
dynamicParams: { userIds: 'userId[]' },
},
],
columns: [
{
title: '序号',
dataIndex: 'address',
customRender: (text: any) => {
return text.index + 1;
},
},
{
title: '规则id',
dataIndex: 'ruleId',
},
{
title: '设备信息/节点信息',
dataIndex: 'equipmentInfo',
},
{
title: '对比类型',
dataIndex: 'valueType',
},
{
title: '告警点位',
dataIndex: 'devicePoint',
},
{
title: '判断条件',
dataIndex: 'ruleType',
},
{
title: '异常描述',
dataIndex: 'abnormalDescription',
textEllipsis: true,
},
{
title: '启用通知',
dataIndex: 'enableRules',
},
],
// rowSelection: null, 选择按钮
columnActions: {
title: '操作',
actions: [
{
label: '编辑',
name: 'FeedBackDetail',
dynamicParams: ['uuid', 'appealType'],
handle: (data: any) => {
editConfigureEnergyAlarms.value.toggle(data);
},
},
{
label: '删除',
name: 'FeedBackDetail',
dynamicParams: ['uuid', 'appealType'],
confirm: true,
handle: () => {
// mockData.value.splice(0, 1);
},
},
],
},
formConfig: {
title: value.errorCode,
schemas: [
{
field: 'provider',
label: '设备名称',
component: 'NsInput',
componentProps: {
placeholder: '请输入设备名称',
},
},
{
field: 'provider',
label: '设备点位',
component: 'nsSelectApi',
componentProps: {
api: '/api/community/objs/DictItem',
params: {
pageSize: 100,
code: 'MZ',
},
placeholder: '请选择设备点位',
resultField: 'data',
labelField: 'dictName',
valueField: 'dictValue',
immediate: true,
autoSelectFirst: false,
},
},
{
field: 'payWay',
label: '启用状态',
component: 'NsSelect',
componentProps: {
placeholder: '请选择启用状态',
options: [
{
label: '启用',
value: '1',
},
{
label: '关闭',
value: '0',
},
],
},
},
{
field: 'provider',
label: '异常描述',
component: 'NsInput',
componentProps: {
placeholder: '请输入异常描述关键字',
},
},
],
},
params: { id: value.id },
// pagination: { pageSizeOptions: false },
rowKey: 'id',
};
};
return {
configureDeviceAlarmsData,
show,
clickSwitch,
doWnload,
tableConfig,
editConfigureEnergyAlarms,
setconfigureDeviceAlarmsData,
};
},
};
</script>
<style lang="less" scoped>
.blue-background.ant-switch-checked {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch {
background-color: grey !important;
}
.blue-background.ant-switch-checked .ant-switch-handle {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch .ant-switch-handle {
background-color: grey !important;
}
</style>

View File

@@ -0,0 +1,421 @@
<template>
<ns-drawer
v-model:visible="visible"
width="520"
:title="infoObject?.id ? '编辑告警规则' : '新增告警规则'"
:footer-style="{ textAlign: 'right' }"
:ok="btnClick"
:cancel="handleClose"
placement="right">
<div style="padding: 18px; width: 100%; overflow: hidden">
<a-form ref="formRef" :model="infoObject" :rules="rules">
<a-form-item ref="site" label="站点" name="site">
<a-tree-select
v-model:value="infoObject.site"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择站点"
allow-clear
tree-default-expand-all
:tree-data="zdTreeData"
tree-node-filter-prop="label">
<!-- 特殊处理情况 -->
<!-- <template #title="{ value: val, label }">
<b v-if="val === 'parent 1-1'" style="color: #08c">sss</b>
<template v-else>{{ label }}</template>
</template> -->
</a-tree-select>
</a-form-item>
<a-form-item label="数据来源" name="sbtype">
<a-cascader
v-model:value="infoObject.sbtype"
:options="sbOptions"
:show-search="{ filter }"
placeholder="请选择数据来源" />
</a-form-item>
<a-form-item label="设备节点" name="jdDevice">
<a-tree-select
v-model:value="infoObject.jdDevice"
style="width: 100%"
placeholder="请选择设备节点"
:tree-line="true && { showLeafIcon: false }"
:tree-data="jdTreeData" />
</a-form-item>
<a-form-item label="启用规则" name="delivery">
<a-switch
v-model:checked="infoObject.delivery"
:class="{
'blue-background': infoObject.delivery,
'grey-background': !infoObject.delivery,
}" />
</a-form-item>
<a-form-item label="异常描述" name="desc">
<a-textarea
v-model:value="infoObject.desc"
style="height: 32px"
:autoSize="{ minRows: 1, maxRows: 1 }"
show-count
:maxlength="30" />
</a-form-item>
<a-form-item label="规则类型" name="resource">
<a-radio-group v-model:value="infoObject.resource">
<a-radio value="1"> (and) </a-radio>
<a-radio value="2"> (or) </a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="对比类型1" name="qzType">
<a-cascader
v-model:value="infoObject.sbtype"
:options="sbOptions"
placeholder="请选择数据来源" />
</a-form-item>
<template v-for="index in infoObject.alarmList?.length" :key="index">
<div style="width: 100%; display: flex; margin-left: 42px; padding: 12px">
<span style="line-height: 32px">{{ `逻辑${index}:` }}</span>
<a-select
v-model:value="infoObject.alarmList[index - 1].logic"
style="width: 70px; margin-left: 12px"
:options="ljOptions"
@change="handleQzChange" />
<span style="line-height: 32px; margin-left: 32px">{{ `数值${index}:` }}</span>
<a-input
style="width: 65px; margin-left: 6px"
type="number"
v-model:value="infoObject.alarmList[index - 1].num" />
<div
style="width: 70px; align-items: center; cursor: pointer"
@click="deleteAlarmList(index - 1)">
<img
style="width: 14px; margin: 0 12px"
src="https://files.axshare.com/gsc/4T0UQR/5a/e6/81/5ae6813d499c422383c7a15dd956523f/images/设备规则/u72.svg?pageId=cbce6e61-bc6a-4283-802d-993fce6151c0" />
</div>
</div>
<div
v-if="
infoObject.alarmList[index - 1].num === null ||
infoObject.alarmList[index - 1].logic === null
"
style="width: 100%; color: #ff4d4f; text-align: center; margin-bottom: 5px">
请选择正确的逻辑{{ index }} 或 输入正确的数值{{ index }}
</div>
</template>
<div
v-if="infoObject.alarmList?.length < 2"
style="width: 100%; color: #ff4d4f; text-align: center; margin-bottom: 5px">
逻辑至少2条
</div>
<div style="width: 100%; margin-top: 12px; display: flex; justify-content: flex-end">
<a-button type="primary" @click="addAlarmList"> 新增</a-button>
</div>
</a-form>
</div>
</ns-drawer>
</template>
<script lang="ts" setup>
import { NsMessage } from '/nerv-lib/component';
import { ref, toRaw } from 'vue';
import type { CascaderProps, TreeSelectProps, SelectProps } from 'ant-design-vue';
import type { ShowSearchType } from 'ant-design-vue/es/cascader';
import { device } from '/@/api/deviceManage';
import { http } from '/nerv-lib/util';
import type { Rule } from 'ant-design-vue/es/form';
const visible = ref(false);
//表单数据
const infoObject = ref({
id: null,
site: null,
jdDevice: null,
resource: null,
desc: null,
sbtype: null,
delivery: null,
alarmList: [{ logic: null, num: null }],
});
//删除的逻辑列表
const delAlarmList = ref([]);
const formRef = ref();
//站点数
const zdTreeData = ref<TreeSelectProps['treeData']>([
{
label: '铁路总局(T01)',
value: 'T01',
children: [
{
label: '济阳站(T0101)',
value: 'T0101',
},
],
},
]);
//设备类型树
const sbOptions: CascaderProps['options'] = [
{
value: '3',
label: '3.电梯',
children: [
{
value: '301',
label: '301.扶梯',
},
{
value: '302',
label: '301.直梯',
},
],
},
{
value: '4',
label: '4.冷热源',
children: [
{
value: '401',
label: '402.冷水机组',
},
{
value: '402',
label: '403.热泵机组',
},
{
value: '403',
label: '403.锅炉',
},
{
value: '404',
label: '404.水处理机组',
},
{
value: '405',
label: '405.板式热交换机组',
},
],
},
];
//节点数据
const jdTreeData = ref([
{
title: 'parent 1',
value: 'parent 1',
children: [
{
title: 'parent 1-0',
value: 'parent 1-0',
children: [
{
title: 'my leaf',
value: 'leaf1',
},
{
title: 'your leaf',
value: 'leaf2',
},
],
},
{
title: 'parent 1-1',
value: 'parent 1-1',
},
],
},
]);
//设备点位
const dwOptions = ref<SelectProps['options']>([
{ value: '电压 (U)', label: '电压 (U)', code: '1' },
{ value: '电流 (I)', label: '电流 (I)', code: '2' },
{ value: '电流 (I)', label: 'a相电流 (Ia)', code: '3' },
]);
const filter: ShowSearchType['filter'] = (inputValue: any, path: any) => {
return path.some(
(option: any) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1,
);
};
//取值类型
const qzOptions = ref<SelectProps['options']>([
{ value: '实时值', label: '实时值', code: '1' },
{ value: '平均值', label: '平均值', code: '2' },
]);
//逻辑
const ljOptions = ref<SelectProps['options']>([
{ value: '1', label: '≥' },
{ value: '2', label: '>' },
{ value: '3', label: '≤' },
{ value: '4', label: '<' },
{ value: '5', label: '=' },
]);
//设备点位方法
const filterOption = (input: string, option: any) => {
console.log('搜索', option.value);
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const handleFocus = () => {
console.log('点击');
};
const handleChange = (value: string) => {
console.log(`selected ${value}`);
console.log('选择', infoObject.value.sbAdress);
//获得code
dwOptions.value.forEach((item) => {
if (item.value === value) {
console.log('符合', item.code);
}
});
};
const handleQzChange = (value: string) => {
//获得code
qzOptions.value.forEach((item) => {
if (item.value === value) {
console.log('符合', item.code);
}
});
};
const emit = defineEmits(['editObject']);
//父调子 页面显示方法
const toggle = (value: any) => {
//获取设备类型
http.post(device.queryDeviceTree, null).then((res) => {
console.log(res, '请求出来的数据');
NsMessage.success('操作成功');
});
//判断 是新增 还是修改
if (value) {
infoObject.value = value;
} else {
infoObject.value = {
id: null,
site: null,
jdDevice: null,
resource: null,
desc: null,
sbtype: null,
delivery: null,
alarmList: [{ logic: null, num: null }],
};
}
visible.value = !visible.value;
};
//表单 判断规格
const rules: Record<string, Rule[]> = {
site: [{ required: true, message: '请选择站点', trigger: 'change' }],
sbtype: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
delivery: [{ required: true, message: '请选择启用规则', trigger: 'change' }],
jdDevice: [{ required: true, message: '请选择设备名称', trigger: 'change' }],
sbAdress: [{ required: true, message: '请选择设备点位', trigger: 'change' }],
qzType: [{ required: true, message: '请选择取值类型', trigger: 'change' }],
resource: [{ required: true, message: '请选择规则类型', trigger: 'change' }],
desc: [{ required: true, message: '请输入异常描述', trigger: 'blur' }],
alarm: [{ required: true, message: '请选择逻辑', trigger: 'blur' }],
number: [{ required: true, message: '请输入数值', trigger: 'blur' }],
};
// 开关选择
const changeSwitch = () => {
console.log(infoObject.value.selectSwitch, '开关');
};
// 确认按钮
const btnClick = () => {
console.log(infoObject.value, '数据');
infoObject.value.alarmList.forEach((item) => {
if (item.logic === null || item.num === null) {
// NsMessage.error('请选择逻辑和数值');
return;
}
});
if (infoObject.value.alarmList.length < 2) {
NsMessage.error('请选择逻辑和数值');
return;
}
//数据是否验证通过
formRef.value.validate().then(() => {
console.log('values', infoObject, toRaw(infoObject));
delAlarmList.value = [];
});
//调用接口
// http
// .post(props.api, data)
// .then(() => {
// isLoading.value = false;
// NsMessage.success('操作成功', 1, () => {
// navigateBack();
// });
// })
// .catch(() => {
// isLoading.value = false;
// });
};
//取消按钮
const handleClose = () => {
// 清楚校验错误信息
formRef.value.resetFields();
//对象清空
infoObject.value = {
id: null,
site: null,
resource: null,
desc: null,
sbtype: null,
delivery: null,
alarmList: [{ alarm: null, number: null }],
};
visible.value = false;
delAlarmList.value = [];
};
// 新增逻辑列表
const addAlarmList = () => {
if (infoObject.value.alarmList) {
infoObject.value.alarmList.push({ logic: null, num: null });
} else {
infoObject.value.alarmList = [{ logic: null, num: null }];
}
};
// 删除 逻辑列表、
const deleteAlarmList = (index: number) => {
if (infoObject.value.alarmList[index]?.id) {
delAlarmList.value.push(infoObject.value.alarmList[index]);
}
infoObject.value.alarmList.splice(index, 1);
};
defineExpose({
toggle,
handleClose,
dwOptions,
formRef,
});
</script>
<style scoped lang="less">
.drawerContainer {
height: 100%;
display: flex;
justify-content: space-between;
}
.blue-background.ant-switch-checked {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch {
background-color: grey !important;
}
.blue-background.ant-switch-checked .ant-switch-handle {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch .ant-switch-handle {
background-color: grey !important;
}
/deep/ .ant-form-item-label {
z-index: 20;
text-align: right;
width: 23%;
}
</style>

View File

@@ -0,0 +1,235 @@
<template>
<ns-drawer
v-model:visible="visible"
width="520"
:title="infoObject?.id ? '修改能源告警' : '新增能源告警'"
:footer-style="{ textAlign: 'right' }"
:ok="btnClick"
:cancel="handleClose"
placement="right">
<ns-form ref="formRef" :schemas="schemas" :model="infoObject" formLayout="vertical" />
<div style="margin-left: 52px">
应用规则:
<a-switch
:checked="infoObject?.enableRules === 1 ? true : false"
:class="{
'blue-background': infoObject?.enableRules === 1 ? true : false,
'grey-background': infoObject?.enableRules === 1 ? false : true,
}"
style="margin-left: 6px"
@change="changeSwitch" />
</div>
</ns-drawer>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { NsMessage } from '/nerv-lib/component';
import { http } from '/nerv-lib/util';
// import { deviceAlarms } from '/@/api/alarmSettings/deviceAlarms';
const visible = ref(false);
//表单数据
const infoObject = ref({
id: null,
accountNo: null,
enableRules: 0,
});
const formRef = ref();
const emit = defineEmits(['editObject']);
const toggle = (value: any) => {
//判断 是新增 还是修改
if (value) {
infoObject.value = value;
} else {
infoObject.value = {
id: null,
accountNo: null,
enableRules: 0,
};
}
visible.value = !visible.value;
};
const schemas = [
{
field: 'basicInfo',
label: '',
displayFormItem: false,
class: 'ns-form-item-full',
component: 'NsChildForm',
componentProps: {
schemas: [
{
field: 'alarmTitle',
label: '告警标题',
component: 'NsInput',
rules: [
{
required: true,
message: '告警标题不能为空',
trigger: 'change',
},
],
componentProps: {
placeholder: '请输入告警标题',
maxLength: 20,
},
},
{
field: 'repetitions',
label: '重复次数',
rules: [
{
required: true,
message: '重复次数不能为空',
trigger: 'change',
},
],
component: 'NsSelect',
componentProps: {
allowClear: true,
placeholder: '请选择重复次数',
options: [
{
label: '单次',
value: 1,
},
{
label: '重复',
value: 2,
},
{
label: '累计',
value: 3,
},
],
},
},
{
field: 'priority',
label: '优先级',
component: 'NsSelect',
rules: [
{
required: true,
message: '优先级不能为空',
trigger: 'change',
},
],
componentProps: {
allowClear: true,
placeholder: '请选择优先级',
options: [
{
label: '紧急',
value: 1,
},
{
label: '重要',
value: 2,
},
{
label: '一般',
value: 3,
},
],
},
},
{
field: 'priority',
label: '监测频率',
component: 'NsSelect',
rules: [
{
required: true,
message: '监测频率不能为空',
trigger: 'change',
},
],
componentProps: {
allowClear: true,
placeholder: '请选择监测频率',
componentProps: {
api: '/api/community/objs/DictItem',
params: {
pageSize: 100,
code: 'MZ',
},
placeholder: '请选择设备点位',
resultField: 'data',
labelField: 'dictName',
valueField: 'dictValue',
immediate: true,
autoSelectFirst: false,
},
},
},
],
},
},
];
//开关
const changeSwitch = () => {
switch (infoObject.value.enableRules) {
case 1:
infoObject.value.enableRules = 0;
break;
case 0:
infoObject.value.enableRules = 1;
break;
}
};
const btnClick = () => {
//表单校验
formRef.value.triggerSubmit().then(() => {
//调用接口
// http.post(deviceAlarms.addOrUpNewData, infoObject.value).then(() => {
// NsMessage.success('操作成功');
// visible.value = false;
// emit('editObject', null);
// });
});
};
const handleClose = () => {
// 清楚校验错误信息
formRef.value.formElRef.clearValidate();
console.log(infoObject.value);
visible.value = false;
NsMessage.success('操作成功');
};
defineExpose({
toggle,
handleClose,
formRef,
});
</script>
<style scoped lang="less">
.drawerContainer {
height: 100%;
display: flex;
justify-content: space-between;
}
.blue-background.ant-switch-checked {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch {
background-color: grey !important;
}
.blue-background.ant-switch-checked .ant-switch-handle {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch .ant-switch-handle {
background-color: grey !important;
}
</style>

View File

@@ -1,13 +1,36 @@
<!-- 配置设备告警 -->
<template>
<ns-view-list-table v-if="show" class="table" v-bind="tableConfig" />
<ns-view-list-table v-if="show" ref="mainRef" class="table" v-bind="tableConfig">
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'enableRules'">
<a-switch
:checked="record.enableRules === 1 ? true : false"
:class="{
'blue-background': record.enableRules === 1 ? true : false,
'grey-background': record.enableRules === 1 ? false : true,
}"
@click="clickSwitch({ enableRules: record.enableRules, record: record })" />
</template>
<template v-if="column.dataIndex === 'equipmentInfo'">
{{
record?.enableRules
? record.enableRules + '>' + record.deviceType + '>' + record.deviceName
: '-'
}}
</template>
</template>
</ns-view-list-table>
<!-- 新增or编辑界面 -->
<editConfigureDeviceAlarm ref="editConfigureDeviceAlarms" />
</template>
<script lang="ts">
import { ref } from 'vue';
import { ref, createVNode } from 'vue';
import { http } from '/nerv-lib/util';
import data from '../notificationManagementMock.json';
import { NsMessage, NsModal } from '/nerv-lib/component';
import editConfigureDeviceAlarm from '../equipmentAlarm/editConfigureDeviceAlarm.vue';
import { deviceAlarms } from '/@/api/alarmSettings/deviceAlarms';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
export default {
components: { editConfigureDeviceAlarm },
@@ -17,9 +40,28 @@
const configureDeviceAlarmsData = ref({});
const show = ref(false);
const tableConfig = ref({});
const mainRef = ref({});
const editConfigureDeviceAlarms = ref({});
const mockData = ref(data.listData);
const doWnload = (url) => {
const clickSwitch = (data: any) => {
NsModal.confirm({
title: '启用状态',
icon: createVNode(ExclamationCircleOutlined),
content: '确定' + (data.record.enableRules === 1 ? '关闭' : '启用') + '?',
onOk: () => {
http
.post(deviceAlarms.addOrUpNewData, {
id: data.record.id,
enableRules: data.record.enableRules === 1 ? 0 : 1,
})
.then(() => {
NsMessage.success('操作成功');
mainRef.value?.nsTableRef.reload();
});
},
});
};
const doWnload = (url: any) => {
const a = document.createElement('a');
document.body.appendChild(a);
a.href = encodeURI(url);
@@ -33,7 +75,7 @@
show.value = true;
tableConfig.value = {
title: '告警规则',
// api: '/carbon_emission/device/getDeviceList',
api: deviceAlarms.getTableList,
value: mockData.value,
headerActions: [
{
@@ -41,7 +83,6 @@
name: 'RoleTypeAdd',
type: 'primary',
handle: () => {
console.log('新增xxxxxxx');
editConfigureDeviceAlarms.value.toggle();
},
},
@@ -94,38 +135,34 @@
},
{
title: '规则id',
dataIndex: 'id',
dataIndex: 'ruleId',
},
{
title: '设备信息',
dataIndex: 'deviceCode',
dataIndex: 'equipmentInfo',
},
{
title: '告警点位',
dataIndex: 'deviceName',
dataIndex: 'devicePoint',
},
{
title: '判断条件',
dataIndex: 'position',
dataIndex: 'ruleType',
},
{
title: '取值类型',
dataIndex: 'position',
dataIndex: 'valueType',
textEllipsis: true,
},
{
title: '异常描述',
dataIndex: 'position',
dataIndex: 'abnormalDescription',
},
{
title: '启用通知',
dataIndex: 'isUse',
dataIndex: 'enableRules',
},
],
params: {
page: 0,
pageSize: 10,
},
// rowSelection: null, 选择按钮
columnActions: {
title: '操作',
@@ -151,7 +188,7 @@
],
},
formConfig: {
title: value.position,
title: value.errorCode,
schemas: [
{
field: 'provider',
@@ -164,19 +201,19 @@
{
field: 'provider',
label: '设备点位',
component: 'NsSelect',
component: 'nsSelectApi',
componentProps: {
placeholder: '请选择告警优先级',
options: [
{
label: '电流',
value: '1',
},
{
label: '电压',
value: '0',
},
],
api: '/api/community/objs/DictItem',
params: {
pageSize: 100,
code: 'MZ',
},
placeholder: '请选择设备点位',
resultField: 'data',
labelField: 'dictName',
valueField: 'dictValue',
immediate: true,
autoSelectFirst: false,
},
},
{
@@ -207,6 +244,7 @@
},
],
},
params: { id: value.id },
// pagination: { pageSizeOptions: false },
rowKey: 'id',
};
@@ -214,6 +252,7 @@
return {
configureDeviceAlarmsData,
show,
clickSwitch,
doWnload,
tableConfig,
editConfigureDeviceAlarms,
@@ -222,3 +261,28 @@
},
};
</script>
<style lang="less" scoped>
.blue-background.ant-switch-checked {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch {
background-color: grey !important;
}
.blue-background.ant-switch-checked .ant-switch-handle {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch .ant-switch-handle {
background-color: grey !important;
}
</style>

View File

@@ -3,14 +3,15 @@
v-model:visible="visible"
width="520"
:title="infoObject?.id ? '编辑告警规则' : '新增告警规则'"
:footer-style="{ textAlign: 'right' }"
:ok="btnClick"
:cancel="handleClose"
placement="right">
<div style="padding: 18px; width: 100%; overflow: hidden">
<a-form ref="formRef" :model="infoObject" :rules="rules">
<a-form-item ref="name" label="站点" name="name">
<a-form-item ref="site" label="站点" name="site">
<a-tree-select
v-model:value="infoObject.name"
v-model:value="infoObject.site"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
@@ -18,70 +19,77 @@
allow-clear
tree-default-expand-all
:tree-data="zdTreeData"
tree-node-filter-prop="label">
<!-- 特殊处理情况 -->
<!-- <template #title="{ value: val, label }">
<b v-if="val === 'parent 1-1'" style="color: #08c">sss</b>
<template v-else>{{ label }}</template>
</template> -->
</a-tree-select>
tree-node-filter-prop="label" />
</a-form-item>
<a-form-item label="设备类型" name="sbtype">
<a-form-item label="设备类型" name="deviceType">
<a-cascader
v-model:value="infoObject.sbtype"
:options="sbOptions"
v-model:value="infoObject.deviceType"
:options="deviceTypeTreeData"
:field-names="{
children: 'children',
label: 'deviceType',
value: 'id',
}"
:show-search="{ filter }"
@change="selectDeviceType"
placeholder="请选择设备类型" />
</a-form-item>
<a-form-item label="设备名称" name="sbName">
<a-form-item label="设备名称" name="deviceName">
<a-select
v-model:value="infoObject.sbName"
v-model:value="infoObject.deviceName"
:disabled="!(infoObject && infoObject.deviceType && infoObject.deviceType.length > 0)"
style="width: 100%"
:autoClearSearchValue="true"
@change="selectDevice"
placeholder="请选择设备名称">
<a-select-option :key="1" :value="1"> AC001电表 </a-select-option>
<a-select-option :key="2" :value="2"> AC002电表 </a-select-option>
<template v-for="(item, index) in deviceNameTreeData" :key="index">
<a-select-option :value="item.id">
{{ item.deviceName }}
</a-select-option>
</template>
</a-select>
</a-form-item>
<a-form-item label="设备点位" name="sbAdress">
<a-form-item label="设备点位" name="devicePoint">
<a-select
v-model:value="infoObject.sbAdress"
v-model:value="infoObject.devicePoint"
show-search
placeholder="请选择设备点位"
:disabled="!infoObject?.deviceName"
style="width: 100%"
:options="dwOptions"
:filter-option="filterOption"
@focus="handleFocus"
@change="handleChange" />
</a-form-item>
<a-form-item label="启用规则" name="delivery">
<a-form-item label="启用规则" name="enableRules">
<a-switch
v-model:checked="infoObject.delivery"
:checked="infoObject.enableRules === 1 ? true : false"
:class="{
'blue-background': infoObject.delivery,
'grey-background': !infoObject.delivery,
}" />
'blue-background': infoObject.enableRules === 1 ? true : false,
'grey-background': infoObject.enableRules === 1 ? false : true,
}"
@click="clickSwitch" />
</a-form-item>
<a-form-item label="取值类型" name="qzType">
<a-form-item label="取值类型" name="valueType">
<a-select
v-model:value="infoObject.qzType"
v-model:value="infoObject.valueType"
show-search
placeholder="请选择设备点位"
placeholder="请选择取值类型"
style="width: 100%"
:options="qzOptions"
:filter-option="filterOption"
@change="handleQzChange" />
</a-form-item>
<a-form-item label="异常描述" name="desc">
<a-form-item label="异常描述" name="abnormalDescription">
<a-textarea
v-model:value="infoObject.desc"
v-model:value="infoObject.abnormalDescription"
style="height: 32px"
:autoSize="{ minRows: 1, maxRows: 1 }"
show-count
:maxlength="30" />
</a-form-item>
<a-form-item label="规则类型" name="resource">
<a-radio-group v-model:value="infoObject.resource">
<a-form-item label="规则类型" name="ruleType">
<a-radio-group v-model:value="infoObject.ruleType">
<a-radio value="1"> (and) </a-radio>
<a-radio value="2"> (or) </a-radio>
</a-radio-group>
@@ -97,7 +105,7 @@
">
<span style="line-height: 32px">{{ `逻辑${index}:` }}</span>
<a-select
v-model:value="infoObject.alarmList[index - 1].alarm"
v-model:value="infoObject.alarmList[index - 1].logic"
style="width: 70px; margin-left: 12px"
:options="ljOptions"
@change="handleQzChange" />
@@ -105,7 +113,7 @@
<a-input
style="width: 65px; margin-left: 6px"
type="number"
v-model:value="infoObject.alarmList[index - 1].number" />
v-model:value="infoObject.alarmList[index - 1].num" />
<div
style="width: 70px; align-items: center; cursor: pointer"
@click="deleteAlarmList(index - 1)">
@@ -114,7 +122,20 @@
src="https://files.axshare.com/gsc/4T0UQR/5a/e6/81/5ae6813d499c422383c7a15dd956523f/images/设备规则/u72.svg?pageId=cbce6e61-bc6a-4283-802d-993fce6151c0" />
</div>
</div>
<div
v-if="
infoObject.alarmList[index - 1].num === null ||
infoObject.alarmList[index - 1].logic === null
"
style="width: 100%; color: #ff4d4f; text-align: center; margin-bottom: 5px">
请选择正确的逻辑{{ index }} 或 输入正确的数值{{ index }}
</div>
</template>
<div
v-if="infoObject?.alarmList?.length < 2"
style="width: 100%; color: #ff4d4f; text-align: center; margin-bottom: 5px">
逻辑至少2条
</div>
<div style="width: 100%; margin-top: 12px; display: flex; justify-content: flex-end">
<a-button type="primary" @click="addAlarmList"> 新增</a-button>
</div>
@@ -124,26 +145,36 @@
</template>
<script lang="ts" setup>
import { NsMessage } from '/nerv-lib/component';
import { ref, toRaw } from 'vue';
import { ref, toRaw, watch } from 'vue';
import type { CascaderProps, TreeSelectProps, SelectProps } from 'ant-design-vue';
import type { ShowSearchType } from 'ant-design-vue/es/cascader';
import { device } from '/@/api/deviceManage';
import { http } from '/nerv-lib/util';
import type { Rule } from 'ant-design-vue/es/form';
import { async } from '@antv/x6/lib/registry/marker/async';
const visible = ref(false);
//表单数据
const infoObject = ref({
id: null,
name: null,
resource: null,
desc: null,
sbtype: null,
delivery: null,
alarmList: [{ alarm: null, number: null }],
site: null,
ruleType: null,
abnormalDescription: null,
deviceType: [],
devicePoint: null,
valueType: null,
deviceName: null,
enableRules: 0,
alarmList: [{ id: null, alarm: null, number: null, isDelete: '0' }],
});
//删除的逻辑列表
const delAlarmList = ref([]);
const formRef = ref();
//组织数
const orgId = ref('');
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
orgId.value = result;
//站点数
const zdTreeData = ref<TreeSelectProps['treeData']>([
{
@@ -158,48 +189,39 @@
},
]);
//设备类型树
const sbOptions: CascaderProps['options'] = [
{
value: '3',
label: '3.电梯',
children: [
{
value: '301',
label: '301.扶梯',
},
{
value: '302',
label: '301.直梯',
},
],
},
{
value: '4',
label: '4.冷热源',
children: [
{
value: '401',
label: '402.冷水机组',
},
{
value: '402',
label: '403.热泵机组',
},
{
value: '403',
label: '403.锅炉',
},
{
value: '404',
label: '404.水处理机组',
},
{
value: '405',
label: '405.板式热交换机组',
},
],
},
];
let deviceTypeTreeData = ref([]);
//设备树
let deviceNameTreeData = ref([]);
//选择设备类型方法
const selectDeviceType = (value: any, selectedOptions: any) => {
//获取该类型设备
getDevicePage({
orgId: orgId.value,
code: selectedOptions[selectedOptions.length - 1].code,
pageNum: 1,
pageSize: 999,
});
};
//选择设备方法
const selectDevice = (value: string) => {
console.log(`selected ${value}`);
};
//获取设备列表
const getDevicePage = (value: any) => {
http.post(device.queryDevicePage, value).then((res) => {
if (res.msg === 'success') {
deviceNameTreeData.value = res.data.records;
}
});
};
//获取设备点位
const getDevicePoint = (value: any) => {
http.post(device.queryDevicePoint, value).then((res) => {
if (res.msg === 'success') {
console.log(res, '数据');
}
});
};
//设备点位
const dwOptions = ref<SelectProps['options']>([
{ value: '电压 (U)', label: '电压 (U)', code: '1' },
@@ -234,7 +256,7 @@
};
const handleChange = (value: string) => {
console.log(`selected ${value}`);
console.log('选择', infoObject.value.sbAdress);
console.log('选择', infoObject.value.devicePoint);
//获得code
dwOptions.value.forEach((item) => {
if (item.value === value) {
@@ -250,36 +272,95 @@
}
});
};
//开关
const clickSwitch = () => {
if (infoObject.value.enableRules === 1) {
infoObject.value.enableRules = 0;
} else {
infoObject.value.enableRules = 1;
}
};
const emit = defineEmits(['editObject']);
// 定义一个递归函数来查找每一级的id 设备类型回显 层级方法
function findParentIds(tree: any, targetId: number, result: any) {
for (let item of tree) {
if (item.children && item.children.length > 0) {
if (item.children.some((child: any) => child.id === targetId)) {
result.unshift(item.id); // 将当前节点的id添加到结果数组的最前面
findParentIds(tree, item.id, result); // 递归查找父级节点的id
break; // 找到后可以退出循环
}
}
}
}
// 递归 获取当前 选择的设备类型 对象 用来获取设备列表
const findNodeById = (nodes: any, id: any) => {
for (let node of nodes) {
if (node.id === id) {
//获取设备树
getDevicePage({ orgId: orgId.value, code: node.code, pageNum: 1, pageSize: 99 });
return;
} else if (node.children.length > 0) {
const found = findNodeById(node.children, id);
if (found) {
//获取设备树
getDevicePage({ orgId: orgId.value, code: found.code, pageNum: 1, pageSize: 99 });
return;
}
}
}
return null; // 没有找到目标节点,返回 null
};
//父调子 页面显示方法
const toggle = (value: any) => {
const toggle = async (value: any) => {
//获取设备类型
await http
.post(device.queryDeviceTree, { orgId: orgId.value, pageNum: 1, pageSize: 10 })
.then((res) => {
if (res.msg === 'success') {
deviceTypeTreeData.value = res.data;
}
});
//判断 是新增 还是修改
if (value) {
infoObject.value = value;
// 获取 选择的设备类型对象
let selectDevice = ref([value.deviceType]);
findNodeById(deviceTypeTreeData.value, value.deviceType);
//获取设备点位
getDevicePoint({ id: 1 });
// 修改时
findParentIds(deviceTypeTreeData.value, value.deviceType, selectDevice.value);
infoObject.value.deviceType = selectDevice.value;
} else {
//获取设备树
infoObject.value = {
id: null,
name: null,
resource: null,
desc: null,
sbtype: null,
delivery: null,
alarmList: [{ alarm: null, number: null }],
site: null,
ruleType: null,
abnormalDescription: null,
deviceType: [],
devicePoint: null,
valueType: null,
deviceName: null,
enableRules: 0,
alarmList: [{ id: null, alarm: null, number: null, isDelete: '0' }],
};
}
visible.value = !visible.value;
};
//表单 判断规格
const rules: Record<string, Rule[]> = {
name: [{ required: true, message: '请选择站点', trigger: 'change' }],
sbtype: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
delivery: [{ required: true, message: '请选择启用规则', trigger: 'change' }],
sbName: [{ required: true, message: '请选择设备名称', trigger: 'change' }],
sbAdress: [{ required: true, message: '请选择设备点位', trigger: 'change' }],
qzType: [{ required: true, message: '请选择取值类型', trigger: 'change' }],
resource: [{ required: true, message: '请选择规则类型', trigger: 'change' }],
desc: [{ required: true, message: '请输入异常描述', trigger: 'blur' }],
const rules: infoObject<string, Rule[]> = {
site: [{ required: true, message: '请选择站点', trigger: 'change' }],
deviceType: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
enableRules: [{ required: true, message: '请选择启用规则', trigger: 'change' }],
deviceName: [{ required: true, message: '请选择设备名称', trigger: 'change' }],
devicePoint: [{ required: true, message: '请选择设备点位', trigger: 'change' }],
valueType: [{ required: true, message: '请选择取值类型', trigger: 'change' }],
ruleType: [{ required: true, message: '请选择规则类型', trigger: 'change' }],
abnormalDescription: [{ required: true, message: '请输入异常描述', trigger: 'blur' }],
alarm: [{ required: true, message: '请选择逻辑', trigger: 'blur' }],
number: [{ required: true, message: '请输入数值', trigger: 'blur' }],
};
@@ -290,11 +371,24 @@
// 确认按钮
const btnClick = () => {
console.log(infoObject.value, '数据');
console.log(dwOptions.value, '点位');
dwOptions.value?.forEach;
infoObject.value.alarmList.forEach((item) => {
if (item.logic === null || item.num === null) {
// NsMessage.error('请选择逻辑和数值');
return;
}
});
if (infoObject.value.alarmList.length < 2) {
NsMessage.error('请选择逻辑和数值');
return;
}
//数据是否验证通过
formRef.value.validate().then(() => {
console.log('values', infoObject, toRaw(infoObject));
console.log('values', infoObject.value);
let data = toRaw(infoObject);
//逻辑列表
data.alarmList = [...infoObject.value.alarmList, ...delAlarmList.value];
//设备类型 只取最后一级的id
data.deviceType = infoObject.value.deviceType[infoObject.value.deviceType.length - 1];
delAlarmList.value = [];
});
//调用接口
@@ -317,31 +411,39 @@
//对象清空
infoObject.value = {
id: null,
name: null,
resource: null,
desc: null,
sbtype: null,
delivery: null,
alarmList: [{ alarm: null, number: null }],
site: null,
ruleType: null,
abnormalDescription: null,
deviceType: [],
devicePoint: null,
enableRules: 0,
alarmList: [{ id: null, alarm: null, number: null, isDelete: '0' }],
};
visible.value = false;
delAlarmList.value = [];
NsMessage.success('操作成功');
};
// 新增逻辑列表
const addAlarmList = () => {
if (infoObject.value.alarmList) {
infoObject.value.alarmList.push({ alarm: null, number: null });
infoObject.value.alarmList.push({ logic: null, num: null });
} else {
infoObject.value.alarmList = [{ alarm: null, number: null }];
infoObject.value.alarmList = [{ logic: null, num: null }];
}
};
// 删除 逻辑列表、
const deleteAlarmList = (index: number) => {
if (infoObject.value.alarmList[index]?.id) {
delAlarmList.value.push(infoObject.value.alarmList[index]);
// 确保 index 在有效范围内
if (index < infoObject.value.alarmList.length && index >= 0) {
const alarmItemToDelete = infoObject.value.alarmList[index];
if (alarmItemToDelete?.id) {
// 添加到 delAlarmList 中,并标记为已删除
delAlarmList.value.push({ ...alarmItemToDelete, isDelete: '1' });
}
// 从 infoObject 中删除该元素
infoObject.value.alarmList.splice(index, 1);
} else {
console.error('Invalid index: ', index);
}
infoObject.value.alarmList.splice(index, 1);
};
defineExpose({
toggle,

View File

@@ -2,7 +2,8 @@
<ns-drawer
v-model:visible="visible"
width="520"
:title="infoObject?.id ? '修改告警功能' : '新增告警'"
:title="infoObject?.id ? '修改设备告警' : '新增设备告警'"
:footer-style="{ textAlign: 'right' }"
:ok="btnClick"
:cancel="handleClose"
placement="right">
@@ -10,10 +11,10 @@
<div style="margin-left: 52px">
应用规则:
<a-switch
v-model:checked="infoObject.isUse"
:checked="infoObject?.enableRules === 1 ? true : false"
:class="{
'blue-background': infoObject.isUse,
'grey-background': !infoObject.isUse,
'blue-background': infoObject?.enableRules === 1 ? true : false,
'grey-background': infoObject?.enableRules === 1 ? false : true,
}"
style="margin-left: 6px"
@change="changeSwitch" />
@@ -23,23 +24,29 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { NsMessage } from '/nerv-lib/component';
import { http } from '/nerv-lib/util';
import { deviceAlarms } from '/@/api/alarmSettings/deviceAlarms';
const visible = ref(false);
//表单数据
const infoObject = ref({});
const infoObject = ref({
id: null,
accountNo: null,
enableRules: 0,
});
const formRef = ref();
const emit = defineEmits(['editObject']);
const toggle = (value) => {
const toggle = (value: any) => {
//判断 是新增 还是修改
if (value) {
infoObject.value = value;
} else {
infoObject.value = {
id: null,
accountNo: null,
enableRules: 0,
};
}
console.log('xxxx');
visible.value = !visible.value;
};
const schemas = [
@@ -52,7 +59,7 @@
componentProps: {
schemas: [
{
field: 'accountNo',
field: 'alarmTitle',
label: '告警标题',
component: 'NsInput',
rules: [
@@ -68,7 +75,7 @@
},
},
{
field: 'select',
field: 'repetitions',
label: '重复次数',
rules: [
{
@@ -85,31 +92,31 @@
options: [
{
label: '单次',
value: 0,
},
{
label: '重复',
value: 1,
},
{
label: '累计',
label: '重复',
value: 2,
},
{
label: '累计',
value: 3,
},
],
},
},
{
field: 'accountNo',
field: 'monitorTime',
label: '检测时长',
component: 'NsInputNumber',
rules: [
{
required: true,
validator: (rules, value, cbfn) => {
validator: (rules: any, value: any, cbfn: any) => {
if (value && /^[0-9]*$/.test(value)) {
cbfn();
} else {
cbfn('请输入正确的测时长');
cbfn('请输入正确的测时长');
}
},
trigger: 'change',
@@ -121,7 +128,7 @@
},
},
{
field: 'danwei',
field: 'monitorTimeUnit',
label: '监测时长单位',
component: 'NsSelect',
rules: [
@@ -151,7 +158,7 @@
},
},
{
field: 'select',
field: 'priority',
label: '优先级',
component: 'NsSelect',
rules: [
@@ -184,36 +191,32 @@
},
},
];
//开关
const changeSwitch = () => {
console.log(infoObject.value.selectSwitch, '开关');
switch (infoObject.value.enableRules) {
case 1:
infoObject.value.enableRules = 0;
break;
case 0:
infoObject.value.enableRules = 1;
break;
}
};
const btnClick = () => {
//表单校验
formRef.value.triggerSubmit().then((data: any) => {
console.log('校验成功');
console.log('data', infoObject.value);
visible.value = false;
NsMessage.success('操作成功');
emit('editObject', null);
formRef.value.triggerSubmit().then(() => {
//调用接口
// http
// .post(props.api, data)
// .then(() => {
// isLoading.value = false;
// NsMessage.success('操作成功', 1, () => {
// navigateBack();
// });
// })
// .catch(() => {
// isLoading.value = false;
// });
http.post(deviceAlarms.addOrUpNewData, infoObject.value).then(() => {
NsMessage.success('操作成功');
visible.value = false;
emit('editObject', null);
});
});
};
const handleClose = () => {
// 清楚校验错误信息
formRef.value.formElRef.clearValidate();
console.log(infoObject.value);
infoObject.value = {};
visible.value = false;
NsMessage.success('操作成功');
};

View File

@@ -2,14 +2,50 @@
<div class="box">
<a-tabs default-active-key="1" @change="callback">
<a-tab-pane key="1" tab="通知管理">
<ns-view-list-table v-bind="notificationConfig" ref="mainRef" />
<ns-view-list-table v-bind="notificationConfig" ref="mainRef">
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'enableRules'">
<a-switch
:checked="record.enableRules === 1 ? true : false"
:class="{
'blue-background': record.enableRules === 1 ? true : false,
'grey-background': record.enableRules === 1 ? false : true,
}"
@change="clickSwitch({ enableRules: record.enableRules, record: record })" />
</template>
</template>
</ns-view-list-table>
<!-- 联系方式 -->
</a-tab-pane>
<a-tab-pane key="2" tab="设备告警" force-render>
<ns-view-list-table v-show="equipmentAlarm" class="table" v-bind="equipmentAlarmConfig" />
<ns-view-list-table
v-show="equipmentAlarm"
ref="mainRefEquipmentAlarm"
class="table"
v-bind="equipmentAlarmConfig">
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'enableRules'">
<a-switch
:checked="record.enableRules === 1 ? true : false"
:class="{
'blue-background': record.enableRules === 1 ? true : false,
'grey-background': record.enableRules === 1 ? false : true,
}"
@change="clickSwitch({ enableRules: record.enableRules, record: record })" />
</template>
<template v-if="column.dataIndex === 'monitor'">
{{
record?.monitorTime && record?.monitorTimeUnit
? record?.monitorTime + record?.monitorTimeUnit
: '-'
}}
</template>
</template>
</ns-view-list-table>
<a-button
v-if="!equipmentAlarm"
type="primary"
style="position: absolute; right: 130px; z-index: 99; top: 75px"
style="position: absolute; right: 130px; z-index: 99; top: 80px; height: 30px"
@click="backequipmentAlarm"
>返回</a-button
>
@@ -19,7 +55,34 @@
<configureDeviceAlarms v-show="!equipmentAlarm" ref="configureDeviceAlarms" />
</a-tab-pane>
<a-tab-pane key="3" tab="能源告警">
<energyAlarm class="full-height" />
<ns-view-list-table
v-bind="energyAlarmConfig"
v-show="energyAlarm"
ref="mainEnergyAlarmConfig"
class="table">
<template #bodyCell="{ record, column }">
<template v-if="column.dataIndex === 'enableRules'">
<a-switch
:checked="record.enableRules === 1 ? true : false"
:class="{
'blue-background': record.enableRules === 1 ? true : false,
'grey-background': record.enableRules === 1 ? false : true,
}"
@click="clickSwitch({ enableRules: record.enableRules, record: record })" />
</template>
</template>
</ns-view-list-table>
<a-button
v-if="!energyAlarm"
type="primary"
style="position: absolute; right: 130px; z-index: 99; top: 80px; height: 30px"
@click="backenergyAlarm"
>返回</a-button
>
<!-- 新增 编辑 能源告警 -->
<editeEnergyAlarm ref="editeEnergyAlarm" @editObject="editObject" />
<!-- 配置能源告警-->
<configureEnergyAlarms v-show="!energyAlarm" ref="configureEnergyAlarms" />
</a-tab-pane>
</a-tabs>
</div>
@@ -27,22 +90,47 @@
<script lang="ts">
import { notificationtableConfig } from './ts/notificationManagementConfig';
import { equipmentAlarmTableConfig } from './ts/equipmentAlarmConfig';
import { ref } from 'vue';
import energyAlarm from './components/energyAlarm.vue';
import { energyAlarmConfigs } from './ts/energyAlarmConfig';
import { ref, createVNode } from 'vue';
import { http } from '/nerv-lib/util';
import { NsMessage, NsModal } from '/nerv-lib/component';
import editeEquipmentAlarm from './equipmentAlarm/editeEquipmentAlarm.vue';
import editeEnergyAlarm from './energyAlarm/editeEnergyAlarm.vue';
import configureEnergyAlarms from './energyAlarm/configureEnergyAlarms.vue';
import configureDeviceAlarms from './equipmentAlarm/configureDeviceAlarms.vue';
import { deviceAlarms } from '/@/api/alarmSettings/deviceAlarms';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
export default {
name: 'AlarmSettings',
components: { energyAlarm, editeEquipmentAlarm, configureDeviceAlarms },
components: {
editeEquipmentAlarm,
configureDeviceAlarms,
configureEnergyAlarms,
editeEnergyAlarm,
},
setup() {
const mainRef = ref();
const mainRefEquipmentAlarm = ref();
const mainEnergyAlarmConfig = ref();
const editEquipmentAlarm = ref();
const editeEnergyAlarm = ref();
const configureDeviceAlarms = ref();
const configureEnergyAlarms = ref();
const equipmentAlarm = ref(true);
const notificationConfig = notificationtableConfig(null, null, null);
const energyAlarm = ref(true);
const notificationConfig = notificationtableConfig(null, mainEnergyAlarmConfig, null);
//能源告警配置
const energyAlarmConfig = energyAlarmConfigs(
editeEnergyAlarm,
mainRefEquipmentAlarm,
energyAlarm,
configureEnergyAlarms,
);
//设备告警配置
const equipmentAlarmConfig = equipmentAlarmTableConfig(
editEquipmentAlarm,
null,
mainRefEquipmentAlarm,
equipmentAlarm,
configureDeviceAlarms,
);
@@ -54,27 +142,52 @@
equipmentAlarm.value = !equipmentAlarm.value;
configureDeviceAlarms.value.show = false;
};
// const changeUse = () => {
// console.log(mainRef.value);
// mainRef.value?.nsTableRef.reload();
// // console.log(newList.value.formFinish, '数据');
// };
const backenergyAlarm = () => {
energyAlarm.value = !energyAlarm.value;
configureEnergyAlarms.value.show = false;
};
const clickSwitch = (data: any) => {
console.log(data.record.enableRules === 1 ? '关闭' : '启用');
NsModal.confirm({
title: '启用状态',
icon: createVNode(ExclamationCircleOutlined),
content: '确定' + (data.record.enableRules === 1 ? '关闭' : '启用') + '吗?',
onOk: () => {
http
.post(deviceAlarms.addOrUpNewData, {
id: data.record.id,
enableRules: data.record.enableRules === 1 ? 0 : 1,
})
.then(() => {
NsMessage.success('操作成功');
mainRefEquipmentAlarm.value?.nsTableRef.reload();
});
},
});
};
// 编辑或添加成功 刷新列表
const editObject = () => {
console.log('添加成功 刷新列表');
mainRef.value?.nsTableRef.reload();
mainRefEquipmentAlarm.value?.nsTableRef.reload();
// console.log(newList.value.formFinish, '数据');
};
return {
callback,
notificationConfig,
energyAlarmConfig,
equipmentAlarmConfig,
editObject,
clickSwitch,
editEquipmentAlarm,
editeEnergyAlarm,
configureDeviceAlarms,
configureEnergyAlarms,
equipmentAlarm,
energyAlarm,
backequipmentAlarm,
backenergyAlarm,
mainRef,
mainRefEquipmentAlarm,
mainEnergyAlarmConfig,
};
},
};
@@ -90,4 +203,27 @@
.full-height {
height: 100%; /* 设置高度为父容器高度 */
}
.blue-background.ant-switch-checked {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch {
background-color: grey !important;
}
.blue-background.ant-switch-checked .ant-switch-handle {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch .ant-switch-handle {
background-color: grey !important;
}
</style>

View File

@@ -43,6 +43,13 @@
"children": null,
"devicePointList": null,
"insertUser": null,
"priority": "1",
"alarmTitle": "电压异常告警",
"errorCode": "A001",
"monitorTime":"1",
"repetitions":"1",
"monitorTimeUnit": "分",
"enableRules": "1",
"isUse":true
} , {
"id": "d5",
@@ -87,6 +94,13 @@
"children": null,
"devicePointList": null,
"insertUser": null,
"priority": "1",
"alarmTitle": "电压异常告警",
"errorCode": "A001",
"monitorTime":"1",
"repetitions":"1",
"monitorTimeUnit": "分",
"enableRules": "0",
"isUse":true
}
]

View File

@@ -1,7 +1,8 @@
import { dateUtil } from '/nerv-lib/util/date-util';
import data from '../notificationManagementMock.json';
import { http } from '/nerv-lib/util';
import { Modal } from 'ant-design-vue';
import { NsMessage } from '/nerv-lib/component';
import { deviceAlarms } from '/@/api/alarmSettings/deviceAlarms';
import { ref } from 'vue';
const tableKeyMap = [
{
@@ -12,37 +13,33 @@ const tableKeyMap = [
},
},
{
title: '规则id',
dataIndex: 'id',
title: '优先级',
dataIndex: 'priority',
},
{
title: '设备信息',
dataIndex: 'deviceCode',
title: '告警标题',
dataIndex: 'alarmTitle',
},
{
title: '告警点位',
dataIndex: 'deviceName',
title: '错误码',
dataIndex: 'errorCode',
},
{
title: '判断条件',
dataIndex: 'position',
},
{
title: '取值类型',
dataIndex: 'position',
title: '重复次数',
dataIndex: 'repetitions',
textEllipsis: true,
},
{
title: '异常描述',
dataIndex: 'position',
title: '监测时长',
dataIndex: 'monitor',
},
{
title: '启用通知',
dataIndex: 'isUse',
title: '是否启用',
dataIndex: 'enableRules',
},
];
const mockData = ref(data.listData);
const doWnload = (url) => {
const doWnload = (url: any) => {
const a = document.createElement('a');
document.body.appendChild(a);
a.href = encodeURI(url);
@@ -52,10 +49,15 @@ const doWnload = (url) => {
a.click();
};
export const configureDeviceAlarms = (editEquipmentAlarm, elFormula, title) => {
export const energyAlarmConfigs = (
editeEnergyAlarm: any,
mainRefEquipmentAlarm: any,
energyAlarm: any,
configureDeviceAlarms: any,
) => {
return {
title: '告警规则',
// api: '/carbon_emission/device/getDeviceList',
api: deviceAlarms.getTableList,
value: mockData.value,
headerActions: [
{
@@ -63,7 +65,7 @@ export const configureDeviceAlarms = (editEquipmentAlarm, elFormula, title) => {
name: 'RoleTypeAdd',
type: 'primary',
handle: () => {
editEquipmentAlarm.value.toggle();
editeEnergyAlarm.value.toggle();
},
},
{
@@ -91,25 +93,8 @@ export const configureDeviceAlarms = (editEquipmentAlarm, elFormula, title) => {
doWnload('/hx-ai-intelligent/asset/file/whiteListUser.xlsx');
},
},
{
label: '批量删除',
type: 'primary',
name: 'userBatchDel',
dynamicDisabled: (data: any) => {
return data.list.length === 0;
},
confirm: true,
isReload: true,
isClearCheck: true,
// api: origanizemanage.batchDel,
dynamicParams: { userIds: 'userId[]' },
},
],
columns: tableKeyMap,
params: {
page: 0,
pageSize: 10,
},
// rowSelection: null, 选择按钮
columnActions: {
title: '操作',
@@ -119,7 +104,16 @@ export const configureDeviceAlarms = (editEquipmentAlarm, elFormula, title) => {
name: 'FeedBackDetail',
dynamicParams: ['uuid', 'appealType'],
handle: (data: any) => {
editEquipmentAlarm.value.toggle(data);
editeEnergyAlarm.value.toggle(data);
},
},
{
label: '配置',
name: 'FeedBackDetail',
dynamicParams: ['uuid', 'appealType'],
handle: (data: any) => {
energyAlarm.value = !energyAlarm.value;
configureDeviceAlarms.value.setconfigureDeviceAlarmsData(data);
},
},
{
@@ -127,39 +121,42 @@ export const configureDeviceAlarms = (editEquipmentAlarm, elFormula, title) => {
name: 'FeedBackDetail',
dynamicParams: ['uuid', 'appealType'],
confirm: true,
handle: () => {
// mockData.value.splice(0, 1);
handle: (data: any) => {
http.post(deviceAlarms.del, { id: data.id }).then(() => {
NsMessage.success('操作成功');
mainRefEquipmentAlarm.value?.nsTableRef.reload();
});
},
},
],
},
formConfig: {
title: title,
schemas: [
{
field: 'provider',
field: 'priority',
label: '告警优先级',
component: 'NsSelect',
componentProps: {
placeholder: '请选择告警优先级',
options: [
{
label: '全部',
value: '',
label: '紧急',
value: 1,
},
{
label: '启用',
value: '1',
label: '重要',
value: 2,
},
{
label: '关闭',
value: '0',
label: '一般',
value: 3,
},
],
},
},
{
field: 'provider',
field: 'alarmTitle',
label: '告警标题',
component: 'NsInput',
componentProps: {
@@ -167,31 +164,27 @@ export const configureDeviceAlarms = (editEquipmentAlarm, elFormula, title) => {
},
},
{
field: 'provider',
label: '告警标题',
field: 'errorCode',
label: '错误码',
component: 'NsInput',
componentProps: {
placeholder: '请输入告警标题关键字',
placeholder: '请输入告警错误码',
},
},
{
field: 'payWay',
field: 'enableRules',
label: '启用状态',
component: 'NsSelect',
componentProps: {
placeholder: '请选择启用状态',
options: [
{
label: '全部',
value: '',
},
{
label: '启用',
value: '1',
value: 1,
},
{
label: '关闭',
value: '0',
value: 0,
},
],
},

View File

@@ -1,7 +1,8 @@
import { dateUtil } from '/nerv-lib/util/date-util';
import data from '../notificationManagementMock.json';
import { http } from '/nerv-lib/util';
import { Modal } from 'ant-design-vue';
import { NsMessage } from '/nerv-lib/component';
import { deviceAlarms } from '/@/api/alarmSettings/deviceAlarms';
import { ref } from 'vue';
const tableKeyMap = [
{
@@ -13,36 +14,32 @@ const tableKeyMap = [
},
{
title: '优先级',
dataIndex: 'id',
},
{
title: '告警类型',
dataIndex: 'deviceCode',
dataIndex: 'priority',
},
{
title: '告警标题',
dataIndex: 'deviceName',
dataIndex: 'alarmTitle',
},
{
title: '错误码',
dataIndex: 'position',
dataIndex: 'errorCode',
},
{
title: '重复次数',
dataIndex: 'position',
dataIndex: 'repetitions',
textEllipsis: true,
},
{
title: '监测时长',
dataIndex: 'position',
dataIndex: 'monitor',
},
{
title: '启用通知',
dataIndex: 'isUse',
title: '是否启用',
dataIndex: 'enableRules',
},
];
const mockData = ref(data.listData);
const doWnload = (url) => {
const doWnload = (url: any) => {
const a = document.createElement('a');
document.body.appendChild(a);
a.href = encodeURI(url);
@@ -53,14 +50,14 @@ const doWnload = (url) => {
};
export const equipmentAlarmTableConfig = (
editEquipmentAlarm,
elFormula,
equipmentAlarm,
configureDeviceAlarms,
editEquipmentAlarm: any,
mainRefEquipmentAlarm: any,
equipmentAlarm: any,
configureDeviceAlarms: any,
) => {
return {
title: '告警规则',
// api: '/carbon_emission/device/getDeviceList',
api: deviceAlarms.getTableList,
value: mockData.value,
headerActions: [
{
@@ -98,10 +95,6 @@ export const equipmentAlarmTableConfig = (
},
],
columns: tableKeyMap,
params: {
page: 0,
pageSize: 10,
},
// rowSelection: null, 选择按钮
columnActions: {
title: '操作',
@@ -128,8 +121,11 @@ export const equipmentAlarmTableConfig = (
name: 'FeedBackDetail',
dynamicParams: ['uuid', 'appealType'],
confirm: true,
handle: () => {
// mockData.value.splice(0, 1);
handle: (data: any) => {
http.post(deviceAlarms.del, { id: data.id }).then(() => {
NsMessage.success('操作成功');
mainRefEquipmentAlarm.value?.nsTableRef.reload();
});
},
},
],
@@ -138,29 +134,29 @@ export const equipmentAlarmTableConfig = (
formConfig: {
schemas: [
{
field: 'provider',
field: 'priority',
label: '告警优先级',
component: 'NsSelect',
componentProps: {
placeholder: '请选择告警优先级',
options: [
{
label: '全部',
value: '',
label: '紧急',
value: 1,
},
{
label: '启用',
value: '1',
label: '重要',
value: 2,
},
{
label: '关闭',
value: '0',
label: '一般',
value: 3,
},
],
},
},
{
field: 'provider',
field: 'alarmTitle',
label: '告警标题',
component: 'NsInput',
componentProps: {
@@ -168,31 +164,27 @@ export const equipmentAlarmTableConfig = (
},
},
{
field: 'provider',
label: '告警标题',
field: 'errorCode',
label: '错误码',
component: 'NsInput',
componentProps: {
placeholder: '请输入告警标题关键字',
placeholder: '请输入错误码关键字',
},
},
{
field: 'payWay',
field: 'enableRules',
label: '启用状态',
component: 'NsSelect',
componentProps: {
placeholder: '请选择启用状态',
options: [
{
label: '全部',
value: '',
},
{
label: '启用',
value: '1',
value: 1,
},
{
label: '关闭',
value: '0',
value: 0,
},
],
},

View File

@@ -37,21 +37,17 @@ const tableKeyMap = [
},
{
title: '启用通知',
dataIndex: 'isUse',
dataIndex: 'enableRules',
},
];
const mockData = ref(data.listData);
export const notificationtableConfig = (el, elGroup, elFormula) => {
export const notificationtableConfig = (el) => {
return {
title: '告警规则',
// api: '/carbon_emission/device/getDeviceList',
value: mockData.value,
headerActions: [{}],
columns: tableKeyMap,
params: {
page: 0,
pageSize: 10,
},
// rowSelection: null, 选择按钮
columnActions: {
title: '操作',
@@ -64,15 +60,6 @@ export const notificationtableConfig = (el, elGroup, elFormula) => {
console.log(data, '联系方式');
},
},
// {
// label: '删除',
// name: 'FeedBackDetail',
// dynamicParams: ['uuid', 'appealType'],
// confirm: true,
// handle: () => {
// mockData.value.splice(0, 1);
// },
// },
],
},
@@ -86,12 +73,16 @@ export const notificationtableConfig = (el, elGroup, elFormula) => {
placeholder: '请选告警类型',
options: [
{
label: '启用',
label: '设备告警',
value: '1',
},
{
label: '关闭',
value: '0',
label: '网关告警',
value: '2',
},
{
label: '能耗告警',
value: '3',
},
],
},
@@ -104,16 +95,16 @@ export const notificationtableConfig = (el, elGroup, elFormula) => {
placeholder: '请选择告警优先级',
options: [
{
label: '全部',
value: '',
},
{
label: '启用',
label: '紧急',
value: '1',
},
{
label: '关闭',
value: '0',
label: '重要',
value: '1',
},
{
label: '一般',
value: '2',
},
],
},
@@ -128,10 +119,10 @@ export const notificationtableConfig = (el, elGroup, elFormula) => {
},
{
field: 'provider',
label: '告警标题',
label: '错误码',
component: 'NsInput',
componentProps: {
placeholder: '请输入告警标题关键字',
placeholder: '请输入告警错误码',
},
},
{
@@ -141,10 +132,6 @@ export const notificationtableConfig = (el, elGroup, elFormula) => {
componentProps: {
placeholder: '请选择启用状态',
options: [
{
label: '全部',
value: '',
},
{
label: '启用',
value: '1',

View File

@@ -1,21 +1,21 @@
import { ref } from 'vue';
import { http } from '/nerv-lib/util';
import { origanizemanage } from '/@/api/origanizemanage';
import { carbonEmissionFactorLibrary } from '/@/api/carbonEmissionFactorLibrary';
export const formConfig = (disabled) => {
return ref([
{
field: 'field111',
field: 'fields',
component: 'NsChildForm',
componentProps: {
schemas: [
{
label: '排放源',
field: 'accountNo',
field: 'emissionSources',
component: 'NsInput',
componentProps: {
placeholder: '请输入排放源',
maxLength: 20,
disabled,
},
rules: [
{
@@ -25,7 +25,25 @@ export const formConfig = (disabled) => {
],
},
{
field: 'userStatus',
field: 'emissionType',
label: '排放分类',
component: 'NsCascader',
fieldMap: ['emissionType'],
componentProps: {
placeholder: '请选择排放分类',
api: carbonEmissionFactorLibrary.getCarbonFactorTree,
fieldNames: { label: 'emissionName', value: 'id' },
showSearch: true,
},
rules: [
{
required: true,
message: '请选择排放分类',
},
],
},
{
field: 'emissionGas',
label: '排放气体',
component: 'NsSelect',
componentProps: {
@@ -33,12 +51,28 @@ export const formConfig = (disabled) => {
placeholder: '请选择排放气体',
options: [
{
label: '正常',
value: 0,
label: 'CO2',
value: 'CO2',
},
{
label: '冻结',
value: 1,
label: 'CO2e',
value: 'CO2e',
},
{
label: 'SF6',
value:'SF6',
},
{
label: 'CH4',
value: 'CH4',
},
{
label: 'PFCs',
value: 'PFCs',
},
{
label: 'HFCs',
value: 'HFCs',
},
],
},
@@ -49,56 +83,77 @@ export const formConfig = (disabled) => {
},
],
},
// {
// field: 'emissionProcess',
// label: '排放环节',
// component: 'NsSelectApi',
// componentProps: {
// api: carbonEmissionFactorLibrary.getEmissionProcess,
// params: {
// emissionType: sessionStorage.getItem('checkedTreeNode'),
// },
// resultField: 'data',
// labelField: 'emissionProcess',
// valueField: 'emissionProcess',
// immediate: true,
// autoSelectFirst: false,
// filterOption:false,
// showSearch:true,
// autoClearSearchValue:false
// },
// },
{
field: 'userStatus',
label: '排放环节',
component: 'NsSelect',
field: 'emissionProcess',
component: 'NsInput',
componentProps: {
allowClear: true,
placeholder: '请选择排放环节',
options: [
{
label: '正常',
value: 0,
},
{
label: '冻结',
value: 1,
},
],
placeholder: '请输入排放环节',
maxLength: 20,
},
rules: [
{
required: true,
message: '请输入排放环节',
},
],
},
{
label: '排放因子',
field: 'accountNo',
component: 'NsInput',
field: 'emissionFactors',
component: 'NsInputNumber',
componentProps: {
placeholder: '请输入排放因子值',
maxLength: 20,
disabled,
},
rules: [
{
required: true,
message: '请输入排放因子值',
trigger: 'change',
},
],
},
{
field: 'userStatus',
field: 'carbonEmissionPrefix',
label: '碳排前缀',
component: 'NsSelect',
componentProps: {
disabled: true,
allowClear: true,
defaultValue: 't',
placeholder: '请选择碳排前缀',
options: [
{
label: '正常',
value: 0,
label: 'g',
value: 'g',
},
{
label: '冻结',
value: 1,
label: 'kg',
value: 'kg',
},
{
label: 't',
value: 't',
},
],
},
@@ -111,12 +166,11 @@ export const formConfig = (disabled) => {
},
{
label: '碳排后缀',
field: 'accountNo',
field: 'carbonEmissionSuffix',
component: 'NsInput',
componentProps: {
placeholder: '请输入碳排后缀',
maxLength: 20,
disabled,
},
rules: [
{
@@ -127,21 +181,21 @@ export const formConfig = (disabled) => {
},
{
label: '已引用数',
field: 'accountNo',
field: 'numberOfReferences',
component: 'NsInput',
componentProps: {
defaultValue: 10,
disabled: true,
maxLength: 20,
},
},
{
label: '参考文献',
field: 'accountNo',
field: 'reference',
component: 'NsTextarea',
componentProps: {
placeholder: '请输入参考文献',
maxLength: 20,
disabled,
},
},
],

View File

@@ -19,6 +19,7 @@
</div>
</a-form>
<a-tree
v-if="gData && gData.length > 0"
class="draggable-tree"
style="padding: 0 16px !important;"
draggable
@@ -26,12 +27,15 @@
checkable
block-node
:tree-data="gData"
:checkedKeys="checkedTreeNodeKeys"
:selectedKeys="selectedKeys"
@dragenter="onDragEnter"
@drop="onDrop"
@check="checkTreeNode"
@select="onSelect"
:expanded-keys="expandedKeys"
:auto-expand-parent="autoExpandParent"
@expand="onExpand"
>
@expand="onExpand">
<template #title="{ emissionName }">
<span v-if="emissionName && selectTreeDataValue && emissionName.indexOf(selectTreeDataValue) > -1">
{{ emissionName.substring(0, emissionName.indexOf(selectTreeDataValue)) }}
@@ -41,12 +45,14 @@
<span v-else>{{ emissionName }}</span>
</template>
</a-tree>
<a-popover placement="rightTop" trigger="focus">
<a-popover v-if="showOperation" placement="rightTop" trigger="focus">
<template #content>
<div style="display: flex;flex-direction: column;">
<a-button type="text">编辑</a-button>
<a-button type="text">新增子节点</a-button>
<a-button type="text">删除</a-button>
<a-button type="text" @click="editTreeNodeData">编辑</a-button>
<a-button type="text" @click="addTreeNodeData">新增子节点</a-button>
<a-button type="text">上移</a-button>
<a-button type="text">下移</a-button>
<a-button type="text" @click="deleteTreeNode">删除</a-button>
</div>
</template>
<MoreOutlined style="position: absolute;right: 0;top: 16%;font-size: 25px;cursor: pointer;" />
@@ -62,10 +68,9 @@
<!-- 新增树节点 -->
<ns-modal :visible="treeNodeAdd" :title="operationTree" @ok="handleOk" @cancel="handleCancel">
<ns-input
v-model:value="latitude1"
v-model:value="addTreeNode"
class="input"
placeholder="请输入排放类型"
/>
placeholder="请输入排放类型"/>
</ns-modal>
<!-- 新增数据库数据 -->
<a-drawer
@@ -90,7 +95,8 @@
</div>
</template>
<script lang="ts" setup>
import { MoreOutlined } from '@ant-design/icons-vue';
import { MoreOutlined,ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { Modal } from 'ant-design-vue';
import { computed, createVNode, defineComponent, reactive, ref, watchEffect,watch } from 'vue';
import { http } from '/nerv-lib/util/http';
import { NsMessage, NsModal } from '/nerv-lib/component';
@@ -115,6 +121,7 @@ import { log } from 'node:console';
const disabled = ref(false);
const treeNodeAdd = ref<boolean>(false);
const operationTree = ref<string>('新增');
const showOperation = ref(false)
const opMap: any = ref({
type: 'add',
@@ -128,7 +135,7 @@ import { log } from 'node:console';
const casData = ref([]);
const treeData = ref([]);
const userAuthList = ref([]);
const orgId = JSON.parse(sessionStorage.getItem('userInfo')).orgId;
const orgId = JSON.parse(sessionStorage.getItem(import.meta.env.VITE_PUBLIC_PATH)).orgId;
const dynamicDisabled = computed(() => {
return formRef.value?.validateResult && userAuthList.value?.length;
});
@@ -141,6 +148,7 @@ import { log } from 'node:console';
const y = 2;
const z = 1;
const genData: TreeProps['treeData'] = [];
const checkedTreeNodeKeys = ref<string[]>(['0-0']);
const generateData = (_level: number, _preKey?: string, _tns?: TreeProps['treeData']) => {
const preKey = _preKey || '0';
@@ -265,7 +273,6 @@ import { log } from 'node:console';
expandedKeys.value = keys;
autoExpandParent.value = false;
};
watch(selectTreeDataValue, value => {
const expanded = dataList
.map((item: TreeProps['treeData'][number]) => {
@@ -284,24 +291,109 @@ import { log } from 'node:console';
console.log('use value', selectTreeDataValue);
console.log('or use this.value', value.value);
};
// 点击数据点的复选框
const checkedIds = ref([])
const emissionType = ref()
const checkTreeNode = (checkedKeys, info) => {
checkedTreeNodeKeys.value = checkedKeys
checkedIds.value = []
info.checkedNodes.forEach(item=>{
checkedIds.value.push(item.id)
})
sessionStorage.setItem('checkedTreeNode', checkedIds.value);
emissionType.value = checkedIds.value.join(',')
mainRef.value?.nsTableRef.reload();
}
// 点击新增树节点
const addTreeNodeData = () => {
operationTree.value='新增'
treeNodeAdd.value = true;
editTreeNode.value.type = 'create'
};
// 编辑树节点
const editTreeNodeData = () => {
operationTree.value='编辑'
treeNodeAdd.value = true;
editTreeNode.value.type = 'update'
}
// 删除树节点
const deleteTreeNode = () => {
Modal.confirm({
title: '警告',
icon: createVNode(ExclamationCircleOutlined),
content: '确定要删除么?',
okText: '确认',
okType: 'primary',
cancelText: '取消',
onOk() {
http.post(carbonEmissionFactorLibrary.delTreeNode,editTreeNode.value).then(() => {
getOrgTree()
NsMessage.success('操作成功');
});
},
onCancel() {
console.log('Cancel');
},
});
}
// 新增/编辑树节点点击确定
const addTreeNode =ref()
const handleOk = (e: MouseEvent) => {
console.log(e);
treeNodeAdd.value = false;
};
const handleCancel = () => {
treeNodeAdd.value = false;
};
// 企业树
const getOrgTree = (params?) => {
fetch(carbonEmissionFactorLibrary.getCarbonFactorTree, params).then((res) => {
gData.value = res
editTreeNode.value.emissionName = addTreeNode.value
http.post(carbonEmissionFactorLibrary.creat,editTreeNode.value).then(() => {
getOrgTree()
NsMessage.success('操作成功');
addTreeNode.value = ''
treeNodeAdd.value = false;
});
};
const handleCancel = () => {
addTreeNode.value = ''
treeNodeAdd.value = false;
};
// 获取排放分类树
const getOrgTree = (params?) => {
fetch(carbonEmissionFactorLibrary.getCarbonFactorTree, params).then((res) => {
gData.value = res.data
// 找到匹配的节点数据
const selectedNodes = [];
checkedTreeNodeKeys.value.forEach(key => {
const [parentId, childId] = key.split('-').map(Number);
if (parentId >= 0 && childId >= 0 && gData.value[parentId]?.children?.[childId]) {
selectedNodes.push(gData.value[parentId]);
}
});
// 获取默认选中节点的所有id
getDefaultIds(selectedNodes)
});
};
const defaultIds = ref([])
const getDefaultIds = (selectedNodes) => {
selectedNodes.forEach(items => {
defaultIds.value.push(items.id)
if(items.children){
getDefaultIds(items.children)
}
})
emissionType.value = defaultIds.value.join(',')
checkedIds.value = defaultIds.value
sessionStorage.setItem('checkedTreeNode', checkedIds.value);
}
getOrgTree();
// 被选中的树节点
const editTreeNode = ref({})
const onSelect = (selectedKeys: string[], info: any) => {
if(info.selected){
showOperation.value = true
editTreeNode.value = {
id:info.selectedNodes[0].id,
level:info.selectedNodes[0].level,
dataNumber:info.selectedNodes[0].dataNumber,
sortNumber:info.selectedNodes[0].sortNumber,
parentEmissionId:info.selectedNodes[0].parentEmissionId,
}
}
};
const onSearch = () => {
console.log(searchValue.value);
@@ -338,21 +430,21 @@ import { log } from 'node:console';
const onEdit = () => {
formRef.value?.triggerSubmit().then(() => {
console.log(formData.value, 'formData.value');
if (!userAuthList.value.length) {
NsMessage.error('请添加用户权限');
return;
}
// if (!userAuthList.value.length) {
// NsMessage.error('请添加用户权限');
// return;
// }
opMap.value.fuc &&
opMap.value.fuc({ ...formData.value, userRoleList: userAuthList.value, orgId });
opMap.value.fuc({ ...formData.value });
});
};
const tableConfig = ref({
title: '数据库',
api: carbonEmissionFactorLibrary.getTableList,
params: {
orgId,
emissionType
},
headerActions: [
{
@@ -363,12 +455,14 @@ import { log } from 'node:console';
opMap.value.type = 'add';
setTimeout(() => {
formData.value = {
// orgName: JSON.parse(sessionStorage.getItem('userInfo')).orgName,
carbonEmissionPrefix:'t',
numberOfReferences:'10'
};
userAuthList.value.splice(0);
});
opMap.value.fuc = (formData: any) => {
return http.post(carbonEmissionFactorLibrary.addNewData, formData).then(() => {
formData.emissionType = formData.emissionType[formData.emissionType.length - 1]
return http.post(carbonEmissionFactorLibrary.creatOrUpdate, formData).then(() => {
mainRef.value?.nsTableRef.reload();
visible.value = false;
NsMessage.success('操作成功');
@@ -398,14 +492,13 @@ import { log } from 'node:console';
confirm: true,
isReload: true,
isClearCheck: true,
api: carbonEmissionFactorLibrary.batchDel,
dynamicParams: { userIds: 'userId[]' },
api: carbonEmissionFactorLibrary.del,
dynamicParams: { id: 'id[]' },
},
],
columns: [
{
title: 'id',
dataIndex: 'address',
customRender: (text: any) => {
return text.index + 1;
},
@@ -416,7 +509,7 @@ import { log } from 'node:console';
},
{
title: '排放类型',
dataIndex: 'emissionType',
dataIndex: 'emissionTypeColumn',
},
{
title: '排放气体',
@@ -460,12 +553,15 @@ import { log } from 'node:console';
handle: (record: any) => {
userAuthList.value.splice(0);
setTimeout(() => {
formData.value = record;
userAuthList.value.push(...record.userRoleInfos);
console.log(record.id);
http.post(carbonEmissionFactorLibrary.findById,{ id: record.id } ).then((res) => {
formData.value = res.data;
});
}, 10);
opMap.value.type = 'edit';
opMap.value.fuc = (formData: any) => {
return http.post(carbonEmissionFactorLibrary.editUser, formData).then(() => {
return http.post(carbonEmissionFactorLibrary.creatOrUpdate, formData).then(() => {
mainRef.value?.nsTableRef.reload();
visible.value = false;
NsMessage.success('操作成功');
@@ -477,7 +573,7 @@ import { log } from 'node:console';
{
label: '删除',
name: 'userDelete',
dynamicParams: 'userId',
dynamicParams: 'id',
confirm: true,
isReload: true,
api: carbonEmissionFactorLibrary.del,
@@ -497,7 +593,7 @@ import { log } from 'node:console';
},
},
{
field: 'userStatus',
field: 'emissionProcess',
label: '排放环节',
component: 'NsSelect',
componentProps: {
@@ -505,18 +601,14 @@ import { log } from 'node:console';
placeholder: '请选择排放环节',
options: [
{
label: '正常',
label: '消费环节',
value: 0,
},
{
label: '冻结',
value: 1,
},
}
],
},
},
{
field: 'userStatus',
field: 'emissionGas',
label: '排放气体',
component: 'NsSelect',
componentProps: {
@@ -524,37 +616,38 @@ import { log } from 'node:console';
placeholder: '请选择排放气体',
options: [
{
label: '正常',
label: 'CO2',
value: 0,
},
{
label: '冻结',
label: 'CO2e',
value: 1,
},
],
},
},
{
field: 'userStatus',
field: 'carbonDatabase',
label: '数据库名称',
component: 'NsSelect',
componentProps: {
allowClear: true,
placeholder: '请选择数据库名称',
showSearch: true,
options: [
{
label: '正常',
value: 0,
value: '正常',
},
{
label: '冻结',
value: 1,
value: '冻结',
},
],
},
},
{
field: 'accountNo',
field: 'reference',
label: '文献关键字',
component: 'NsInput',
componentProps: {
@@ -566,7 +659,7 @@ import { log } from 'node:console';
params: {},
},
// pagination: { defaultPageSize: 10 },
rowKey: 'userId',
rowKey: 'id',
});
</script>
<style lang="less" scoped>

View File

@@ -1,105 +1,74 @@
import { dateUtil } from '/nerv-lib/util/date-util';
import data from './mock.json';
export const tableConfig = {
title: '设备台账',
// api: '/carbon_emission/device/getDeviceList',
value: data.dataSource,
params: {
page: 0,
pageSize: 10,
export const tableColumns = [
{
title: '序号',
customRender: (text: any) => {
return text.index + 1;
},
},
rowSelection: null,
columns: [
{
title: '设备名称',
dataIndex: 'id',
},
{
title: '设备型号',
dataIndex: 'deviceCode',
},
{
title: 'SN码',
dataIndex: 'deviceName',
textNumber: 8,
textEllipsis: true,
},
{
title: '设备一级区域',
dataIndex: 'position',
},
{
title: '设备二级区域',
dataIndex: 'position',
},
{
title: '设备详细位置',
dataIndex: 'position',
},
{
title: '设备规格',
dataIndex: 'position',
},
{
title: '设备厂商纳税人识别号',
dataIndex: 'position',
},
{
title: '厂商联系人',
dataIndex: 'position',
},
{
title: '设备描述',
dataIndex: 'position',
},
{
title: 'IP地址',
dataIndex: 'position',
},
{
title: '生产日期',
dataIndex: 'position',
},
{
title: '采购日期',
dataIndex: 'position',
},
{
title: '启用日期',
dataIndex: 'position',
},
{
title: '设备成本(元)',
dataIndex: 'position',
},
{
title: '使用期限',
dataIndex: 'position',
},
{
title: '额定功率',
dataIndex: 'position',
},
{
title: '特殊参数',
dataIndex: 'position',
},
],
formConfig: {
schemas: [
{
field: 'createTime',
label: '生产日期',
component: 'NsRangePicker',
fieldMap: ['queryStartDate', 'queryEndDate'],
componentProps: {
valueFormat: 'YYYY-MM-DD',
},
},
],
params: {},
{
title: '能源种类',
dataIndex: 'money',
},
// pagination: { pageSizeOptions: false },
rowKey: 'uuid',
};
{
title: '计量单位',
className: 'column-money',
dataIndex: 'money',
},
{
title: '全年',
dataIndex: 'address',
},
{
title: '1月',
dataIndex: 'address',
},
{
title: '2月',
dataIndex: 'address',
},
{
title: '3月',
dataIndex: 'address',
},
{
title: '4月',
dataIndex: 'address',
},
{
title: '5月',
dataIndex: 'address',
},
{
title: '6月',
dataIndex: 'address',
},
{
title: '7月',
dataIndex: 'address',
},
{
title: '8月',
dataIndex: 'address',
},
{
title: '9月',
dataIndex: 'address',
},
{
title: '10月',
dataIndex: 'address',
},
{
title: '11月',
dataIndex: 'address',
},
{
title: '12月',
dataIndex: 'address',
},
{
title: '操作',
key: 'action',
width: 130
},
];

View File

@@ -0,0 +1,251 @@
<template>
<div>
<a-table
:columns="tableColumns"
:data-source="data"
bordered
:pagination="false"
:scroll="{ x: 2000 }">
<template #bodyCell="{ column, text }">
<template v-if="column.key === 'action'">
<span>
<a>编辑</a>
<a-divider type="vertical" />
<a>删除</a>
</span>
</template>
</template>
<template #title>
<a-date-picker v-model:value="selectYear" picker="year" />
<div class="buttonGroup">
<a-button type="primary" @click="addNewData">新增</a-button>
<a-button type="primary">导入</a-button>
<a-button type="primary">导出</a-button>
<a-button type="primary">模板下载</a-button>
<a-button type="primary">上传凭证</a-button>
<a-button type="primary">凭证下载</a-button>
</div>
</template>
</a-table>
<a-pagination
:current="current"
:page-size="pageSize"
:total="0"
:show-total="(total, range) => `${range[0]}-${range[1]} of ${total} items`" />
<!-- 新增数据库数据 -->
<a-drawer
:width="500"
:visible="visible"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
@close="onClose">
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item ref="name" label="能源种类" name="name">
<a-input v-model:value="formState.name" />
</a-form-item>
<a-form-item label="计量单位">
<a-cascader v-model:value="value" :options="options" placeholder="Please select" />
</a-form-item>
<a-form-item label="自动采集节点">
<a-tree-select
v-model:value="treeValue"
style="width: 300px"
placeholder="Please select"
:tree-line="true"
:tree-data="treeData"
tree-node-filter-prop="title"
>
<template #title="{ value: val, title }">
<b v-if="val === 'parent 1-1'" style="color: #08c">sss</b>
<template v-else>{{ title }}</template>
</template>
</a-tree-select>
</a-form-item>
<a-form-item label="计算碳排" name="resource">
<a-radio-group v-model:value="formState.resource">
<a-radio value="1">是</a-radio>
<a-radio value="2">否</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="排放类型" name="region">
<a-select v-model:value="formState.region" placeholder="请选择排放类型">
<a-select-option value="shanghai">Zone one</a-select-option>
<a-select-option value="beijing">Zone two</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="1" name="delivery">
<a-switch v-model:checked="formState.delivery" />
</a-form-item>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose">取消</a-button>
<a-button type="primary" @click="onSubmit">确定</a-button>
</template>
</a-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref,reactive, toRaw } from 'vue';
import type { UnwrapRef } from 'vue';
import type { Rule } from 'ant-design-vue/es/form';
import type { CascaderProps,TreeSelectProps } from 'ant-design-vue';
import type { Dayjs } from 'dayjs';
import { http } from '/nerv-lib/util/http';
import { tableColumns } from '../config';
import { energyConsumption } from '/@/api/carbonEmissionFactorLibrary';
defineOptions({
name: 'EnergyConsumption', // 与页面路由name一致缓存才可生效
});
const orgId = JSON.parse(sessionStorage.getItem(import.meta.env.VITE_PUBLIC_PATH)).orgId;
const fetch = (api, params = { orgId }) => {
return http.post(api, params);
};
const selectYear = ref<Dayjs>();
const current = ref<number>(1);
const pageSize = ref<number>(10);
const visible = ref(false);
const value = ref<string[]>([]);
const data = ref([]);
interface FormState {
name: string;
region: string | undefined;
delivery: boolean;
resource: string;
}
const formRef = ref();
const labelCol = { span: 5 };
const wrapperCol = { span: 13 };
const formState: UnwrapRef<FormState> = reactive({
name: '',
region: undefined,
delivery: false,
resource: '',
});
// 定义form表单的必填
const rules: Record<string, Rule[]> = {
name: [
{ required: true, message: '请输入能源种类', trigger: 'change' },
],
resource: [{ required: true, message: 'Please select activity resource', trigger: 'change' }],
};
// 定义计量单位级联选择的变量
const options: CascaderProps['options'] = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
// 定义自动采集节点数的变量
const treeValue = ref<string>();
const treeData = ref<TreeSelectProps['treeData']>([
{
title: 'parent 1',
value: 'parent 1',
children: [
{
title: 'parent 1-0',
value: 'parent 1-0',
children: [
{
title: 'my leaf',
value: 'leaf1',
},
{
title: 'your leaf',
value: 'leaf2',
},
],
},
{
title: 'parent 1-1',
value: 'parent 1-1',
},
],
},
]);
// 获取表格数据
const getTableList = () => {
fetch(energyConsumption.pageList).then((res) => {
console.log(res,'aaaaaa');
});
};
getTableList()
// 点击确定提交
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
console.log('values', formState, toRaw(formState));
})
.catch(error => {
console.log('error', error);
});
};
const resetForm = () => {
formRef.value.resetFields();
};
// 点击新增按钮
const addNewData = () => {
visible.value = true
}
// 关闭新增抽屉
const onClose = () => {
visible.value = false;
};
</script>
<style scoped lang="less">
::v-deep .ant-table-title{
display: flex;
}
::v-deep .ant-table-container{
padding: 0px 16px;
}
.buttonGroup{
margin-left: 1vw;
width: 30vw;
display: flex;
justify-content: space-around;
}
</style>
<style scoped>
th.column-money,
td.column-money {
text-align: right !important;
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="能耗统计">
<ns-view-list-table v-bind="tableConfig" />
<energyConsumption ref="energyConsumptionRef" />
</a-tab-pane>
<a-tab-pane key="2" tab="碳排统计" force-render>Content of Tab Pane 2</a-tab-pane>
<a-tab-pane key="3" tab="碳排速算">Content of Tab Pane 3</a-tab-pane>
@@ -9,7 +9,7 @@
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { tableConfig } from './config';
import energyConsumption from './energyConsumption/index.vue';
defineOptions({
name: 'CarbonEmissionStatisticsIndex', // 与页面路由name一致缓存才可生效
});

View File

@@ -4,10 +4,13 @@ import { http } from '/nerv-lib/util';
import { ref } from 'vue';
import { group } from '/@/api/deviceManage';
import { dict } from '/@/api';
const tableKeyMap = [
import { origanizemanage } from '/@/api/origanizemanage';
const tableCalKeyMap = [
{
title: '来源企业',
dataIndex: 'id',
dataIndex: 'orgName',
// textEllipsis: true,
// textNumber: 4,
},
{
title: '设备id',
@@ -32,20 +35,18 @@ const tableKeyMap = [
dataIndex: 'position',
},
];
const tableCalKeyMap = [
const tableKeyMap = [
{
title: '来源企业',
dataIndex: 'id',
dataIndex: 'orgName',
},
{
title: '节点编号',
dataIndex: 'deviceName',
textNumber: 8,
textEllipsis: true,
dataIndex: 'pointNum',
},
{
title: '节点路径',
dataIndex: 'position',
dataIndex: 'routeName',
},
];
const doWnload = (url) => {
@@ -62,7 +63,12 @@ const mockData = ref(data.listData);
export const formSchema = [
{
field: 'isCreate',
component: 'NsInput',
component: 'NsCheck',
show: false,
},
{
field: 'isCreatSon',
component: 'NsCheck',
show: false,
},
{
@@ -71,7 +77,17 @@ export const formSchema = [
show: false,
},
{
field: 'isCreatSon',
field: 'energyType',
component: 'NsInput',
show: false,
},
{
field: 'id',
component: 'NsInput',
show: false,
},
{
field: 'pid',
component: 'NsInput',
show: false,
},
@@ -95,10 +111,10 @@ export const formSchema = [
component: 'NsSelectApi',
componentProps: {
placeholder: '请选择节点类型(必填)',
api: dict,
params: { dicKey: 'COUNT_POINT' },
api: () => dict({ params: { dicKey: 'COUNT_POINT' } }),
// params: { dicKey: 'COUNT_POINT' },
immediate: true,
resultField: 'data.COUNT_POINT',
// resultField: 'data.COUNT_POINT',
labelField: 'cnValue',
valueField: 'cnValue',
},
@@ -110,6 +126,26 @@ export const formSchema = [
],
},
];
export const editTreeConfig = (orgId) => ({
selectedKeys: ['0-0'],
defaultExpandAll: true,
api: group.queryEditGroup,
params: { orgId },
resultField: 'data.orgInfos',
fieldNames: { title: 'orgName', key: 'orgId' },
formConfig: {
schemas: [
{
field: 'orgName',
component: 'NsInput',
autoSubmit: true,
componentProps: {
placeholder: '请输入企业名称',
},
},
],
},
});
export const treeConfig = (orgId) => {
return {
defaultExpandAll: true,
@@ -118,16 +154,10 @@ export const treeConfig = (orgId) => {
title: '能耗分组',
},
params: { orgId },
api: group.queryDeviceGroupTree,
// api: () => {
// return new Promise((resolve) => {
// setTimeout(() => {
// resolve({ data: [{ title: '全部', key: 'all', children: data.data }] });
// }, 100);
// });
// },
transform: (data) => {
return [{ title: '全部', key: 'all', selectable: false, children: data }];
return [{ pointName: '全部', id: 'all', selectable: false, children: data }];
},
formConfig: {
schemas: [
@@ -137,13 +167,14 @@ export const treeConfig = (orgId) => {
component: 'NsSelectApi',
autoSubmit: true,
componentProps: {
api: dict,
params: { dicKey: 'ENERGY_TYPE' },
api: () => dict({ params: { dicKey: 'ENERGY_TYPE' } }),
// params: { dicKey: 'ENERGY_TYPE' },
immediate: true,
resultField: 'data.ENERGY_TYPE',
// resultField: 'data.ENERGY_TYPE',
labelField: 'cnValue',
valueField: 'cnValue',
placeholder: '请选择能耗种类',
autoSelectFirst: true,
},
},
{
@@ -159,12 +190,11 @@ export const treeConfig = (orgId) => {
},
};
};
export const tableConfig = (el, elGroup, elFormula) => {
export const tableConfig = (el, elGroup, elFormula, defaultParams) => {
return {
title: '点位信息',
// api: '/carbon_emission/device/getDeviceList',
value: mockData.value,
api: group.queryGroupPage,
params: defaultParams.value,
headerActions: [
{
label: '编辑',
@@ -176,7 +206,7 @@ export const tableConfig = (el, elGroup, elFormula) => {
},
{
label: '批量删除',
name: 'groupTemDownload',
name: 'groupBatDelete',
type: 'primary',
dynamicDisabled: (data: any) => {
return data.list.length === 0;
@@ -227,7 +257,7 @@ export const tableConfig = (el, elGroup, elFormula) => {
{
label: '批量分组',
name: 'groupTemDownload',
name: 'groupBatGroup',
type: 'primary',
handle: () => {
elGroup.value.toggle();
@@ -235,7 +265,7 @@ export const tableConfig = (el, elGroup, elFormula) => {
},
{
label: '公式编辑',
name: 'groupTemDownload',
name: 'groupFormulaEdit',
type: 'primary',
handle: () => {
elFormula.value.toggle();
@@ -248,7 +278,7 @@ export const tableConfig = (el, elGroup, elFormula) => {
actions: [
{
label: '删除',
name: 'FeedBackDetail',
name: 'groupDelete',
dynamicParams: ['uuid', 'appealType'],
confirm: true,
handle: () => {
@@ -324,11 +354,13 @@ export const tableConfig = (el, elGroup, elFormula) => {
};
};
export const tableConfigCal = (el, elGroup, elFormula) => {
export const tableConfigCal = (el, elGroup, elFormula, defaultParams) => {
// 分组节点
return {
title: '点位信息',
// api: '/carbon_emission/device/getDeviceList',
value: mockData.value,
params: defaultParams,
headerActions: [
{
@@ -396,12 +428,12 @@ export const tableConfigCal = (el, elGroup, elFormula) => {
actions: [
{
label: '删除',
name: 'FeedBackDetail',
dynamicParams: ['uuid', 'appealType'],
name: 'groupDelete',
dynamicParams: 'id',
confirm: true,
handle: () => {
mockData.value.splice(0, 1);
},
isReload: true,
isClearCheck: true,
api: group.delGroupList,
},
],
},

View File

@@ -1,53 +1,72 @@
<template>
<ns-drawer
v-model:visible="visible"
size="large"
:width="800"
class="custom-class"
title="编辑"
title=" "
destroyOnClose
:ok="btnClick"
:cancel="() => (visible = false)"
placement="right">
<div class="drawerContainer">
<ns-tree-api v-bind="treeConfig" @select="treeSelect" />
<ns-tree-api v-bind="config" @select="treeSelect" />
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
:data-source="dataSource"
style="height: 100%; width: 66%"
oneWay
:listStyle="listStyle"
show-search
:render="(item) => item.title"
:filter-option="filterOption"
:render="(item) => item.title" />
@change="handleChange" />
</div>
</ns-drawer>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { NsMessage } from '/nerv-lib/component';
import { treeConfig } from './config';
const visible = ref(false);
const mockData = ref([]);
import { editTreeConfig } from './config';
import { group } from '/@/api/deviceManage';
import { http } from '/nerv-lib/util/http';
const props = defineProps({ params: Object });
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
const config = computed(() => {
return editTreeConfig(result);
});
const visible = ref(true);
const dataSource = ref([]);
const listStyle = {
height: '100%',
width: '100%',
};
const targetKeys = ref<string[]>([]);
const targetKeys = ref([]);
const currentId = ref('');
const toggle = () => {
visible.value = !visible.value;
};
const handleChange = (nextTargetKeys: string[], direction: string, moveKeys: string[]) => {
console.log('targetKeys: ', nextTargetKeys);
console.log('direction: ', direction);
console.log('moveKeys: ', moveKeys);
};
onMounted(() => {
getMock('上海公司');
});
const filterOption = (inputValue: string, option: MockData) => {
return option.description.indexOf(inputValue) > -1;
const filterOption = (inputValue: string, option: any) => {
return option?.title.toLowerCase().indexOf(inputValue.toLowerCase()) > -1;
};
const btnClick = () => {
visible.value = false;
NsMessage.success('编辑成功');
// visible.value = false;
http
.post(group.saveGroupList, {
...props.params,
saveOrgId: currentId.value,
saveGroupIds: targetKeys.value,
})
.then(() => {
NsMessage.success('操作成功');
});
};
function treeSelect(
selectedKeys: never[],
@@ -62,26 +81,27 @@
const {
dataRef: { title },
} = e.node;
getMock(title);
currentId.value = selectedKeys[0];
getData(currentId.value);
}
const getMock = (company) => {
const keys = [];
const mData = [];
for (let i = 0; i < 60; i++) {
const data = {
key: i.toString(),
title: `A00${i + 1}总用电量(${company})`,
description: `description of content${i + 1}`,
chosen: Math.random() * 2 > 1,
};
if (data.chosen) {
keys.push(data.key);
}
mData.push(data);
}
mockData.value = mData;
targetKeys.value = keys;
const getData = (id) => {
http
.post(group.queryEditGroup, {
orgId: result,
queryOrgId: id,
...props.params,
// energyType: props.params.energyType,
})
.then((res) => {
dataSource.value = res.data.groups?.map((item) => {
item['title'] = item.pointName;
item['key'] = item.id.toString();
return item;
});
targetKeys.value = res.data.linkGroups.map((item) => {
return item.id;
});
});
};
defineExpose({
toggle,

View File

@@ -1,5 +1,5 @@
<template>
<editDrawer ref="editDrawerRef" />
<editDrawer ref="editDrawerRef" :params="defaultParams" />
<editGroup ref="editGroupRef" />
<editFormula ref="editFormulaRef" />
@@ -10,20 +10,23 @@
<NsModalFrom ref="modalFormRef" v-bind="nsModalFormConfig" />
<div class="groupContainer">
<div class="tree">
<ns-tree-api v-bind="tConfig" @select="handleSelect">
<ns-tree-api ref="treeRef" v-bind="tConfig" @select="handleSelect">
<template #title="data">
<div class="treeRow">
<span>{{ data.title }}</span>
<div>
<ns-icon :name="data.pointType !== '计算节点' ? 'fenzujiedian' : 'jisuanjiedian'" />
<span style="padding-left: 8px">{{ data.pointName }}</span>
</div>
<a-dropdown>
<ns-icon name="actionMore" size="14" class="actionMore" />
<template #overlay>
<a-menu>
<a-menu-item
v-for="(item, index) in actionList"
:key="index"
@click="item.func(data)">
<span>{{ item.title }}</span>
</a-menu-item>
<template v-for="(item, index) in filterAction(actionList, data)" :key="index">
<!-- 全部节点只需要新增子节点 -->
<a-menu-item @click="item.func(data)">
<span>{{ item.title }}</span>
</a-menu-item>
</template>
</a-menu>
</template>
</a-dropdown>
@@ -31,8 +34,8 @@
</template>
</ns-tree-api>
</div>
<ns-view-list-table v-show="defaultType" class="table" v-bind="config" />
<ns-view-list-table v-show="!defaultType" class="table" v-bind="configCal" />
<ns-view-list-table v-if="defaultType" class="table" v-bind="config" ref="tableRef" />
<ns-view-list-table v-else class="table" v-bind="configCal" ref="tableCalRef" />
</div>
</template>
<script lang="ts" setup>
@@ -45,17 +48,23 @@
import { NsMessage, NsModal } from '/nerv-lib/component';
import NsModalFrom from '/@/components/ns-modal-form.vue';
import { group } from '/@/api/deviceManage';
import { http } from '/nerv-lib/util/http';
type opType = 'up' | 'down';
const { getParams } = useParams();
const modalFormRef = ref();
const tableRef = ref();
const tableCalRef = ref();
const editDrawerRef = ref();
const editGroupRef = ref();
const editFormulaRef = ref();
const treeRef = ref();
const defaultType = ref(true);
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
const config = tableConfig(editDrawerRef, editGroupRef, editFormulaRef);
const configCal = tableConfigCal(editDrawerRef, editGroupRef, editFormulaRef);
const defaultParams = ref({ orgId: result, energyType: '用电量', id: 19 });
const config = tableConfig(editDrawerRef, editGroupRef, editFormulaRef, defaultParams);
const configCal = tableConfigCal(editDrawerRef, editGroupRef, editFormulaRef, defaultParams);
const tConfig = treeConfig(result);
const nsModalFormConfig = ref({
api: group.creatOrUpdate,
@@ -65,6 +74,9 @@
extraModalConfig: {
bodyStyle: { paddingBottom: 0 },
},
success: () => {
treeRef.value?.treeReload();
},
});
nextTick(() => {
console.log(modalFormRef.value, 'modal');
@@ -72,36 +84,69 @@
const addNodeSon = (data) => {
console.log(data);
nsModalFormConfig.value.title = '新增';
nsModalFormConfig.value.data = {
pointName: '新增',
isCreate: true,
isCreatSon: false,
orgId: result,
energyType: data.formModel?.energyType,
};
if (data.id !== 'all') {
nsModalFormConfig.value.data.pid = data.id;
nsModalFormConfig.value.data.isCreatSon = true;
}
modalFormRef.value?.toggle();
};
const editNode = (data) => {
console.log(data);
nsModalFormConfig.value.title = '编辑';
nsModalFormConfig.value.data = { pointName: 123 };
setTimeout(() => {
nsModalFormConfig.value.data = data;
}, 1);
modalFormRef.value?.toggle();
data.value = { pointName: 'qwe' };
};
const moveNode = (data, type: opType) => {
console.log(data);
const flag = type === 'up';
http.post(group.move, { ...data, isUp: flag }).then(() => {
treeRef.value?.treeReload();
NsMessage.success('操作成功');
});
};
const deleteNode = (a) => {
const deleteNode = (record) => {
NsModal.confirm({
centered: true,
title: '提示',
content: '确定删除吗?',
onOk: () => {
console.log(a);
http.post(group.del, { id: record.id }).then(() => {
treeRef.value?.treeReload();
treeRef.value?.clearSelectedKeys();
NsMessage.success('操作成功');
});
},
});
};
const filterAction = (data) => {};
const filterAction = (actions, data) => {
let list: string | any[] = ['addNodeSon', 'editNode', 'deleteNode'];
//判断逻辑先后不可颠倒
// 最后一个以及中间的不会包含isFirst即支持上移
if (!data?.isFirst) {
list.push('moveUp');
}
// 第一个以及中间的不会包含isEnd即支持下移
if (!data?.isEnd) {
list.push('moveDown');
}
// 只有一条即不支持上移也不支持下移
if (data.isOnlyOne) {
list = ['addNodeSon', 'editNode', 'deleteNode'];
}
// 全部根节点
if (data.id === 'all') {
list = ['addNodeSon'];
}
return actions.filter(({ key }) => list.includes(key));
};
const actionList = [
{ title: '新增子节点', key: 'addNodeSon', func: (data) => addNodeSon(data) },
{ title: '编辑', key: 'editNode', func: (data) => editNode(data) },
@@ -109,8 +154,17 @@
{ title: '下移', key: 'moveDown', func: (data) => moveNode(data, 'down') },
{ title: '删除', key: 'deleteNode', func: (data) => deleteNode(data) },
];
const handleSelect = () => {
defaultType.value = !defaultType.value;
const handleSelect = (key, record) => {
console.log(record);
const {
node: { pointType, id, energyType },
} = record;
defaultParams.value.energyType = energyType;
defaultParams.value.id = id;
defaultType.value = pointType === '分组节点';
defaultType.value
? tableRef.value?.nsTableRef.reload()
: tableCalRef.value?.nsTableRef.reload();
};
</script>
<style lang="less" scoped>

View File

@@ -33,6 +33,7 @@ export const tableConfig = (orgId) => {
},
},
rowSelection: null,
scroll: { x: 2000 },
columns: [
{
title: '设备名称',
@@ -74,7 +75,7 @@ export const tableConfig = (orgId) => {
{
title: '设备厂商纳税人识别号',
dataIndex: 'deviceRatepay',
textNumber: 8,
textNumber: 9,
textEllipsis: true,
},
{

View File

@@ -1,210 +1,136 @@
<template>
<div ref="chart" style="width: 100%; height: 80%;"></div>
<div ref="graphChart" style="width: 100%; height: 80%"></div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, watch } from 'vue';
import * as echarts from 'echarts';
const data = [
{
date: '2023-12-01 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '21'
},
{
name: 'AC_003照明电表',
value: '36'
},
{
name: 'AC_004给排水电表',
value: '5'
}
]
},
{
date: '2023-12-02 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '26'
},
{
name: 'AC_003照明电表',
value: '25'
},
{
name: 'AC_004给排水电表',
value: '47'
}
]
},
{
date: '2023-12-03 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '18'
},
{
name: 'AC_003照明电表',
value: '22'
},
{
name: 'AC_004给排水电表',
value: '26'
}
]
},
{
date: '2023-12-04 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '40'
},
{
name: 'AC_003照明电表',
value: '15'
},
{
name: 'AC_004给排水电表',
value: '12'
}
]
},
{
date: '2023-12-05 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '15'
},
{
name: 'AC_003照明电表',
value: '18'
},
{
name: 'AC_004给排水电表',
value: '15'
}
]
},
];
import { defineComponent, onMounted, ref, watch } from 'vue';
import * as echarts from 'echarts';
import { inject } from 'vue';
export default defineComponent({
// eslint-disable-next-line vue/multi-word-component-names
name: 'Graph',
setup() {
const chart = ref(null);
onMounted(() => {
var seriesList = []
var date = []
var legendList: string|any[] = []
for (let i = 0; i < data.length; i++) {
date.push(data[i].date)
for (let j = 0; j < data[i].data.length; j++) {
if (seriesList.length < j + 1) {
seriesList.push(
{
name: data[i].data[j].name,
data: [data[i].data[j].value],
type: 'line',
smooth: true
}
)
} else {
seriesList[j].data.push(data[i].data[j].value)
}
if (legendList.length == 0 || legendList.length < j + 1) {
legendList.push(data[i].data[j].name)
}
}
let data = ref<any[]>([]);
interface PageData {
tableList: any[];
tableColumns: any[];
graphList: any[];
}
const pageData = inject<PageData>('pageData');
const option = {
legend: {
data: legendList,
orient: 'horizontal',
bottom: 30
if (!pageData) {
throw new Error('pageData is not provided');
}
// 监听 pageData 的变化
watch(
() => pageData as PageData,
(_newValue, _oldValue) => {
// 执行你的逻辑代码
draw();
},
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
const date = params[0].name;
const values = params.map((param: any) => {
const unit = data.find(d => d.date === date)?.unit || '';
return `<tr>
<td>${param.marker}${param.seriesName}</td>
<td style="text-align: right;">${param.value} ${unit}</td>
</tr>`;
}).join('');
return `<div>${date}</div><table style="width: 100%;">${values}</table>`;
{ deep: true },
);
const graphChart = ref(null);
let chartInstance: echarts.ECharts | null = null;
const draw = () => {
data.value = pageData.graphList;
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(graphChart.value);
var seriesList = [];
var date = [];
var legendList: string | any[] = [];
for (let i = 0; i < data.value.length; i++) {
date.push(data.value[i].date);
for (let j = 0; j < data.value[i].data.length; j++) {
if (seriesList.length < j + 1) {
seriesList.push({
name: data.value[i].data[j].name,
data: [data.value[i].data[j].value],
type: 'line',
smooth: true,
});
} else {
seriesList[j].data.push(data.value[i].data[j].value);
}
if (legendList.length == 0 || legendList.length < j + 1) {
legendList.push(data.value[i].data[j].name);
}
}
},
xAxis: {
type: 'category',
data: date
},
yAxis: {
type: 'value'
},
dataZoom: [
// {
// type: 'inside',
// start: 0,
// end: 10
// },
{
height: 10,
start: 0,
end: 100,
handleSize: '300%', // 设置滑块的大小
bottom: 15,
}
const option = {
legend: {
data: legendList,
orient: 'horizontal',
bottom: 30,
},
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
const date = params[0].name;
const values = params
.map((param: any) => {
const unit = data.value.find((d) => d.date === date)?.unit || '';
return `<tr>
<td>${param.marker}${param.seriesName}</td>
<td style="text-align: right;">${param.value} ${unit}</td>
</tr>`;
})
.join('');
return `<div>${date}</div><table style="width: 100%;">${values}</table>`;
},
},
xAxis: {
type: 'category',
data: date,
},
yAxis: {
type: 'value',
},
dataZoom: [
{
height: 10,
start: 0,
end: 100,
handleSize: '300%', // 设置滑块的大小
bottom: 15,
},
],
series: seriesList,
};
}
],
series: seriesList
// series: [
// {
// data: [820, 932, 901, 934, 1290, 1330, 1320],
// type: 'line'
// }
// ]
chartInstance = echarts.init(graphChart.value);
chartInstance.setOption(option);
};
const chartInstance = echarts.init(chart.value);
chartInstance.setOption(option);
});
return { chart };
}
})
onMounted(() => {
draw();
});
// 下载图表
const downloadChart = () => {
if (chartInstance) {
const base64 = chartInstance.getDataURL({
type: 'png',
backgroundColor: '#fff',
});
const link = document.createElement('a');
link.href = base64;
link.download = 'chart.png';
link.click();
}
};
return {
draw,
graphChart,
downloadChart,
};
},
});
</script>
<style lang="less" scoped>
</style>
<style lang="less" scoped></style>

View File

@@ -1,76 +1,19 @@
<!-- eslint-disable vue/multi-word-component-names -->
<template>
<a-row type="flex">
<a-col :span="4">
<div style="padding: 0 20px; width: 100%; height: 100%;">
<tree ref="treeRef"></tree>
</div>
</a-col>
<a-col :span="20">
<div style="width: 100%; height: 100%;">
<div class="ns-right-title">
<span>历史数据</span>
<div class="button">
<ns-icon name="biaoge" size="18" style="margin-right: 10px;"/>
<ns-icon :name="iconName" size="18" style="margin-right: 10px;" @click="change"/>
</div>
</div>
<graph ref="graphRef" v-if="isGraph"></graph>
<environment-table ref="tableRef" v-else></environment-table>
</div>
</a-col>
</a-row>
<page />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import tree from './tree/index.vue';
import graph from './graph/index.vue';
import environmentTable from './table/index.vue';
<script setup>
import { reactive, provide } from 'vue';
import page from './page.vue';
const iconName = ref('biaoge');
const treeRef = ref();
const graphRef = ref();
const tableRef = ref();
let isGraph = ref(true)
defineOptions({
name: 'EnvironmentMonitorIndex', // 与页面路由name一致缓存才可生效
});
function change() {
isGraph.value = !isGraph.value
if (iconName.value == 'biaoge') {
iconName.value = 'bingtu'
} else {
iconName.value = 'biaoge'
}
}
// 创建一个响应式对象
const pageData = reactive({
tableList: [],
tableColumns: [],
graphList: [],
});
// 使用 provide 函数来提供这个响应式对象
provide('pageData', pageData);
</script>
<style lang="less" scoped>
.ns-right-title {
display: flex;
justify-content: space-between;
align-items: center;
user-select: text;
margin-bottom: 5px;
padding-bottom: 10px;
padding-top: 10px;
border-bottom: 1px solid #e9e9e9;
> span {
padding-left: 10px;
line-height: 20px;
}
}
.button {
display: inline-block;
padding-right: 10px;
}
</style>

View File

@@ -0,0 +1,81 @@
<!-- eslint-disable vue/multi-word-component-names -->
<template>
<a-row type="flex">
<a-col :span="4">
<div style="padding: 0 20px; width: 100%; height: 100%">
<tree ref="treeRef" />
</div>
</a-col>
<a-col :span="20">
<div style="width: 100%; height: 100%">
<div class="ns-right-title">
<span>历史数据</span>
<div class="button">
<ns-icon name="xiazai" size="18" style="margin-right: 10px" @click="downloadChart" />
<ns-icon :name="iconName" size="18" style="margin-right: 10px" @click="change" />
</div>
</div>
<graph ref="graphRef" v-if="isGraph" />
<environment-table ref="tableRef" v-else />
</div>
</a-col>
</a-row>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import tree from './tree/index.vue';
import graph from './graph/index.vue';
import environmentTable from './table/index.vue';
const iconName = ref('biaoge');
const treeRef = ref();
const graphRef = ref();
const tableRef = ref();
let isGraph = ref(true);
defineOptions({
name: 'EnvironmentMonitorIndex', // 与页面路由name一致缓存才可生效
});
const downloadChart = () => {
if (isGraph.value) {
if (graphRef.value) {
graphRef.value.downloadChart();
}
}
};
function change() {
isGraph.value = !isGraph.value;
if (iconName.value == 'biaoge') {
iconName.value = 'bingtu';
} else {
iconName.value = 'biaoge';
}
}
</script>
<style lang="less" scoped>
.ns-right-title {
display: flex;
justify-content: space-between;
align-items: center;
user-select: text;
margin-bottom: 5px;
padding-bottom: 10px;
padding-top: 10px;
border-bottom: 1px solid #e9e9e9;
> span {
padding-left: 10px;
line-height: 20px;
}
}
.button {
display: inline-block;
padding-right: 10px;
}
</style>

View File

@@ -3,184 +3,177 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import type { TableColumnType } from 'ant-design-vue';
import { defineComponent, watch, ref, onMounted } from 'vue';
import type { TableColumnType } from 'ant-design-vue';
import { inject } from 'vue';
const data = [
{
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '3',
name: 'AC_004给排水电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
];
// let data: any[] = [];
export default defineComponent({
name: 'EnvironmentTable',
setup() {
const getRowSpan = (dataIndex: string, record, data, dependents: string[] = []) => {
let rowSpan = 1;
for (let i = data.indexOf(record) + 1; i < data.length; i++) {
let shouldMerge = true;
for (const dependent of dependents) {
if (data[i][dependent] !== record[dependent]) {
shouldMerge = false;
export default defineComponent({
name: 'EnvironmentTable',
setup() {
let data = ref<any[]>([]);
let columns = ref<TableColumnType[]>([]);
interface PageData {
tableList: any[];
tableColumns: any[];
graphList: any[];
}
const pageData = inject<PageData>('pageData');
if (!pageData) {
throw new Error('pageData is not provided');
}
// 监听 pageData 的变化
watch(
() => pageData as PageData,
(_newValue, _oldValue) => {
data.value = pageData.tableList;
let columnA: any[] = [...column];
columnA.push(...pageData.tableColumns);
columns.value = columnA;
// pageData.graphList;
// 执行你的逻辑代码
},
{ deep: true },
);
const getRowSpan = (dataIndex: string, record: any, data: any, dependents: string[] = []) => {
let rowSpan = 1;
for (let i = data.indexOf(record) + 1; i < data.length; i++) {
let shouldMerge = true;
for (const dependent of dependents) {
if (data[i][dependent] !== record[dependent]) {
shouldMerge = false;
break;
}
}
if (shouldMerge && data[i][dataIndex] === record[dataIndex]) {
rowSpan++;
} else {
break;
}
}
if (shouldMerge && data[i][dataIndex] === record[dataIndex]) {
rowSpan++;
} else {
break;
}
}
return rowSpan;
};
return rowSpan;
};
const columns: TableColumnType[] = [
{
title: '序号',
dataIndex: 'key',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('name', record, data);
if (rowIndex != 0 && data[rowIndex-1].key == record.key) {
return {
rowSpan: 0,
colSpan: 0,
const column: TableColumnType[] = [
{
title: '序号',
dataIndex: 'key',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '设备名称',
dataIndex: 'name',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('name', record, data);
if (rowIndex != 0 && data[rowIndex-1].name == record.name) {
return {
rowSpan: 0,
colSpan: 0,
const rowSpan = getRowSpan('name', record, data.value);
if (rowIndex != 0 && data.value[rowIndex - 1].key == record.key) {
return {
rowSpan: 0,
colSpan: 0,
};
}
}
return {
rowSpan: rowSpan,
};
// if (rowIndex === data.indexOf(record)) {
// return {
// rowSpan: rowSpan,
// };
// }
// return {
// rowSpan: 0,
// colSpan: 0,
// };
},
},
{
title: '设备点位',
dataIndex: 'position',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('position', record, data, ['name']);
if (rowIndex != 0 && data[rowIndex-1].name == record.name && data[rowIndex-1].position == record.position) {
return {
rowSpan: 0,
colSpan: 0,
}
}
return {
rowSpan: rowSpan,
};
// if (rowIndex === data.indexOf(record)) {
// return {
// rowSpan: rowSpan,
// };
// }
// return {
// rowSpan: 0,
// colSpan: 0,
// };
rowSpan: rowSpan,
};
},
},
},
{
title: '计量单位',
dataIndex: 'unit',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('unit', record, data, ['name', 'position']);
if (rowIndex != 0 && data[rowIndex-1].name == record.name && data[rowIndex-1].position == record.position && data[rowIndex-1].unit == record.unit) {
{
title: '设备名称',
dataIndex: 'name',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
const rowSpan = getRowSpan('name', record, data.value);
if (rowIndex != 0 && data.value[rowIndex - 1].name == record.name) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: 0,
colSpan: 0,
}
}
return {
rowSpan: rowSpan,
};
// if (rowIndex === data.indexOf(record)) {
// return {
// rowSpan: rowSpan,
// };
// }
// return {
// rowSpan: 0,
// colSpan: 0,
// };
rowSpan: rowSpan,
};
},
},
},
{
title: '日期',
dataIndex: 'date',
},
{
title: '1:00',
dataIndex: '1:00',
},
];
{
title: '设备点位',
dataIndex: 'position',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
const rowSpan = getRowSpan('position', record, data.value, ['name']);
if (
rowIndex != 0 &&
data.value[rowIndex - 1].name == record.name &&
data.value[rowIndex - 1].position == record.position
) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '计量单位',
dataIndex: 'unit',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
const rowSpan = getRowSpan('unit', record, data.value, ['name', 'position']);
if (
rowIndex != 0 &&
data.value[rowIndex - 1].name == record.name &&
data.value[rowIndex - 1].position == record.position &&
data.value[rowIndex - 1].unit == record.unit
) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
];
return {
data,
columns,
};
},
});
onMounted(() => {
data.value = pageData.tableList;
let columnA: any[] = [...column];
columnA.push(...pageData.tableColumns);
columns.value = columnA;
});
return {
data,
column,
columns,
pageData,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@@ -1,3 +1,4 @@
<!-- eslint-disable vue/v-on-event-hyphenation -->
<template>
<div class="parent-container">
<div class="ns-tree-title">
@@ -6,29 +7,20 @@
<a-tree-select
v-model:value="value"
style="width: 100%"
:tree-line="treeLine && { showLeafIcon }"
:tree-data="treeData1"
>
<!-- <template #title="{ value: val, title }">
<b v-if="val === 'parent 1-1'" style="color: #08c">sss</b>
<template v-else>{{ title }}</template>
</template> -->
</a-tree-select>
@change="changeDeviceType" />
<a-tree
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
checkable
style="width: 100%; margin-bottom: 10px;"
:tree-data="treeData2"
>
<!-- <template #title="{ title, key }">
<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
<template v-else>{{ title }}</template>
</template> -->
</a-tree>
<a-spin :spinning="treeLoading">
<a-tree
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
checkable
:height="340"
style="width: 100%; overflow-y: auto; margin-bottom: 10px; margin-top: 10px"
:tree-data="treeData2" />
</a-spin>
<div class="fixed-bottom">
<a-divider />
@@ -36,190 +28,505 @@
ref="select"
v-model:value="selectedValue"
placeholder="请选择点位"
style="width: 100%; margin-bottom: 10px;"
:options="options1"
></a-select>
style="width: 100%; margin-bottom: 10px"
:options="options1" />
<a-select
v-model:value="frequencyValue"
placeholder="请选择频率"
style="width: 100%; margin-bottom: 10px;"
:options="options2"
></a-select>
<a-range-picker v-model:value="dateRange" style="width: 100%; margin-bottom: 10px;" :placeholder="['请选择日期','请选择日期']"/>
<a-button type="primary" style="width: 100%; margin-bottom: 10px;" @click="getDianWeiList">查询</a-button>
style="width: 100%; margin-bottom: 10px"
:options="options2" />
<a-range-picker
:value="hackValue || dateRange"
:disabled-date="disabledDate"
@change="onChange"
@openChange="onOpenChange"
@calendarChange="onCalendarChange"
style="width: 100%; margin-bottom: 10px"
:placeholder="['请选择日期', '请选择日期']" />
<a-button type="primary" style="width: 100%; margin-bottom: 10px" @click="getSelect">
查询
</a-button>
<a-button type="primary" style="width: 100%; margin-bottom: 10px" @click="getSelect11">
模拟不同数据查询
</a-button>
</div>
</div>
</template>
<script lang="ts">
import type { TreeSelectProps, TreeProps, SelectProps } from 'ant-design-vue';
import { defineComponent, ref, watch ,onMounted } from 'vue';
import dayjs, { Dayjs } from 'dayjs';
import type { TreeSelectProps, SelectProps } from 'ant-design-vue';
import { defineComponent, ref, onMounted } from 'vue';
import { Dayjs } from 'dayjs';
import { inject } from 'vue';
import { http } from '/nerv-lib/util';
import { device } from '/@/api/deviceManage';
const treeData2: TreeProps['treeData'] = [
{
title: 'AC_001(总电表)',
key: '1',
children: [
{
title: 'AC_002(暖通电表)',
key: '2',
},
{
title: 'AC_003(照明电表)',
key: '3',
},
{
title: 'AC_004(给排水电表)',
key: '4',
},
{
title: 'AC_005(通风电表)',
key: '5',
},
{
title: 'AC_006(电动门电表)',
key: '6',
},
],
},
];
export default defineComponent({
// eslint-disable-next-line vue/multi-word-component-names
name: 'Tree',
setup() {
const treeLoading = ref(false);
const treeLine = ref(true);
const showLeafIcon = ref(false);
const value = ref<string>();
export default defineComponent({
name: 'Tree',
setup() {
const treeLine = ref(true);
const showLeafIcon = ref(false);
const value = ref<string>();
const orgId = ref('');
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
orgId.value = result;
const treeData1 = ref<TreeSelectProps['treeData']>([
{
title: '3.电梯',
value: '3',
children: [
const treeData1 = ref<TreeSelectProps['treeData']>([]);
http.post(device.queryDeviceTree, { orgId: orgId.value }).then((res) => {
treeData1.value = formatTreeData(res.data);
});
const formatTreeData = (data: any) => {
return data.map((item: any) => ({
title: item.code + '.' + item.deviceType,
value: item.code,
children: item.children ? formatTreeData(item.children) : [],
}));
};
const treeData2 = ref<TreeSelectProps['treeData']>([]);
const changeDeviceType = (val: any, label: any) => {
treeLoading.value = true;
http
.post(device.queryDevicePage, {
code: val,
orgId: orgId.value,
pageNum: 1,
pageSize: 1000,
})
.then((res) => {
if (!val) {
val = '999999999';
}
if (!label) {
label = ['所有设备'];
}
let records = res.data.records;
records.forEach((item: any) => {
(item.title = item.deviceModel + ' ' + item.deviceName), (item.key = item.id);
});
let a: TreeSelectProps['treeData'] = [{ title: label[0], key: val, children: records }];
treeData2.value = a;
expandedKeys.value = [val];
})
.finally(() => {
treeLoading.value = false;
});
};
const expandedKeys = ref<string[]>([]);
const selectedKeys = ref<string[]>([]);
const checkedKeys = ref<string[]>([]);
const options1 = ref<SelectProps['options']>([]);
const options2 = ref<SelectProps['options']>([
{
value: '1',
label: '5分钟',
},
{
value: '2',
label: '10分钟',
},
{
value: '3',
label: '30分钟',
},
{
value: '4',
label: '1小时',
},
]);
const selectedValue = ref<string | undefined>();
const frequencyValue = ref<string | undefined>();
const dateRange = ref<[Dayjs, Dayjs] | undefined>();
const getDianWeiList = () => {
console.log('getDianWeiList 被调用');
options1.value = [
{ value: '1', label: 'A 项电压' },
{ value: '2', label: 'B 项电压' },
{ value: '3', label: 'C 项电压' },
{ value: '4', label: 'AB 线电压' },
{ value: '5', label: 'BC 线电压' },
{ value: '6', label: 'A 项电流' },
{ value: '7', label: 'B 项电流' },
];
};
interface PageData {
tableList: any[];
tableColumns: any[];
graphList: any[];
}
const pageData = inject<PageData>('pageData');
if (!pageData) {
throw new Error('pageData is not provided');
}
// 查询数据后放入pageData
const getSelect = () => {
pageData.tableList = [
{
title: '301.扶梯',
value: '301',
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
title: '302.直梯',
value: '302',
},
],
},
{
title: '4.冷热源',
value: '4',
children: [
{
title: '401.冷水机组',
value: '401',
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
title: '402.热泵机组',
value: '402',
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
title: '403.锅炉',
value: '403',
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
title: '404.水处理机组',
value: '404',
key: '3',
name: 'AC_004给排水电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
],
},
]);
];
pageData.tableColumns = [
{
title: '日期',
dataIndex: 'date',
},
{
title: '1:00',
dataIndex: '1:00',
},
];
pageData.graphList = [
{
date: '2023-12-01 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '21',
},
{
name: 'AC_003照明电表',
value: '36',
},
{
name: 'AC_004给排水电表',
value: '5',
},
],
},
{
date: '2023-12-02 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '26',
},
{
name: 'AC_003照明电表',
value: '25',
},
{
name: 'AC_004给排水电表',
value: '47',
},
],
},
{
date: '2023-12-03 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '18',
},
{
name: 'AC_003照明电表',
value: '22',
},
{
name: 'AC_004给排水电表',
value: '26',
},
],
},
{
date: '2023-12-04 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '40',
},
{
name: 'AC_003照明电表',
value: '15',
},
{
name: 'AC_004给排水电表',
value: '12',
},
],
},
{
date: '2023-12-05 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '15',
},
{
name: 'AC_003照明电表',
value: '18',
},
{
name: 'AC_004给排水电表',
value: '15',
},
],
},
{
date: '2023-12-06 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '15',
},
{
name: 'AC_003照明电表',
value: '18',
},
{
name: 'AC_004给排水电表',
value: '15',
},
],
},
];
};
// 测试查询 模拟不同数据
const getSelect11 = () => {
pageData.tableList = [
{
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
'2:00': '2222',
},
{
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
'2:00': '2222',
},
{
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
'2:00': '2222',
},
{
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
'2:00': '2222',
},
{
key: '3',
name: 'AC_004给排水电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
'2:00': '2222',
},
];
pageData.tableColumns = [
{
title: '日期',
dataIndex: 'date',
},
{
title: '1:00',
dataIndex: '1:00',
},
{
title: '2:00',
dataIndex: '2:00',
},
];
pageData.graphList = [
{
date: '2023-12-01 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '21',
},
{
name: 'AC_003照明电表',
value: '36',
},
{
name: 'AC_004给排水电表',
value: '5',
},
{
name: 'AC_0051111111',
value: '14',
},
],
},
{
date: '2023-12-02 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '26',
},
{
name: 'AC_003照明电表',
value: '25',
},
{
name: 'AC_004给排水电表',
value: '47',
},
{
name: 'AC_0051111111',
value: '28',
},
],
},
];
};
const expandedKeys = ref<string[]>(['0-0-0', '0-0-1']);
const selectedKeys = ref<string[]>(['0-0-0', '0-0-1']);
const checkedKeys = ref<string[]>(['0-0-0', '0-0-1']);
type RangeValue = [Dayjs, Dayjs];
const dates = ref<RangeValue>();
const hackValue = ref<RangeValue>();
const options1 = ref<SelectProps['options']>([]);
const options2 = ref<SelectProps['options']>([
{
value: '1',
label: '5分钟',
},
{
value: '2',
label: '10分钟',
},
{
value: '3',
label: '30分钟',
},
{
value: '4',
label: '1小时',
},
]);
const selectedValue = ref<string | undefined>();
const frequencyValue = ref<string | undefined>();
const dateRange = ref<[Dayjs, Dayjs] | undefined>();
const onChange = (val: RangeValue) => {
dateRange.value = val;
};
const onOpenChange = (open: boolean) => {
if (open) {
dates.value = [] as any;
hackValue.value = [] as any;
} else {
hackValue.value = undefined;
}
};
const disabledDate = (current: Dayjs) => {
if (!dates.value || (dates.value as any).length === 0) {
return false;
}
const tooLate = dates.value[0] && current.diff(dates.value[0], 'days') > 2;
const tooEarly = dates.value[1] && dates.value[1].diff(current, 'days') > 2;
return tooEarly || tooLate;
};
const onCalendarChange = (val: RangeValue) => {
dates.value = val;
};
const getDianWeiList = () => {
console.log('getDianWeiList 被调用');
options1.value = [
{ value: '1', label: 'A 项电压' },
{ value: '2', label: 'B 项电压' },
{ value: '3', label: 'C 项电压' },
{ value: '4', label: 'AB 线电压' },
{ value: '5', label: 'BC 线电压' },
{ value: '6', label: 'A 项电流' },
{ value: '7', label: 'B 项电流' },
];
};
onMounted(() => {
getDianWeiList();
changeDeviceType(null, null);
});
onMounted(() => {
getDianWeiList();
});
const dateFormat = 'YYYY-MM-DD';
return {
treeLine,
showLeafIcon,
value,
treeData1,
treeData2,
expandedKeys,
selectedKeys,
checkedKeys,
options1,
options2,
selectedValue,
frequencyValue,
dateRange,
getDianWeiList,
};
},
});
return {
treeLine,
showLeafIcon,
value,
treeData1,
treeData2,
expandedKeys,
selectedKeys,
checkedKeys,
options1,
options2,
selectedValue,
frequencyValue,
dateRange,
getDianWeiList,
getSelect,
getSelect11,
disabledDate,
onCalendarChange,
onOpenChange,
onChange,
hackValue,
pageData,
changeDeviceType,
treeLoading,
};
},
});
</script>
<style lang="less" scoped>
.ns-tree-title {
user-select: text;
margin-bottom: 5px;
padding-bottom: 10px;
padding-top: 10px;
border-bottom: 1px solid #e9e9e9;
> span {
padding-left: 10px;
line-height: 20px;
.ns-tree-title {
user-select: text;
margin-bottom: 5px;
padding-bottom: 10px;
padding-top: 10px;
border-bottom: 1px solid #e9e9e9;
> span {
padding-left: 10px;
line-height: 20px;
}
}
.parent-container {
position: relative;
height: 100%;
}
}
.parent-container {
position: relative;
height: 100%;
}
.fixed-bottom {
position: absolute;
bottom: 0;
width: 100%;
margin-bottom: 10px;
}
.fixed-bottom {
position: absolute;
bottom: 0;
width: 100%;
margin-bottom: 10px;
}
</style>

View File

@@ -0,0 +1,126 @@
<template>
<div ref="graphGraphchart" style="width: 100%; height: 80%"></div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, inject, watch } from 'vue';
import * as echarts from 'echarts';
export default defineComponent({
name: 'GraphGraph',
setup() {
let data = ref<any[]>([]);
interface PageData {
graphTableList: any[];
graphTableColumns: any[];
graphGraphList: any[];
}
const pageData = inject<PageData>('pageData');
if (!pageData) {
throw new Error('pageData is not provided');
}
// 监听 pageData 的变化
watch(
() => pageData as PageData,
(_newValue, _oldValue) => {
// 执行你的逻辑代码
draw();
},
{ deep: true },
);
const graphGraphchart = ref(null);
let chartInstance: echarts.ECharts | null = null;
const draw = () => {
data.value = pageData.graphGraphList;
if (chartInstance) {
chartInstance.dispose();
}
chartInstance = echarts.init(graphGraphchart.value);
var seriesList = [];
var date = [];
var legendList: string | any[] = [];
for (let i = 0; i < data.value.length; i++) {
date.push(data.value[i].date);
for (let j = 0; j < data.value[i].data.length; j++) {
if (seriesList.length < j + 1) {
seriesList.push({
name: data.value[i].data[j].name,
data: [data.value[i].data[j].value],
type: 'line',
smooth: true,
});
} else {
seriesList[j].data.push(data.value[i].data[j].value);
}
if (legendList.length == 0 || legendList.length < j + 1) {
legendList.push(data.value[i].data[j].name);
}
}
}
const option = {
legend: {
data: legendList,
orient: 'horizontal',
bottom: 0,
left: 60,
},
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
const date = params[0].name;
const values = params
.map((param: any) => {
const unit = data.value.find((d) => d.date === date)?.unit || '';
return `<tr>
<td>${param.marker}${param.seriesName}</td>
<td style="text-align: right;">${param.value} ${unit}</td>
</tr>`;
})
.join('');
return `<div>${date}</div><table style="width: 100%;">${values}</table>`;
},
},
xAxis: {
type: 'category',
data: date,
},
yAxis: {
type: 'value',
},
series: seriesList,
};
chartInstance = echarts.init(graphGraphchart.value);
chartInstance.setOption(option);
};
onMounted(() => {
draw();
});
// 下载图表
const downloadChart = () => {
if (chartInstance) {
const base64 = chartInstance.getDataURL({
type: 'png',
backgroundColor: '#fff',
});
const link = document.createElement('a');
link.href = base64;
link.download = 'chart.png';
link.click();
}
};
return {
graphGraphchart,
downloadChart,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,122 @@
<template>
<a-table :columns="columns" :data-source="data" bordered />
</template>
<script lang="ts">
import { defineComponent, watch, inject, ref, onMounted } from 'vue';
import type { TableColumnType } from 'ant-design-vue';
export default defineComponent({
name: 'EnvironmentTable',
setup() {
let data = ref<any[]>([]);
let columns = ref<TableColumnType[]>([]);
interface PageData {
graphTableList: any[];
graphTableColumns: any[];
graphGraphList: any[];
}
const pageData = inject<PageData>('pageData');
if (!pageData) {
throw new Error('pageData is not provided');
}
// 监听 pageData 的变化
watch(
() => pageData as PageData,
(_newValue, _oldValue) => {
data.value = pageData.graphTableList;
let columnA: any[] = [...column];
columnA.push(...pageData.graphTableColumns);
columns.value = columnA;
// pageData.graphList;
// 执行你的逻辑代码
},
{ deep: true },
);
const getRowSpan = (dataIndex: string, record: any, data: any, dependents: string[] = []) => {
let rowSpan = 1;
for (let i = data.indexOf(record) + 1; i < data.length; i++) {
let shouldMerge = true;
for (const dependent of dependents) {
if (data[i][dependent] !== record[dependent]) {
shouldMerge = false;
break;
}
}
if (shouldMerge && data[i][dataIndex] === record[dataIndex]) {
rowSpan++;
} else {
break;
}
}
return rowSpan;
};
const column: TableColumnType[] = [
{
title: '设备/组名',
dataIndex: 'name',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
const rowSpan = getRowSpan('name', record, data.value);
if (rowIndex != 0 && data.value[rowIndex - 1].name == record.name) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '参数名称',
dataIndex: 'paramName',
customCell: (record, rowIndex) => {
if (rowIndex == undefined) {
return {
rowSpan: 0,
colSpan: 0,
};
}
const rowSpan = getRowSpan('paramName', record, data.value, ['name']);
if (rowIndex != 0 && data.value[rowIndex - 1].name == record.name) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
];
onMounted(() => {
data.value = pageData.graphTableList;
let columnA: any[] = [...column];
columnA.push(...pageData.graphTableColumns);
columns.value = columnA;
});
return {
data,
column,
columns,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,19 @@
<!-- eslint-disable vue/multi-word-component-names -->
<template>
<page />
</template>
<script setup>
import { reactive, provide } from 'vue';
import page from './page.vue';
// 创建一个响应式对象
const pageData = reactive({
graphTableList: [],
graphTableColumns: [],
graphGraphList: [],
});
// 使用 provide 函数来提供这个响应式对象
provide('pageData', pageData);
</script>

View File

@@ -0,0 +1,88 @@
<!-- eslint-disable vue/multi-word-component-names -->
<template>
<a-tabs v-model:activeKey="activeKey" style="height: 8%">
<a-tab-pane key="1" tab="图表" />
<a-tab-pane key="2" tab="分析" />
</a-tabs>
<a-row type="flex" style="height: 92%">
<a-col :span="4">
<div style="padding: 0 20px; width: 100%; height: 100%">
<tree ref="treeRef" />
</div>
</a-col>
<a-col :span="20">
<div style="width: 100%; height: 100%">
<div class="ns-right-title">
<span>统计数据</span>
<div class="button">
<ns-icon name="xiazai" size="18" style="margin-right: 10px" @click="downloadChart" />
<ns-icon :name="iconName" size="18" style="margin-right: 10px" @click="change" />
</div>
</div>
<div v-if="activeKey == '1'" style="height: 90%">
<graph-graph ref="graphRef" v-if="isGraph" />
<environment-table ref="tableRef" v-else />
</div>
</div>
</a-col>
</a-row>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import tree from './tree/index.vue';
import graphGraph from './graphGraph/index.vue';
import environmentTable from './graphTable/index.vue';
const iconName = ref('biaoge');
const treeRef = ref();
const graphRef = ref();
const tableRef = ref();
let isGraph = ref(true);
const activeKey = ref('1');
defineOptions({
name: 'EnvironmentMonitorIndex', // 与页面路由name一致缓存才可生效
});
const downloadChart = () => {
if (activeKey.value == '1' && isGraph) {
if (graphRef.value) {
graphRef.value.downloadChart();
}
}
};
function change() {
isGraph.value = !isGraph.value;
if (iconName.value == 'biaoge') {
iconName.value = 'bingtu';
} else {
iconName.value = 'biaoge';
}
}
</script>
<style lang="less" scoped>
.ns-right-title {
display: flex;
justify-content: space-between;
align-items: center;
user-select: text;
margin-bottom: 5px;
padding-bottom: 10px;
padding-top: 10px;
border-bottom: 1px solid #e9e9e9;
> span {
padding-left: 10px;
line-height: 20px;
}
}
.button {
display: inline-block;
padding-right: 10px;
}
</style>

View File

@@ -0,0 +1,166 @@
<template>
<a-table :columns="columns" :data-source="data" bordered />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import type { TableColumnType } from 'ant-design-vue';
const data = [
{
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '1',
name: 'AC_002暖通电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '3',
name: 'AC_004给排水电表',
position: 'A 相电压',
unit: 'V',
date: '2023-12-01',
'1:00': '3626',
},
];
export default defineComponent({
name: 'EnvironmentTable',
setup() {
const getRowSpan = (dataIndex: string, record, data, dependents: string[] = []) => {
let rowSpan = 1;
for (let i = data.indexOf(record) + 1; i < data.length; i++) {
let shouldMerge = true;
for (const dependent of dependents) {
if (data[i][dependent] !== record[dependent]) {
shouldMerge = false;
break;
}
}
if (shouldMerge && data[i][dataIndex] === record[dataIndex]) {
rowSpan++;
} else {
break;
}
}
return rowSpan;
};
const columns: TableColumnType[] = [
{
title: '序号',
dataIndex: 'key',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('name', record, data);
if (rowIndex != 0 && data[rowIndex - 1].key == record.key) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '设备名称',
dataIndex: 'name',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('name', record, data);
if (rowIndex != 0 && data[rowIndex - 1].name == record.name) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '设备点位',
dataIndex: 'position',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('position', record, data, ['name']);
if (
rowIndex != 0 &&
data[rowIndex - 1].name == record.name &&
data[rowIndex - 1].position == record.position
) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '计量单位',
dataIndex: 'unit',
customCell: (record, rowIndex) => {
const rowSpan = getRowSpan('unit', record, data, ['name', 'position']);
if (
rowIndex != 0 &&
data[rowIndex - 1].name == record.name &&
data[rowIndex - 1].position == record.position &&
data[rowIndex - 1].unit == record.unit
) {
return {
rowSpan: 0,
colSpan: 0,
};
}
return {
rowSpan: rowSpan,
};
},
},
{
title: '日期',
dataIndex: 'date',
},
{
title: '1:00',
dataIndex: '1:00',
},
];
return {
data,
columns,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,620 @@
<!-- eslint-disable vue/v-on-event-hyphenation -->
<template>
<div class="parent-container">
<div class="ns-tree-title">
<span>数据点位</span>
</div>
<a-select
ref="select"
v-model:value="selectedValue"
placeholder="请选择能耗类型"
style="width: 100%; margin-bottom: 10px"
:options="options1"
@change="changeEnergyType" />
<a-radio-group
v-model:value="mode"
@change="changeMode"
style="padding-bottom: 10px; width: 100%">
<a-radio-button value="1" style="width: 50%; text-align: center" :disabled="shebei"
>设备</a-radio-button
>
<a-radio-button value="2" style="width: 50%; text-align: center">节点</a-radio-button>
</a-radio-group>
<a-input
v-model:value="deviceName"
placeholder="请输入设备名称"
v-if="mode == '1'"
@change="changeMode" />
<a-input v-model:value="pointName" placeholder="请输入节点名称" v-else @change="changeMode" />
<a-spin :spinning="treeLoading">
<a-tree
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
checkable
:height="300"
style="width: 100%; overflow-y: auto; margin-bottom: 10px; margin-top: 10px"
:tree-data="treeData2" />
</a-spin>
<div class="fixed-bottom">
<a-divider />
<a-select
v-model:value="frequencyValue"
placeholder="请选择采集频率"
style="width: 100%; margin-bottom: 10px"
:options="options2" />
<a-range-picker
:value="hackValue || dateRange"
:disabled-date="disabledDate"
@change="onChange"
@openChange="onOpenChange"
@calendarChange="onCalendarChange"
style="width: 100%; margin-bottom: 10px"
:placeholder="['请选择日期', '请选择日期']" />
<a-button type="primary" style="width: 100%; margin-bottom: 10px" @click="getSelect">
查询
</a-button>
<!-- <a-button type="primary" style="width: 100%; margin-bottom: 10px" @click="getSelect11">
模拟不同数据查询
</a-button> -->
</div>
</div>
</template>
<script lang="ts">
import type { TreeSelectProps, SelectProps } from 'ant-design-vue';
import { defineComponent, ref, onMounted, inject } from 'vue';
import { Dayjs } from 'dayjs';
import { http } from '/nerv-lib/util';
import { device, group } from '/@/api/deviceManage';
import { dict } from '/@/api';
export default defineComponent({
// eslint-disable-next-line vue/multi-word-component-names
name: 'Tree',
setup() {
const value = ref<string>();
const deviceName = ref<string>();
const pointName = ref<string>();
const treeLoading = ref(false);
const shebei = ref(false);
// 树打开节点
const expandedKeys = ref<string[]>([]);
// 树勾选节点
const selectedKeys = ref<string[]>([]);
// 树点击节点
const checkedKeys = ref<string[]>([]);
// 能耗类型list
const options1 = ref<SelectProps['options']>([]);
// 采集频率list
const options2 = ref<SelectProps['options']>([]);
const mode = ref<String>('1');
// 能耗类型
const selectedValue = ref<string | undefined>();
// 采集频率
const frequencyValue = ref<string | undefined>();
// 时间
const dateRange = ref<[Dayjs, Dayjs] | undefined>();
// 页面初始化参数
const getOptionsList = async () => {
try {
const options = await dict({ params: { dicKey: 'ENERGY_TYPE' } });
options.data.data.forEach((item: any) => {
(item.value = item.cnValue), (item.label = item.cnValue);
});
options1.value = options.data.data; // 根据返回数据的结构来更新 options1.value
} catch (error) {
console.error('Failed to fetch options:', error);
}
options2.value = [
{
value: '1',
label: '5分钟',
},
{
value: '2',
label: '10分钟',
},
{
value: '3',
label: '30分钟',
},
{
value: '4',
label: '1小时',
},
];
};
interface PageData {
graphTableList: any[];
graphTableColumns: any[];
graphGraphList: any[];
}
const pageData = inject<PageData>('pageData');
if (!pageData) {
throw new Error('pageData is not provided');
}
// 选择能耗类型
const changeEnergyType = () => {
if (selectedValue.value == '8') {
shebei.value = true;
mode.value = '2';
} else {
shebei.value = false;
}
changeMode();
};
const getSelect = () => {
pageData.graphTableList = [
{
key: '1',
name: 'AC_002暖通电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '1',
name: 'AC_002暖通电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
},
{
key: '3',
name: 'AC_004给排水电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
},
];
pageData.graphTableColumns = [
{
title: '日期',
dataIndex: 'date',
},
{
title: '1:00',
dataIndex: '1:00',
},
];
pageData.graphGraphList = [
{
date: '2023-12-01 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '21',
},
{
name: 'AC_003照明电表',
value: '36',
},
{
name: 'AC_004给排水电表',
value: '5',
},
],
},
{
date: '2023-12-02 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '26',
},
{
name: 'AC_003照明电表',
value: '25',
},
{
name: 'AC_004给排水电表',
value: '47',
},
],
},
{
date: '2023-12-03 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '18',
},
{
name: 'AC_003照明电表',
value: '22',
},
{
name: 'AC_004给排水电表',
value: '26',
},
],
},
{
date: '2023-12-04 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '40',
},
{
name: 'AC_003照明电表',
value: '15',
},
{
name: 'AC_004给排水电表',
value: '12',
},
],
},
{
date: '2023-12-05 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '15',
},
{
name: 'AC_003照明电表',
value: '18',
},
{
name: 'AC_004给排水电表',
value: '15',
},
],
},
];
};
const getSelect11 = () => {
pageData.graphTableList = [
{
key: '1',
name: 'AC_002暖通电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
'2:00': '3626',
},
{
key: '1',
name: 'AC_002暖通电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
'2:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
'2:00': '3626',
},
{
key: '2',
name: 'AC_003照明电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
'2:00': '3626',
},
{
key: '3',
name: 'AC_004给排水电表',
paramName: '用电量(kWh)',
date: '2023-12-01',
'1:00': '3626',
'2:00': '3626',
},
];
pageData.graphTableColumns = [
{
title: '日期',
dataIndex: 'date',
},
{
title: '1:00',
dataIndex: '1:00',
},
{
title: '2:00',
dataIndex: '2:00',
},
];
pageData.graphGraphList = [
{
date: '2023-12-01 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '21',
},
{
name: 'AC_003照明电表',
value: '36',
},
{
name: 'AC_004给排水电表',
value: '5',
},
{
name: 'AC_005333333333',
value: '22',
},
],
},
{
date: '2023-12-02 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '26',
},
{
name: 'AC_003照明电表',
value: '25',
},
{
name: 'AC_004给排水电表',
value: '47',
},
{
name: 'AC_005333333333',
value: '28',
},
],
},
{
date: '2023-12-03 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '18',
},
{
name: 'AC_003照明电表',
value: '22',
},
{
name: 'AC_004给排水电表',
value: '26',
},
{
name: 'AC_005333333333',
value: '44',
},
],
},
{
date: '2023-12-04 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '40',
},
{
name: 'AC_003照明电表',
value: '15',
},
{
name: 'AC_004给排水电表',
value: '12',
},
{
name: 'AC_005333333333',
value: '26',
},
],
},
{
date: '2023-12-05 0:00',
unit: 'V',
data: [
{
name: 'AC_002暖通电表',
value: '15',
},
{
name: 'AC_003照明电表',
value: '18',
},
{
name: 'AC_004给排水电表',
value: '15',
},
{
name: 'AC_005333333333',
value: '35',
},
],
},
];
};
const orgId = ref('');
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
orgId.value = result;
const treeData2 = ref<TreeSelectProps['treeData']>([]);
// 切换树结构
const changeMode = () => {
treeLoading.value = true;
if (mode.value == '1') {
http
.post(device.queryDevicePage, {
deviceName: deviceName.value,
energyType: selectedValue.value,
orgId: orgId.value,
pageNum: 1,
pageSize: 9999,
})
.then((res) => {
let val = '999999999';
let label = '所有设备';
let records = res.data.records;
records.forEach((item: any) => {
(item.title = item.deviceModel + ' ' + item.deviceName), (item.key = item.id);
});
let a: TreeSelectProps['treeData'] = [{ title: label, key: val, children: records }];
treeData2.value = a;
expandedKeys.value = [val];
})
.finally(() => {
treeLoading.value = false;
});
} else {
http
.post(group.queryDeviceGroupTree, {
pointName: pointName.value,
energyType: selectedValue.value,
orgId: orgId.value,
})
.then((res) => {
treeData2.value = [];
treeData2.value = formatTreeData(pointName.value ? res.data[0].searchList : res.data);
expandedKeys.value = getAllKeys(treeData2.value);
})
.finally(() => {
treeLoading.value = false;
});
}
};
const getAllKeys = (data: any) => {
let keys: any[] = [];
data.forEach((item: any) => {
if (item.children) {
keys.push(item.key);
keys = keys.concat(getAllKeys(item.children));
}
});
return keys;
};
const formatTreeData = (data: any) => {
return data.map((item: any) => ({
title: item.pointName,
key: item.id,
children: item.children ? formatTreeData(item.children) : [],
}));
};
type RangeValue = [Dayjs, Dayjs];
const dates = ref<RangeValue>();
const hackValue = ref<RangeValue>();
const disabledDate = (current: Dayjs) => {
if (!dates.value || (dates.value as any).length === 0) {
return false;
}
const tooLate = dates.value[0] && current.diff(dates.value[0], 'days') > 2;
const tooEarly = dates.value[1] && dates.value[1].diff(current, 'days') > 2;
return tooEarly || tooLate;
};
const onOpenChange = (open: boolean) => {
if (open) {
dates.value = [] as any;
hackValue.value = [] as any;
} else {
hackValue.value = undefined;
}
};
const onChange = (val: RangeValue) => {
dateRange.value = val;
};
const onCalendarChange = (val: RangeValue) => {
dates.value = val;
};
onMounted(() => {
getOptionsList();
changeMode();
});
return {
value,
deviceName,
pointName,
treeData2,
expandedKeys,
selectedKeys,
checkedKeys,
options1,
options2,
mode,
selectedValue,
frequencyValue,
dateRange,
getOptionsList,
getSelect,
getSelect11,
changeMode,
disabledDate,
onCalendarChange,
onOpenChange,
onChange,
hackValue,
treeLoading,
changeEnergyType,
shebei,
};
},
});
</script>
<style lang="less" scoped>
.ns-tree-title {
user-select: text;
margin-bottom: 5px;
padding-bottom: 10px;
padding-top: 10px;
border-bottom: 1px solid #e9e9e9;
> span {
padding-left: 10px;
line-height: 20px;
}
}
.parent-container {
position: relative;
height: 100%;
}
.fixed-bottom {
position: absolute;
bottom: 0;
width: 100%;
margin-bottom: 10px;
}
</style>

View File

@@ -8,6 +8,7 @@
<a-col :span="8" class="tree">
<a-tree
ref="treeRef"
:showLine="{ showLeafIcon: false }"
v-if="deptTreeData?.length"
:tree-data="deptTreeData"
:selectedKeys="deptTreeSelectedKeys"
@@ -87,6 +88,7 @@
</a-space>
<a-tree
v-if="roleTreeData.length"
:showLine="{ showLeafIcon: false }"
:tree-data="roleTreeData"
:selectedKeys="roleTreeSelectedKeys"
blockNode
@@ -192,7 +194,7 @@
const selectRoleRef = ref();
const formSchema = formConfig(disabled as any);
const formSchema2 = formConfig2(roleDisabled as any);
const { orgName, projectId, orgId } = JSON.parse(sessionStorage.getItem('userInfo'));
const { orgName, projectId, orgId } = JSON.parse(sessionStorage.getItem(import.meta.env.VITE_PUBLIC_PATH));
// const orgId = JSON.parse(sessionStorage.getItem('ORGID'));
const roleTreeData = ref([]);
const rolePermissionTreeData = ref([]);

View File

@@ -89,7 +89,7 @@ export const formConfig = (disabled) => {
label: '组织关系',
field: 'orgName',
component: 'NsInput',
defaultValue: JSON.parse(sessionStorage.getItem('userInfo')).orgName,
defaultValue: JSON.parse(sessionStorage.getItem(import.meta.env.VITE_PUBLIC_PATH)).orgName,
componentProps: {
disabled: true,
maxLength: 30,

View File

@@ -26,6 +26,7 @@
</div>
<a-drawer
:width="500"
title=" "
:visible="visible"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
@@ -103,7 +104,7 @@
const treeDataDept = ref([]);
const treeData2 = ref([]);
const userAuthList = ref([]);
// const orgId = JSON.parse(sessionStorage.getItem('userInfo')).orgId;
// const orgId = JSON.parse(sessionStorage.getItem(import.meta.env.VITE_PUBLIC_PATH)).orgId;
const orgId = JSON.parse(sessionStorage.getItem('ORGID')!);
const orgTreeConfig = ref({
selectedKeys: ['0-0'],
@@ -290,7 +291,7 @@
setTimeout(() => {
formData.value = {
sex: '男',
orgName: JSON.parse(sessionStorage.getItem('userInfo')).orgName,
orgName: JSON.parse(sessionStorage.getItem(import.meta.env.VITE_PUBLIC_PATH)).orgName,
};
userAuthList.value.splice(0);
});
@@ -545,7 +546,8 @@
{
label: '新增',
name: 'userAdd',
type: 'primary',
style: { marginBottom: '16px' },
// type: 'primary',
handle: () => {
addformvisible.value = true;
},
@@ -644,6 +646,9 @@
:deep(.ns-table-main) {
border-top: 0;
padding: 0;
.drawerTable {
margin-top: 16px;
}
}
}
</style>

View File

@@ -16,9 +16,5 @@ const proxy = {
changeOrigin: true,
rewrite: (path) => path.replace(/^\/carbon-smart/, ''),
},
'/carbon': {
target: 'http://192.168.11.169:8224',
changeOrigin: true
},
};
export default configFun({ dirname, proxy, serviceMode: 'saas', baseDir: '../' });

View File

@@ -32,6 +32,7 @@
getContainer: (triggerNode: any) => triggerNode.parentNode,
...attrs,
...props,
footerStyle: { textAlign: 'right' },
}));
return { getBindValue };
},

View File

@@ -34,6 +34,7 @@ export const tableProps = {
scroll: PropTypes.object.def(() => tableConfig.scroll), //操作栏
size: PropTypes.string.def('middle'),
showBack: PropTypes.bool.def(false),
bordered: PropTypes.bool.def(true),
pageField: PropTypes.string.def(tableConfig.pageField),
pageCountField: PropTypes.string.def(tableConfig.pageCount),
listField: PropTypes.string.def(tableConfig.listField),

View File

@@ -747,27 +747,4 @@
word-break: break-word;
width: 100%;
}
.blue-background.ant-switch-checked {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch {
background-color: grey !important;
}
.blue-background.ant-switch-checked .ant-switch-handle {
background-color: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
) !important;
}
.grey-background.ant-switch .ant-switch-handle {
background-color: grey !important;
}
</style>

View File

@@ -16,6 +16,7 @@ export const treeProps = {
},
treeData: PropTypes.array.def([]),
params: PropTypes.object.def(() => ({})),
showLine: PropTypes.object.def(() => ({ showLeafIcon: false })),
dynamicParams: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,

View File

@@ -1,11 +1,11 @@
<template>
<a-spin :spinning="treeState.loading">
<div class="ns-tree-form">
<div v-show="header" class="ns-tree-title">
<div v-if="header" class="ns-tree-title">
<ns-icon :name="header.icon" size="14" />
<span>{{ header.title }}</span>
</div>
<div v-show="!formConfig?.schema">
<div v-if="!formConfig?.schema">
<ns-form ref="formElRef" v-bind="formConfig" :model="formModel" @finish="formFinish" />
</div>
</div>
@@ -80,7 +80,7 @@
treeState.loading = loading;
};
const httpPrams = computed(() => {
return { ...route.params, ...route.query, ...props.params };
return { ...route.params, ...route.query, ...props.params, ...formModel };
});
const getData = (params = {}) => {
@@ -105,6 +105,15 @@
};
getData();
const treeReload = (params) => {
getData(params);
};
const clearSelectedKeys = () => {
selectedKeys.value = [];
};
defineExpose({ treeReload, clearSelectedKeys });
</script>
<style lang="less" scoped>
@gap: 16px;

View File

@@ -115,6 +115,10 @@
}
return imgCode;
};
defineExpose({
reload: handleDraw,
});
</script>
<style scoped>
.img-verify {

View File

@@ -4,7 +4,7 @@ export const routerConfig = {
router: router,
login: 'login',
logout: () => {
Cookies.remove('nervsid');
Cookies.remove(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
Cookies.remove('token');
sessionStorage.clear();
router.push('/login');

View File

@@ -4,6 +4,9 @@ import { authorizationService } from '/nerv-base/store/modules/authorization-ser
import { appConfigStore } from '/nerv-base/store/modules/app-config';
import { Cookies } from '/nerv-lib/util/cookie';
import { setRouteChange } from '/nerv-lib/util/routeChange';
console.log(import.meta.env.VITE_PUBLIC_PATH);
export function createPermissionGuard(router: Router, whiteNameList: string[]) {
const authorizationStore = authorizationService();
const appConfig = appConfigStore();
@@ -11,23 +14,26 @@ export function createPermissionGuard(router: Router, whiteNameList: string[]) {
if (!appConfig.userCustomRouterGuard) {
setRouteChange(to);
if (to?.query?.nervsid) {
if (to?.query?.nervsid !== Cookies.get('nervsid')) {
Cookies.set('nervsid', to?.query?.nervsid);
if (to?.query?.nervsid !== Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)) {
Cookies.set(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`, to?.query?.nervsid);
try {
const info = await appConfig.userInfo();
await authorizationStore.initUserResource();
authorizationStore.initMenuResource();
appConfig.setUserInfo(info.data);
info.success
? window.sessionStorage.setItem('userInfo', JSON.stringify(info.data))
? window.sessionStorage.setItem(
import.meta.env.VITE_PUBLIC_PATH,
JSON.stringify(info.data),
)
: '';
} catch (err) {
Cookies.remove('nervsid');
Cookies.remove(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
}
}
}
if (
!Cookies.get('nervsid') &&
!Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`) &&
to.fullPath !== '/login' &&
to.name &&
!whiteNameList.includes(to.name as string)
@@ -36,15 +42,19 @@ export function createPermissionGuard(router: Router, whiteNameList: string[]) {
next({ name: 'login' });
} else {
// 存储用户信息
if (to.fullPath !== '/login') {
const initUserInfo = window.sessionStorage['userInfo'];
if (!initUserInfo && Cookies.get('nervsid')) {
const initUserInfo = window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH];
if (!initUserInfo && Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)) {
try {
const info = await appConfig.userInfo();
info.data ? window.sessionStorage.setItem('userInfo', JSON.stringify(info.data)) : '';
info.data
? window.sessionStorage.setItem(
import.meta.env.VITE_PUBLIC_PATH,
JSON.stringify(info.data),
)
: '';
} catch (err) {
Cookies.remove('nervsid');
Cookies.remove(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
}
} else {
if (initUserInfo) {

View File

@@ -168,7 +168,7 @@
const res = await http.post(configStore.updatePassWordInfo?.api, data);
if (res.success) {
NsMessage.success('修改成功,需重新登陆', 1, () => {
Cookies.remove('nervsid');
Cookies.remove(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
sessionStorage.clear();
router.push('/login');
authorizationStore.clearAuthorization();

View File

@@ -197,7 +197,7 @@
});
const toCustomUrl = (val: string) => {
window.open(`${val}?nervsid=${Cookies.get('nervsid')}`);
window.open(`${val}?nervsid=${Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)}`);
};
const messageCount = computed(() => {
return messagecountStore.getCount;
@@ -267,12 +267,15 @@
const { getProjectName: projectName } = storeToRefs(authorizationStore);
const { getEnterpriseName: enterpriseName } = storeToRefs(authorizationStore);
const initUserInfo = window.sessionStorage['userInfo'];
const initUserInfo = window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH];
if (!authorizationStore.getProjectName && Cookies.get('projectName')) {
authorizationStore.setProjectName(Cookies.get('projectName'));
}
if (!authorizationStore.getEnterpriseName && window.sessionStorage['userInfo']) {
const userInfo = JSON.parse(window.sessionStorage['userInfo']);
if (
!authorizationStore.getEnterpriseName &&
window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH]
) {
const userInfo = JSON.parse(window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH]);
authorizationStore.setEnterpriseName(
!userInfo.organizationalName ? '' : userInfo.organizationalName,
);
@@ -351,7 +354,7 @@
if (configStore.dropOut) {
configStore.dropOut(Cookies, router, useTags, authorizationStore, http);
} else {
Cookies.remove('nervsid');
Cookies.remove(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
sessionStorage.clear();
router.push('/login');
useTags().clearTags();
@@ -392,7 +395,7 @@
];
const backDoor = () => {
let protocol = window.location.protocol;
const nervsid = Cookies.get('nervsid');
const nervsid = Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
http.get('/portalurl.json').then((res) => {
if (nervsid) {
window.location.href = `${protocol}//${res.url}report?nervsid=${nervsid}`;

View File

@@ -187,8 +187,8 @@
// const messageCount = ref();
const time = ref();
const toCustomUrl = (val: string) => {
window.open(`${val}?nervsid=${Cookies.get('nervsid')}`);
// window.location.href = `${val}?nervsid=${Cookies.get('nervsid')}`;
window.open(`${val}?nervsid=${Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)}`);
// window.location.href = `${val}?nervsid=${Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)}`;
};
const messageCount = computed(() => {
return messagecountStore.getCount;
@@ -217,12 +217,15 @@
const userName = ref<string>('-');
const authorizationStore = authorizationService();
const initUserInfo = window.sessionStorage['userInfo'];
const initUserInfo = window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH];
if (!authorizationStore.getProjectName && Cookies.get('projectName')) {
authorizationStore.setProjectName(Cookies.get('projectName'));
}
if (!authorizationStore.getEnterpriseName && window.sessionStorage['userInfo']) {
const userInfo = JSON.parse(window.sessionStorage['userInfo']);
if (
!authorizationStore.getEnterpriseName &&
window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH]
) {
const userInfo = JSON.parse(window.sessionStorage[import.meta.env.VITE_PUBLIC_PATH]);
authorizationStore.setEnterpriseName(
!userInfo.organizationalName ? '' : userInfo.organizationalName,
);
@@ -314,7 +317,7 @@
if (configStore.dropOut) {
configStore.dropOut(Cookies, router, useTags, authorizationStore, http);
} else {
Cookies.remove('nervsid');
Cookies.remove(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
sessionStorage.clear();
router.push('/login');
useTags().clearTags();
@@ -353,7 +356,7 @@
];
const backDoor = () => {
let protocol = window.location.protocol;
const nervsid = Cookies.get('nervsid');
const nervsid = Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
http.get('/portalurl.json').then((res) => {
if (nervsid) {
window.location.href = `${protocol}//${res.url}report?nervsid=${nervsid}`;

View File

@@ -38,7 +38,7 @@
</a-input-password>
</a-form-item>
<a-form-item name="code" :rules="[{ validator }]">
<a-form-item name="code">
<!-- 验证码 -->
<a-input v-model:value="code" placeholder="验证码" class="loginInfo">
<template #prefix>
@@ -49,7 +49,7 @@
style="margin-right: 20px" />
</template>
<template #addonAfter>
<ns-verify @get-code="onGetCode" />
<ns-verify @get-code="onGetCode" ref="verifyRef" />
</template>
</a-input>
</a-form-item>
@@ -78,6 +78,7 @@
import { authorizationService } from '/nerv-base/store/modules/authorization-service';
import { Cookies } from '/nerv-lib/util/cookie';
import { storeToRefs } from 'pinia';
import { NsMessage } from '/nerv-lib/component';
export default defineComponent({
name: 'UserLogin',
@@ -94,6 +95,7 @@
const initUrl = ref('');
const verifyCode = ref('');
const code = ref();
const verifyRef = ref();
const isRemember = ref(false);
title.value = '账号登录';
// title.value = appConfig.title ? appConfig.title : '账号登录';
@@ -123,102 +125,92 @@
const submit = (value): void => {
console.log(value);
// if (password.value === '') {
// errorMsg.value = '请输入密码';
// errorShow.value = true;
// return;
// }
// if (userName.value === '') {
// errorMsg.value = '请输入账号';
// errorShow.value = true;
// return;
// }
// if (!code.value) {
// // errorMsg.value = '请输入验证码';
// // errorShow.value = true;
// return;
// }
// if (code.value.toLocaleLowerCase() !== verifyCode.value.toLocaleLowerCase()) {
// errorMsg.value = '请输入正确的验证码';
// errorShow.value = true;
// return;
// }
if (userName.value !== '' && password.value !== '') {
errorShow.value = false;
let data = JSON.stringify({
accountNo: userName.value.trim(),
password: password.value.trim(),
});
// 记住密码
rememberFunc(data);
loading.value = true;
async function logins() {
try {
const res = await configStore.userLogin(JSON.parse(data));
if (res.data?.token) {
let data = JSON.stringify({
accountNo: userName.value.trim(),
password: password.value.trim(),
});
validator(null, value.code)
.then(() => {
// 记住密码
rememberFunc(data);
loading.value = true;
async function logins() {
try {
const res = await configStore.userLogin(JSON.parse(data));
verifyRef.value?.reload();
if (res.data?.token) {
Cookies.set('nervsid', res.data?.token);
}
const info = await configStore.userInfo();
info.data
? window.sessionStorage.setItem('userInfo', JSON.stringify(info.data))
: '';
loading.value = false;
if (configStore.enablePermissions) {
const res = await configStore.userResource(info);
res?.data.sort((a, b) => {
return a.sort - b.sort;
});
console.log(res.data);
if (configStore.customApplication) {
await useAuthorization.initMenuResource();
if (res.data?.token) {
Cookies.set(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`, res.data?.token);
}
initUrl.value = '';
const dealInitUrl = (item) => {
if (item.type === 'menus' && item.menus && item.menus?.length !== 0) {
dealInitUrl(item.menus[0]);
} else {
if (item.type === 'noChildrenMenu') {
initUrl.value = configStore.resourceName
? item.code.replace(configStore.resourceName, '')
: item.code;
}
}
};
if (configStore.resourceName) {
const initResource = [];
res.data.forEach((item) => {
if (item.code.includes(configStore.resourceName)) {
initResource.push(item);
}
const info = await configStore.userInfo();
info.data
? window.sessionStorage.setItem(
import.meta.env.VITE_PUBLIC_PATH,
JSON.stringify(info.data),
)
: '';
loading.value = false;
if (configStore.enablePermissions) {
const res = await configStore.userResource(info);
res?.data.sort((a, b) => {
return a.sort - b.sort;
});
console.log(res.data);
if (configStore.customApplication) {
await useAuthorization.initMenuResource();
}
initUrl.value = '';
const dealInitUrl = (item) => {
if (item.type === 'menus' && item.menus && item.menus?.length !== 0) {
dealInitUrl(item.menus[0]);
} else {
if (item.type === 'noChildrenMenu') {
initUrl.value = configStore.resourceName
? item.code.replace(configStore.resourceName, '')
: item.code;
}
}
};
if (configStore.resourceName) {
const initResource = [];
res.data.forEach((item) => {
if (item.code.includes(configStore.resourceName)) {
initResource.push(item);
}
});
dealInitUrl(initResource[0]);
} else {
dealInitUrl(res.data[0]);
}
// dealInitUrl(res.data[0]);
useAuthorization.updateUserResource(res.data);
const initRouterList = useAuthorization.getInitRouterList;
router.push({
name: initRouterList.length === 0 ? 'error403' : res.data[0]['code'],
});
dealInitUrl(initResource[0]);
} else {
dealInitUrl(res.data[0]);
router.replace({ name: 'root' });
}
// dealInitUrl(res.data[0]);
useAuthorization.updateUserResource(res.data);
const initRouterList = useAuthorization.getInitRouterList;
router.push({
name: initRouterList.length === 0 ? 'error403' : res.data[0]['code'],
});
} else {
router.replace({ name: 'root' });
}
}
} catch (err) {
console.log(err);
} catch (err) {
console.log(err);
loading.value = false;
loading.value = false;
}
}
}
logins();
}
logins();
})
.catch((flag) => {
if (flag) {
NsMessage.error('请输入正确的验证码');
verifyRef.value?.reload();
} else {
NsMessage.error('请输入验证码');
}
});
};
const checkoutLogo = (): void => {
logUrl.value = '';
@@ -232,10 +224,11 @@
verifyCode.value = res;
};
const validator = async (rule, value) => {
if (!value) return Promise.reject('请输入验证码');
const validator = (rule, value) => {
if (!value) return Promise.reject(false);
if (value?.toLocaleLowerCase() !== verifyCode.value.toLocaleLowerCase())
return Promise.reject('请输入正确的验证码');
return Promise.reject(true);
return Promise.resolve();
};
return {
validator,
@@ -258,6 +251,7 @@
errorShow,
configStore,
isRemember,
verifyRef,
};
},
created() {
@@ -273,7 +267,7 @@
},
mounted() {
// if (Cookies.get('nervsid') !== undefined) {
// if (Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`) !== undefined) {
// this.$router.push('/home');
// }
},

View File

@@ -43,14 +43,14 @@ export class NSAxios {
private interceptors(): void {
this.instance.interceptors.request.use((config: AxiosRequestConfig) => {
// config.headers.token = Cookies.get('nervsid');
// config.headers.token = Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
// config.withCredentials = true;
if (!config.headers) {
config.headers = {};
}
if (Cookies.get('nervsid')) {
config.headers['token'] = Cookies.get('nervsid');
if (Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`)) {
config.headers['token'] = Cookies.get(`${import.meta.env.VITE_PUBLIC_PATH}-nervsid`);
}
//暂时停用后端自己处理