This commit is contained in:
zhaohy
2024-07-15 16:00:15 +08:00
27 changed files with 2628 additions and 2421 deletions

View File

@@ -8,6 +8,9 @@ export enum carbonEmissionFactorLibrary {
getCarbonFactorTree = '/carbon-smart/api/carbon/emission/type/getCarbonFactorTree',
creat = '/carbon-smart/api/carbon/emission/type/creatOrUpdate',
delTreeNode = '/carbon-smart/api/carbon/emission/type/del',
// 单位管理
dictionaryUnitManagement = '/carbon-smart/api/carbon/emission/factor/dictionaryUnitManagement',
findOutermost = '/carbon-smart/api/carbon/emission/factor/findOutermost',
}
// 碳排管理-碳排统计接口
export enum energyConsumption {

View File

@@ -26,4 +26,6 @@ export enum group {
computeGroupNum = `${BASE_URL}/deviceGroup/computeGroupNum`, // 分组删除
formula = `${BASE_URL}/deviceGroup/formula`, // 编辑公式
queryFormula = `${BASE_URL}/deviceGroup/queryFormula`, // 公式查询
dropGroupFilter = `${BASE_URL}/deviceGroup/dropGroupFilter`, // 分组列表查询
dropGroupInfoFilter = `${BASE_URL}/deviceGroup/dropGroupInfoFilter`, // 计算列表查询
}

View File

@@ -27,6 +27,7 @@ export const appConfig = {
themeConfig: {
bgImageUrl: `${import.meta.env.VITE_PUBLIC_PATH}/asset/image/login/background.png`,
logoLessUrl: `${import.meta.env.VITE_PUBLIC_PATH}/asset/image/headerIcon.png`,
logoUrl: `${import.meta.env.VITE_PUBLIC_PATH}/asset/image/headerIcon.png`,
},
// userCustomRouterGuard: (to, from, next, whiteNameList, authorizationStore, appConfig) => {
// console.log({ to, from, next, whiteNameList, authorizationStore, appConfig }, 'routeConfig');
@@ -102,7 +103,7 @@ export const appConfig = {
updatePassWordInfo: {
title: '修改密码',
subtitle: 'huaxing平台',
api: '/api/web/objs/User/changePassword',
api: '/carbon-smart/api/user/password',
},
// headerBellInfo: {
// isShow: true,

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14.3564453125" height="15.0081787109375" viewBox="0 0 14.3564453125 15.0081787109375" fill="none">
<path d="M14.1739 3.70025C14.1361 3.65398 14.0991 3.60771 14.0571 3.56927C13.9468 3.46744 13.8241 3.37991 13.6919 3.30873C11.7357 2.26657 9.78168 1.22155 7.82619 0.17939C7.65994 0.0891889 7.4793 0.0284937 7.2923 1.16683e-07L7.11007 0C6.6559 0.0904063 6.26508 0.352371 5.86004 0.565929C4.08665 1.49755 2.31626 2.43531 0.54908 3.37911C0.273361 3.52656 0.0597992 3.76005 0.0099678 4.11741C0.00854492 4.13022 0.00356293 4.14161 0 4.153L0 4.30463C0.0284805 4.46764 0.0761719 4.62212 0.177967 4.75167C0.213558 4.79581 0.247017 4.84208 0.287598 4.87839C0.395794 4.97306 0.505424 5.06418 0.63142 5.13181C2.60185 6.1818 4.56944 7.23606 6.54201 8.2825C6.70858 8.3722 6.88014 8.45904 7.07235 8.47755C7.2674 8.49749 7.45034 8.46118 7.62261 8.37006L13.6037 5.19445C13.6706 5.15815 13.7403 5.12754 13.803 5.08554C13.9902 4.96381 14.1653 4.82357 14.2636 4.60432C14.3063 4.51107 14.3248 4.405 14.3561 4.30463L14.3561 4.153C14.3248 3.98785 14.2764 3.82982 14.1732 3.70026L14.1739 3.70025ZM13.5004 6.55054C13.2896 6.50635 13.0699 6.53759 12.8797 6.63881C12.501 6.8424 12.1251 7.05525 11.7429 7.25386C10.2444 8.03264 8.74237 8.80714 7.24318 9.5845C7.20047 9.60585 7.16488 9.60941 7.12074 9.58663L1.61023 6.69862C1.3144 6.53849 0.971592 6.48783 0.642097 6.55553C0.300404 6.62601 0.0633507 6.9314 0.0156593 7.31936C0.0128059 7.34072 0.00497818 7.36207 0.000705719 7.38343L0.000705719 7.5799C0.0291786 7.73936 0.0768776 7.89027 0.173687 8.01983C0.266228 8.14156 0.373005 8.24264 0.501858 8.31098C2.51998 9.39372 4.53954 10.4779 6.5605 11.5549C6.72209 11.6404 6.89009 11.7258 7.07874 11.7422C7.2681 11.7592 7.44748 11.7251 7.61619 11.6375C9.69198 10.5626 11.7678 9.48912 13.8407 8.40922C14.1183 8.264 14.2771 8.00702 14.3397 7.68312C14.3461 7.65963 14.3504 7.63472 14.3561 7.61051L14.3561 7.42828C14.3369 7.36207 14.3248 7.2916 14.2998 7.22896C14.1532 6.83814 13.877 6.62387 13.5004 6.55126L13.5004 6.55054ZM13.5019 9.80944C13.2908 9.7652 13.0708 9.79644 12.8804 9.89771C12.496 10.1042 12.1159 10.3177 11.73 10.5199C10.238 11.2944 8.74591 12.0646 7.25386 12.8391C7.20773 12.8661 7.15042 12.8653 7.10508 12.837C5.27916 11.8774 3.45181 10.9207 1.62446 9.96106C1.35194 9.82084 1.04443 9.7632 0.739628 9.7952C0.383698 9.82367 0.108913 10.1049 0.0313225 10.495C0.0206451 10.5441 0.0099678 10.5918 0.000720978 10.6402L0.000720978 10.8366C0.0291862 10.9933 0.0754585 11.1427 0.168713 11.2709C0.26268 11.3983 0.373734 11.5008 0.507553 11.572C2.52355 12.654 4.54025 13.7361 6.55837 14.811C6.73064 14.9035 6.91217 14.9889 7.10935 15.0082L7.24888 15.0082C7.41069 14.9797 7.56661 14.9243 7.71016 14.8444C7.89952 14.7412 8.09955 14.6586 8.29104 14.559C10.1333 13.6008 11.9749 12.6412 13.8151 11.6823C14.107 11.53 14.2757 11.273 14.3412 10.9335L14.3561 10.868L14.3561 10.6857C14.3369 10.6195 14.3248 10.5491 14.2999 10.4864C14.1554 10.0949 13.8777 9.88134 13.5019 9.81015L13.5019 9.80944Z" fill="#4388FB" >
</path>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -5,25 +5,25 @@ const equipment = {
meta: { title: '监控中心', icon: 'jiankongzhongxin', index: 1 },
redirect: { name: 'EnvironmentMonitor' },
children: [
// {
// path: 'environmentMonitor',
// name: 'EnvironmentMonitor',
// meta: { title: '环境监测', hideChildren: true, icon: 'huanjingjiance' },
// component: Base,
// redirect: { name: 'EnvironmentMonitorIndex' },
// children: [
// {
// path: 'index',
// name: 'EnvironmentMonitorIndex',
// component: () => import('/@/view/monitor/environmentMonitor/index.vue'),
// meta: {
// title: '环境监测',
// keepAlive: true,
// // backApi: [],
// },
// },
// ],
// },
{
path: 'environmentMonitor',
name: 'EnvironmentMonitor',
meta: { title: '环境监测', hideChildren: true, icon: 'huanjingjiance' },
component: Base,
redirect: { name: 'EnvironmentMonitorIndex' },
children: [
{
path: 'index',
name: 'EnvironmentMonitorIndex',
component: () => import('/@/view/monitor/environmentMonitor/index.vue'),
meta: {
title: '环境监测',
keepAlive: true,
// backApi: [],
},
},
],
},
{
path: 'deviceMonitor',
name: 'DeviceMonitor',

View File

@@ -36,16 +36,17 @@
:expanded-keys="expandedKeys"
:auto-expand-parent="autoExpandParent"
@expand="onExpand">
<template #title="{ emissionName }">
<span v-if="emissionName && selectTreeDataValue && emissionName.indexOf(selectTreeDataValue) > -1">
{{ emissionName.substring(0, emissionName.indexOf(selectTreeDataValue)) }}
<span style="color: #f50">{{ selectTreeDataValue }}</span>
{{ emissionName.substring(emissionName.indexOf(selectTreeDataValue) + selectTreeDataValue.length) }}
<template #title="data">
<div class="treeRow">
<div>
<span v-if="data.emissionName && selectTreeDataValue && data.emissionName.indexOf(selectTreeDataValue) > -1">
{{ data.emissionName.substring(0, data.emissionName.indexOf(selectTreeDataValue)) }}
<span style="color: #f50" >{{ selectTreeDataValue }}</span>
{{ data.emissionName.substring(data.emissionName.indexOf(selectTreeDataValue) + selectTreeDataValue.length) }}
</span>
<span v-else>{{ emissionName }}</span>
</template>
</a-tree>
<a-popover v-if="showOperation" placement="rightTop" trigger="focus">
<span v-else>{{ data.emissionName }}</span>
</div>
<a-popover placement="rightTop" trigger="focus">
<template #content>
<div style="display: flex;flex-direction: column;">
<a-button type="text" @click="editTreeNodeData">编辑</a-button>
@@ -55,8 +56,11 @@
<a-button type="text" @click="deleteTreeNode">删除</a-button>
</div>
</template>
<MoreOutlined style="position: absolute;right: 0;top: 16%;font-size: 25px;cursor: pointer;" />
<MoreOutlined class="actionMore" />
</a-popover>
</div>
</template>
</a-tree>
<div class="addTreeNode">
<a-button type="primary" style="width:100%;" @click="addTreeNodeData">新增</a-button>
</div>
@@ -92,6 +96,66 @@
<a-button type="primary" @click="onEdit">确定</a-button>
</template>
</a-drawer>
<!-- 单位管理 -->
<a-drawer
:width="500"
:visible="unitManagement"
v-if="unitManagement"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
title="单位管理"
@close="closeUnitManag">
<div class="addButton">
<a-button type="primary">新增分组</a-button>
<a-button type="primary">新增单位</a-button>
</div>
<div class="treePart">
<div style="width: 100%;height: 7%;display: flex;justify-content: center;align-items: center;">
<a-select
ref="select"
:value="unitTreeParams.cnValue"
style="width: 96%"
placeholder="选择分组"
@change="handleChange"
>
<a-select-option v-for="(item, index) in groupData" :key="index" :value="item.cnValue">
{{ item.cnValue }}
</a-select-option>
</a-select>
</div>
<a-tree
:expandedKeys="unitExpandedKeys"
:selectedKeys="unitSelectedKeys"
:tree-data="unitTreeData"
v-if="unitTreeData && unitTreeData.length > 0"
>
<template #title="data">
<div class="treeRow">
<div>
<span>{{ data.cnValue }}</span>
</div>
<a-popover placement="rightTop" trigger="focus">
<template #content>
<div style="display: flex;flex-direction: column;">
<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 class="actionMore" />
</a-popover>
</div>
</template>
</a-tree>
</div>
<template #footer>
<a-button style="margin-right: 8px" @click="closeUnitManag">取消</a-button>
<a-button type="primary" @click="onEdit">确定</a-button>
</template>
</a-drawer>
</div>
</template>
<script lang="ts" setup>
@@ -107,7 +171,7 @@
AntTreeNodeDropEvent,
TreeProps,
} from 'ant-design-vue/es/tree';
import { log } from 'node:console';
import { log } from 'node:console';
defineOptions({ name: 'OrderListIndex' });
const selectTreeDataValue = ref<string>('');
@@ -116,6 +180,7 @@ import { log } from 'node:console';
let formData = ref({});
const formRef = ref();
const visible = ref(false);
const unitManagement = ref(false);
const searchValue = ref<string>('');
const searchValue2 = ref<string>('');
const disabled = ref(false);
@@ -428,7 +493,9 @@ import { log } from 'node:console';
formData.value = {};
userAuthList.value.splice(0);
};
const closeUnitManag = () => {
unitManagement.value = false;
};
const onEdit = () => {
formRef.value?.triggerSubmit().then(() => {
console.log(formData.value, 'formData.value');
@@ -497,6 +564,20 @@ import { log } from 'node:console';
api: carbonEmissionFactorLibrary.del,
dynamicParams: { ids: 'id[]' },
},
{
label: '单位管理',
type: 'primary',
name: 'userAdd',
handle: () => {
unitManagement.value = true,
fetch(carbonEmissionFactorLibrary.findOutermost).then((res) => {
groupData.value = res.data
});
fetch(carbonEmissionFactorLibrary.dictionaryUnitManagement, unitTreeParams.value).then((res) => {
unitTreeData.value = res.data
});
},
},
],
columns: [
{
@@ -679,6 +760,22 @@ import { log } from 'node:console';
// pagination: { defaultPageSize: 10 },
rowKey: 'id',
});
// 单位管理
const unitExpandedKeys = ref<string[]>();
const unitSelectedKeys = ref<string[]>([]);
const unitTreeParams = ref({
grp: 'MEASUREMENT_UNIT'
})
const groupData = ref([])
const unitTreeData = ref<TreeProps['treeData']>(genData);
const handleChange = (value: string) => {
unitTreeParams.value.cnValue = value
fetch(carbonEmissionFactorLibrary.dictionaryUnitManagement, unitTreeParams.value).then((res) => {
unitTreeData.value = res.data
});
};
</script>
<style lang="less" scoped>
.main {
@@ -696,7 +793,6 @@ import { log } from 'node:console';
// box-shadow: @ns-content-box-shadow;
flex: 1;
}
display: flex;
flex-direction: column;
.top{
@@ -727,4 +823,34 @@ import { log } from 'node:console';
padding-bottom: 10px;
border-bottom: 1px solid #e9e9e9;
}
.treeRow {
display: flex;
justify-content: space-between;
align-items: center;
}
.addButton{
width: 70%;
height: 5vh;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.treePart{
width: 70%;
height: 100%;
display: flex;
border: 1px solid #bfbfbf;
margin-left: 10%;
flex-direction: column;
}
.actionMore {
display: none;
}
:deep(.ant-tree-node-content-wrapper) {
&:hover {
.actionMore {
display: block;
}
}
}
</style>

View File

@@ -78,3 +78,45 @@ export const tableColumns = [
width: 130
},
];
export const columns = [
{
title: '序号',
customRender: (text: any) => {
return text.index + 1;
},
},
{
title: '因子值',
dataIndex: 'energyType',
},
{
title: '计量单位',
className: 'unit',
dataIndex: 'unit',
},
{
title: '更新时间',
className: 'unit',
dataIndex: 'unit',
},
{
title: '启用时间',
className: 'unit',
dataIndex: 'unit',
},
{
title: '结束时间',
className: 'unit',
dataIndex: 'unit',
},
{
title: '数据来源',
className: 'unit',
dataIndex: 'unit',
},
{
title: '操作',
key: 'action',
width: 130
},
];

View File

@@ -0,0 +1,401 @@
<!-- @format -->
<template>
<div class="main">
<div class="left">
<div class="top">
<a-form style="width: 100%;margin: 0 auto;">
<div class="ns-form-title"><span>排放分类</span></div>
<div style="padding: 0 16px !important;width: 100%;">
<a-row>
<a-col :span="24" style="margin-bottom: 16px;">
<a-input-search
v-model:value="selectTreeDataValue"
placeholder="请输入关键词"
@search="onSearchTreeData"
/>
</a-col>
</a-row>
</div>
</a-form>
<a-tree
v-if="gData && gData.length > 0"
class="draggable-tree"
style="padding: 0 16px !important;"
show-line
block-node
:tree-data="gData"
:checkedKeys="checkedTreeNodeKeys"
:selectedKeys="selectedKeys"
@check="checkTreeNode"
@select="onSelect"
:expanded-keys="expandedKeys"
:auto-expand-parent="autoExpandParent"
@expand="onExpand">
<template #title="{ emissionName }">
<span v-if="emissionName && selectTreeDataValue && emissionName.indexOf(selectTreeDataValue) > -1">
{{ emissionName.substring(0, emissionName.indexOf(selectTreeDataValue)) }}
<span style="color: #f50">{{ selectTreeDataValue }}</span>
{{ emissionName.substring(emissionName.indexOf(selectTreeDataValue) + selectTreeDataValue.length) }}
</span>
<span v-else>{{ emissionName }}</span>
</template>
</a-tree>
</div>
</div>
<div class="right">
<a-table :columns="columns" :data-source="tableData" bordered :pagination="false">
<template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'name'">
<a>{{ text }}</a>
</template>
</template>
<template #title>Header</template>
</a-table>
</div>
<!-- 新增树节点 -->
<ns-modal :visible="treeNodeAdd" :title="operationTree" @ok="handleOk" @cancel="handleCancel">
<ns-input
v-model:value="addTreeNode"
class="input"
placeholder="请输入排放类型"/>
</ns-modal>
<!-- 新增数据库数据 -->
<a-drawer
:width="500"
:visible="visible"
:body-style="{ paddingBottom: '80px' }"
:footer-style="{ textAlign: 'right' }"
destroyOnClose
@close="onClose">
<ns-form
ref="formRef"
:model="formData"
class="form"
:wrapperCol="{ span: 20 }"
formLayout="vertical" />
<template #footer>
<a-button style="margin-right: 8px" @click="onClose">取消</a-button>
<a-button type="primary" @click="onEdit">确定</a-button>
</template>
</a-drawer>
</div>
</template>
<script lang="ts" setup>
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';
import { carbonEmissionFactorLibrary } from '/@/api/carbonEmissionFactorLibrary';
import type {
AntTreeNodeDragEnterEvent,
AntTreeNodeDropEvent,
TreeProps,
} from 'ant-design-vue/es/tree';
import { log } from 'node:console';
defineOptions({ name: 'QuickCalculation' });
const selectTreeDataValue = ref<string>('');
const data = reactive({});
let formData = ref({});
const formRef = ref();
const visible = ref(false);
const searchValue = ref<string>('');
const searchValue2 = ref<string>('');
const disabled = ref(false);
const treeNodeAdd = ref<boolean>(false);
const operationTree = ref<string>('新增');
const opMap: any = ref({
type: 'add',
fuc: () => {},
record: {},
});
watchEffect(() => {
disabled.value = opMap.value.type === 'edit';
});
const casData = ref([]);
const treeData = ref([]);
const userAuthList = ref([]);
const orgId = ref('');
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
orgId.value = result;
const dynamicDisabled = computed(() => {
return formRef.value?.validateResult && userAuthList.value?.length;
});
const fetch = (api, params = { orgId }) => {
return http.post(api, params);
};
// 树结构
const x = 3;
const y = 2;
const z = 1;
const genData: TreeProps['treeData'] = [];
const generateData = (_level: number, _preKey?: string, _tns?: TreeProps['treeData']) => {
const preKey = _preKey || '0';
const tns = _tns || genData;
const children = [];
for (let i = 0; i < x; i++) {
const key = `${preKey}-${i}`;
tns.push({ title: key, key });
if (i < y) {
children.push(key);
}
}
if (_level < 0) {
return tns;
}
const level = _level - 1;
children.forEach((key, index) => {
tns[index].children = [];
return generateData(level, key, tns[index].children);
});
};
generateData(z);
type TreeDataItem = TreeProps['treeData'][number];
const gData = ref<TreeProps['treeData']>(genData);
const dataList: TreeProps['treeData'] = [];
const generateList = (data: TreeProps['treeData']) => {
for (let i = 0; i < data.length; i++) {
const node = data[i];
const key = node.key;
dataList.push({ key, title: key });
if (node.children) {
generateList(node.children);
}
}
};
generateList(genData);
const getParentKey = (
key: string | number,
tree: TreeProps['treeData'],
): string | number | undefined => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some(item => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
const expandedKeys = ref<(string | number)[]>([]);
const autoExpandParent = ref<boolean>(true);
const onExpand = (keys: string[]) => {
expandedKeys.value = keys;
autoExpandParent.value = false;
};
watch(selectTreeDataValue, value => {
const expanded = dataList
.map((item: TreeProps['treeData'][number]) => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, gData.value);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
expandedKeys.value = expanded;
selectTreeDataValue.value = value;
autoExpandParent.value = true;
});
// 筛选树结构中的数据
const onSearchTreeData = (selectTreeDataValue: string) => {
console.log('use value', selectTreeDataValue);
console.log('or use this.value', value.value);
};
// 点击数据点的复选框
const checkedIds = ref([])
const emissionType = ref()
const checkTreeNode = (checkedKeys, info) => {
checkedIds.value = []
info.checkedNodes.forEach(item=>{
checkedIds.value.push(item.id)
})
sessionStorage.setItem('checkedTreeNode', checkedIds.value);
emissionType.value = checkedIds.value.join(',')
}
// 新增/编辑树节点点击确定
const addTreeNode =ref()
const handleOk = (e: MouseEvent) => {
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]);
}
});
});
};
getOrgTree();
// 被选中的树节点
const editTreeNode = ref({})
const onSelect = (selectedKeys: string[], info: any) => {
if(info.selected){
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);
getOrgTree({ orgName: searchValue.value, orgId });
};
const tableFetch = (params) => {
console.log(params, 'sdfasfasdfasdfasdf');
setTimeout(() => {
mainRef.value?.nsTableRef.reload();
}, 100);
};
const handleSelect = (selectedKeys: any, info: any) => {
fetch(carbonEmissionFactorLibrary.queryDeptTree, { orgId: info.node?.orgInfo.orgId }).then((res) => {
treeData2.value = res.data;
});
tableFetch({ orgId: info.node?.orgInfo.orgId });
};
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
{
title: 'Cash Assets',
className: 'column-money',
dataIndex: 'money',
},
{
title: 'Address',
dataIndex: 'address',
},
];
const tableData = [
{
key: '1',
name: 'John Brown',
money: '¥300,000.00',
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
money: '¥1,256,000.00',
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
money: '¥120,000.00',
address: 'Sidney No. 1 Lake Park',
},
];
const onClose = () => {
visible.value = false;
formData.value = {};
userAuthList.value.splice(0);
};
const onEdit = () => {
formRef.value?.triggerSubmit().then(() => {
console.log(formData.value, 'formData.value');
// if (!userAuthList.value.length) {
// NsMessage.error('请添加用户权限');
// return;
// }
opMap.value.fuc &&
opMap.value.fuc({ ...formData.value });
});
};
</script>
<style lang="less" scoped>
.main {
background-color: @ns-content-bg;
display: flex;
height: 100%;
}
.left {
width: 300px;
// max-height: calc(100vh - 96px);
margin-right: @ns-gap;
min-width: fit-content;
> div {
background-color: @white;
// box-shadow: @ns-content-box-shadow;
flex: 1;
}
display: flex;
flex-direction: column;
.top{
position: relative;
.addTreeNode{
width: 100%;
padding: 16px;
position: absolute;
bottom: 0;
}
}
}
.right {
flex: 1;
min-width: 0;
}
::v-deep .ant-table-container{
padding: 0px 16px;
}
.top {
overflow: auto;
// height: 50%;
// border-bottom: 5px solid rgb(229, 235, 240);
// overflow-y: auto;
}
.ns-form-title{
font-weight: bold;
user-select: text;
padding: 16px;
margin-bottom: 16px;
padding-bottom: 10px;
border-bottom: 1px solid #e9e9e9;
}
</style>
<style scoped>
th.column-money,
td.column-money {
text-align: right !important;
}
</style>

View File

@@ -1,16 +1,14 @@
<!-- @format -->
<template>
<div class="main">
<div class="left">
<div class="top">
<a-form style="width: 100%;margin: 0 auto;">
<div class="ns-form-title"><span>排放分类</span></div>
<div class="ns-form-title"><span>因子分类</span></div>
<div style="padding: 0 16px !important;width: 100%;">
<a-row>
<a-col :span="24" style="margin-bottom: 16px;">
<a-input-search
v-model:value="selectTreeDataValue"
v-model:value="searchValue"
placeholder="请输入关键词"
@search="onSearchTreeData"
/>
@@ -19,45 +17,54 @@
</div>
</a-form>
<a-tree
v-if="gData && gData.length > 0"
class="draggable-tree"
style="padding: 0 16px !important;"
draggable
show-line
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">
<template #title="{ emissionName }">
<span v-if="emissionName && selectTreeDataValue && emissionName.indexOf(selectTreeDataValue) > -1">
{{ emissionName.substring(0, emissionName.indexOf(selectTreeDataValue)) }}
<span style="color: #f50">{{ selectTreeDataValue }}</span>
{{ emissionName.substring(emissionName.indexOf(selectTreeDataValue) + selectTreeDataValue.length) }}
:tree-data="gData"
@expand="onExpand"
style="padding: 0 16px !important;"
>
<template #title="{ title }">
<span v-if="title.indexOf(searchValue) > -1">
{{ title.substring(0, title.indexOf(searchValue)) }}
<span style="color: #f50">{{ searchValue }}</span>
{{ title.substring(title.indexOf(searchValue) + searchValue.length) }}
</span>
<span v-else>{{ emissionName }}</span>
<span v-else>{{ title }}</span>
</template>
</a-tree>
</div>
</div>
<div class="right">
<ns-view-list-table v-bind="tableConfig" :model="data" ref="mainRef" :scroll="{ x: 2000}"/>
<a-table
:columns="columns"
:data-source="tableData"
bordered
:pagination="false">
<template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'action'">
<span>
<a @click="editData(record)">编辑</a>
<a-divider type="vertical" />
<a @click="delData(record)">删除</a>
</span>
</template>
</template>
<template #title>
<div class="ns-table-title"><span>排放因子库</span></div>
<div class="buttonGroup">
<a-button type="primary" @click="addNewData">新增</a-button>
</div>
<!-- 新增树节点 -->
<ns-modal :visible="treeNodeAdd" :title="operationTree" @ok="handleOk" @cancel="handleCancel">
<ns-input
v-model:value="addTreeNode"
class="input"
placeholder="请输入排放类型"/>
</ns-modal>
<!-- 新增数据库数据 -->
</template>
</a-table>
<a-pagination
:current="queryParams.pageNum"
:total="total"
:page-size="queryParams.pageSize"
style="display: flex;justify-content: center;margin-top: 16px;"
:show-size-changer="true"
:show-quick-jumper="true"
@change="onChange" />
<!-- 新增/编辑 -->
<a-drawer
:width="500"
:visible="visible"
@@ -65,72 +72,74 @@
:footer-style="{ textAlign: 'right' }"
destroyOnClose
@close="onClose">
<ns-form
<a-form
ref="formRef"
:model="formData"
class="form"
:wrapperCol="{ span: 20 }"
formLayout="vertical" />
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item ref="name" label="能源种类" name="energyType">
<a-input v-model:value="formState.energyType" placeholder="请输入能源种类" />
</a-form-item>
<a-form-item label="计量单位" name="unit">
<a-cascader v-model:value="formState.unit" :options="options" />
</a-form-item>
<a-form-item label="自动采集节点" name="collectionNode">
<a-tree-select
v-model:value="formState.collectionNode"
:tree-line="true"
:tree-data="treeData"
tree-node-filter-prop="title"
>
</a-tree-select>
</a-form-item>
<a-form-item label="计算碳排" name="isComputeCarbon">
<a-radio-group v-model:value="formState.isComputeCarbon" @change="changeRadio">
<a-radio :value="0"></a-radio>
<a-radio :value="1"></a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="排放类型" name="emissionType">
<a-select v-model:value="formState.emissionType" placeholder="请选择排放类型">
<a-select-option v-for="(item, index) in emissionTypeDic" :key="index" :value="item.cnValue">
{{ item.cnValue }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose">取消</a-button>
<a-button type="primary" @click="onEdit">确定</a-button>
<a-button type="primary" @click="onSubmit">确定</a-button>
</template>
</a-drawer>
</div>
</div>
</template>
<script lang="ts" setup>
import { Modal } from 'ant-design-vue';
import { computed, createVNode, defineComponent, reactive, ref, watchEffect,watch } from 'vue';
import { ref, watch } from 'vue';
import type { TreeProps } from 'ant-design-vue';
import { Pagination } from 'ant-design-vue';
import { columns } from '../config';
import { http } from '/nerv-lib/util/http';
import { NsMessage, NsModal } from '/nerv-lib/component';
import { carbonEmissionFactorLibrary } from '/@/api/carbonEmissionFactorLibrary';
import type {
AntTreeNodeDragEnterEvent,
AntTreeNodeDropEvent,
TreeProps,
} from 'ant-design-vue/es/tree';
import { log } from 'node:console';
defineOptions({ name: 'QuickCalculation' });
const selectTreeDataValue = ref<string>('');
const mainRef = ref();
const data = reactive({});
let formData = ref({});
const formRef = ref();
const visible = ref(false);
const searchValue = ref<string>('');
const searchValue2 = ref<string>('');
const disabled = ref(false);
const treeNodeAdd = ref<boolean>(false);
const operationTree = ref<string>('新增');
const opMap: any = ref({
type: 'add',
fuc: () => {},
record: {},
defineOptions({
energyType: 'quickCalculation', // 与页面路由name一致缓存才可生效
components: {
'a-pagination': Pagination,
},
});
watchEffect(() => {
disabled.value = opMap.value.type === 'edit';
});
const casData = ref([]);
const treeData = ref([]);
const userAuthList = ref([]);
const orgId = ref('');
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
orgId.value = result;
const dynamicDisabled = computed(() => {
return formRef.value?.validateResult && userAuthList.value?.length;
});
const fetch = (api, params = { orgId }) => {
const fetch = (api, params = { orgId } ) => {
return http.post(api, params);
};
// 树结构
// 数结构
const x = 3;
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';
@@ -154,70 +163,7 @@ import { log } from 'node:console';
});
};
generateData(z);
type TreeDataItem = TreeProps['treeData'][number];
const gData = ref<TreeProps['treeData']>(genData);
const onDragEnter = (info: AntTreeNodeDragEnterEvent) => {
console.log(info);
// expandedKeys 需要展开时
// expandedKeys.value = info.expandedKeys;
};
const onDrop = (info: AntTreeNodeDropEvent) => {
console.log(info);
const dropKey = info.node.key;
const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split('-');
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
const loop = (data: TreeProps['treeData'], key: string | number, callback: any) => {
data.forEach((item, index) => {
if (item.key === key) {
return callback(item, index, data);
}
if (item.children) {
return loop(item.children, key, callback);
}
});
};
const data = [...gData.value];
// Find dragObject
let dragObj: TreeDataItem;
loop(data, dragKey, (item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, (item: TreeDataItem) => {
item.children = item.children || [];
/// where to insert 示例添加到头部,可以是随意位置
item.children.unshift(dragObj);
});
} else if (
(info.node.children || []).length > 0 && // Has children
info.node.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
loop(data, dropKey, (item: TreeDataItem) => {
item.children = item.children || [];
// where to insert 示例添加到头部,可以是随意位置
item.children.unshift(dragObj);
});
} else {
let ar: TreeProps['treeData'] = [];
let i = 0;
loop(data, dropKey, (_item: TreeDataItem, index: number, arr: TreeProps['treeData']) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
gData.value = data;
};
const dataList: TreeProps['treeData'] = [];
const generateList = (data: TreeProps['treeData']) => {
for (let i = 0; i < data.length; i++) {
@@ -249,13 +195,16 @@ import { log } from 'node:console';
return parentKey;
};
const expandedKeys = ref<(string | number)[]>([]);
const searchValue = ref<string>('');
const autoExpandParent = ref<boolean>(true);
const gData = ref<TreeProps['treeData']>(genData);
const onExpand = (keys: string[]) => {
expandedKeys.value = keys;
autoExpandParent.value = false;
};
watch(selectTreeDataValue, value => {
watch(searchValue, value => {
const expanded = dataList
.map((item: TreeProps['treeData'][number]) => {
if (item.title.indexOf(value) > -1) {
@@ -265,273 +214,64 @@ import { log } from 'node:console';
})
.filter((item, i, self) => item && self.indexOf(item) === i);
expandedKeys.value = expanded;
selectTreeDataValue.value = value;
searchValue.value = value;
autoExpandParent.value = true;
});
// 筛选树结构中的数据
const onSearchTreeData = (selectTreeDataValue: string) => {
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)
// 列表数据
const total = ref<number>()
const queryParams = ref({
pageNum: 1,
pageSize: 10,
})
sessionStorage.setItem('checkedTreeNode', checkedIds.value);
emissionType.value = checkedIds.value.join(',')
mainRef.value?.nsTableRef.reload();
}
// 新增/编辑树节点点击确定
const addTreeNode =ref()
const handleOk = (e: MouseEvent) => {
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){
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 tableData = [
{
key: '1',
name: 'John Brown',
money: '¥300,000.00',
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
money: '¥1,256,000.00',
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
money: '¥120,000.00',
address: 'Sidney No. 1 Lake Park',
},
];
// 分页器
const onChange = (pageNumber: number,size: number) => {
queryParams.value.pageNum = pageNumber;
queryParams.value.pageSize = size;
// getTableList()
};
// 新增/编辑
const formRef = ref();
const labelCol = { span: 5 };
const wrapperCol = { span: 19 };
const formState = ref({})
const visible = ref(false);
const onSearch = () => {
console.log(searchValue.value);
getOrgTree({ orgName: searchValue.value, orgId });
// 定义form表单的必填
const rules: Record<string, Rule[]> = {
energyType: [{ required: true, message: '请输入能源种类', trigger: 'change' }],
isComputeCarbon: [{ required: true, message: '请选择是否计算碳排', trigger: 'change' }]
};
const tableFetch = (params) => {
console.log(params, 'sdfasfasdfasdfasdf');
tableConfig.value.params = {
...mainRef.value.params,
...params,
// 点击编辑按钮
const editData = (record) =>{
visible.value = true
};
setTimeout(() => {
mainRef.value?.nsTableRef.reload();
}, 100);
};
const handleSelect = (selectedKeys: any, info: any) => {
fetch(carbonEmissionFactorLibrary.queryDeptTree, { orgId: info.node?.orgInfo.orgId }).then((res) => {
treeData2.value = res.data;
});
tableFetch({ orgId: info.node?.orgInfo.orgId });
};
// 关闭新增抽屉
const onClose = () => {
visible.value = false;
formData.value = {};
userAuthList.value.splice(0);
formRef.value.resetFields();
};
const onEdit = () => {
formRef.value?.triggerSubmit().then(() => {
console.log(formData.value, 'formData.value');
// if (!userAuthList.value.length) {
// NsMessage.error('请添加用户权限');
// return;
// }
opMap.value.fuc &&
opMap.value.fuc({ ...formData.value });
});
};
const tableConfig = ref({
title: '数据库',
api: carbonEmissionFactorLibrary.getTableList,
params: {
orgId,
emissionType
},
headerActions: [
{
label: '新增',
name: 'userAdd',
type: 'primary',
handle: () => {
opMap.value.type = 'add';
setTimeout(() => {
formData.value = {
carbonEmissionPrefix:'t',
numberOfReferences:'10'
};
userAuthList.value.splice(0);
});
opMap.value.fuc = (formData: any) => {
formData.emissionType = formData.emissionType[formData.emissionType.length - 1]
return http.post(carbonEmissionFactorLibrary.creatOrUpdate, formData).then(() => {
mainRef.value?.nsTableRef.reload();
visible.value = false;
NsMessage.success('操作成功');
});
};
visible.value = true;
},
},
{
label: '导入',
type: 'primary',
name: 'userImport',
handle: () => {},
},
{
label: '导出',
type: 'primary',
name: 'userExports',
},
{
label: '批量删除',
type: 'primary',
name: 'userBatchDel',
dynamicDisabled: (data: any) => {
return data.list.length === 0;
},
confirm: true,
isReload: true,
isClearCheck: true,
api: carbonEmissionFactorLibrary.del,
dynamicParams: { ids: 'id[]' },
},
],
columns: [
{
title: 'id',
customRender: (text: any) => {
return text.index + 1;
},
},
{
title: '排放源',
dataIndex: 'emissionSources',
},
{
title: '排放类型',
dataIndex: 'emissionTypeColumn',
},
{
title: '排放气体',
dataIndex: 'emissionGas',
},
{
title: '排放环节',
dataIndex: 'emissionProcess',
},
{
title: '排放因子',
dataIndex: 'emissionFactors',
},
{
title: '排放因子单位',
dataIndex: 'emissionFactorUnits',
},
{
title: '数据来源',
dataIndex: 'dataSources',
},
{
title: '数据库',
dataIndex: 'carbonDatabase',
},
{
title: '参考文献',
dataIndex: 'reference',
},
{
title: '引用数量',
dataIndex: 'numberOfReferences',
},
],
columnActions: {
title: '操作',
actions: [
{
label: '编辑',
name: 'userEdit',
handle: (record: any) => {
userAuthList.value.splice(0);
setTimeout(() => {
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.creatOrUpdate, formData).then(() => {
mainRef.value?.nsTableRef.reload();
visible.value = false;
NsMessage.success('操作成功');
});
};
visible.value = true;
},
},
{
label: '删除',
name: 'userDelete',
dynamicParams: { ids: 'id[]' },
confirm: true,
isReload: true,
api: carbonEmissionFactorLibrary.del,
},
],
},
rowKey: 'id',
});
</script>
<style lang="less" scoped>
.main {
background-color: @ns-content-bg;
@@ -540,13 +280,10 @@ import { log } from 'node:console';
}
.left {
width: 300px;
// max-height: calc(100vh - 96px);
margin-right: @ns-gap;
min-width: fit-content;
> div {
border-radius: @ns-border-radius;
background-color: @white;
// box-shadow: @ns-content-box-shadow;
flex: 1;
}
@@ -565,12 +302,14 @@ import { log } from 'node:console';
.right {
flex: 1;
min-width: 0;
height: 100%;
background: #ffffff;
}
::v-deep .ant-table-container{
padding: 0px 16px;
}
.top {
overflow: auto;
// height: 50%;
// border-bottom: 5px solid rgb(229, 235, 240);
// overflow-y: auto;
}
.ns-form-title{
font-weight: bold;
@@ -580,4 +319,24 @@ import { log } from 'node:console';
padding-bottom: 10px;
border-bottom: 1px solid #e9e9e9;
}
::v-deep .ant-table-title{
display: flex;
height: 100%;
align-items: center;
}
.ns-table-title{
font-weight: bold;
}
.buttonGroup{
margin-left: 1vw;
width: 5vw;
display: flex;
justify-content: space-around;
}
</style>
<style scoped>
th.column-money,
td.column-money {
text-align: right !important;
}
</style>

View File

@@ -0,0 +1,141 @@
.out-dialog {
position: fixed;
right: 496px;
width: 500px;
height: 100%;
top: 0;
bottom: 0;
margin: auto;
box-sizing: border-box;
color: rgb(255, 83, 0);
background: black;
display: flex;
padding: 25px;
flex-direction: column;
.content {
overflow-y: scroll;
.div-operation {
display: inline-block;
width: 3px;
height: 13px;
opacity: 1;
border-radius: 1px;
background: rgba(67, 136, 251, 1);
}
.text-operation {
display: inline-block;
color: rgba(255, 255, 255, 1);
font-size: 16px;
font-weight: 700;
margin-left: 5px;
}
.j-box {
background-color: #000;
opacity: 1;
z-index: 99999;
overflow-y: scroll;
.journal {
padding: 1% 3%;
width: 100%;
height: 150px;
background-color: rgba(0, 0, 0);
border-radius: 12px;
border: 2px solid transparent;
border-image: linear-gradient(to bottom, #0077ff, #00f6ff, #000000) 1;
}
.imgText {
display: flex;
align-items: center;
justify-content: space-between;
.ztzm {
display: flex;
align-items: center;
}
.cxbtn {
color: rgba(255, 255, 255, 1);
border: none;
border-radius: 6px;
width: 59.79px;
height: 32px;
opacity: 1;
background: linear-gradient(
180deg,
rgba(255, 187, 0, 1) 0%,
rgba(255, 112, 3, 1) 91.21%,
rgba(255, 129, 3, 1) 100%
);
}
}
.btn-box {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
grid-row-gap: 15px;
.btn-item {
text-align: center;
display: flex;
align-content: space-between;
.left {
width: 70px;
height: 35px;
line-height: 35px;
border-radius: 4px;
background: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
);
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
}
.right {
width: 140px;
height: 35px;
line-height: 35px;
span {
vertical-align: middle;
}
img {
padding: 0 5px;
}
}
}
}
}
}
.button-box {
width: 100%;
box-sizing: border-box;
padding: 10px;
height: 60px;
position: absolute;
background-color: transparent;
bottom: 0;
left: 0;
right: 0;
.execute, .cancel {
margin-right: 10px;
width: 74px;
height: 40px;
opacity: 1;
cursor: pointer;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
border: 0;
margin-left: 10px;
}
.execute {
background: rgb(67, 136, 251);
color: white;
}
.cancel {
background: white;
color: black;
}
}
}

View File

@@ -1,156 +0,0 @@
// 页面容器
.lighting-box {
// width: 100%;
// height: 100%;
position: relative;
// background: linear-gradient(#badaff, #8cabeb, #7095de);
// 照明设备功能总容器
.lighting-img-box {
position: relative;
width: 1280px;
height: 720px;
user-select: none;
background-image: url(../image/bg.jpg);
// 由于背景是俯视图,会产生有交点的透视效果,故使用透视属性
perspective: 1000px;
perspective-origin: 850px -160px;
// 左上角区域切换功能
.btn-box {
width: 120px;
position: sticky;
top: 10px;
left: 10px;
display: flex;
flex-direction: column;
gap: 8px;
.btn-item {
cursor: pointer;
width: 100%;
height: 40px;
border-radius: 4px;
background: rgba(39, 120, 255, 1);
border: 1px solid rgba(51, 199, 255, 1);
box-shadow: 0px 10px 15px rgba(0, 54, 136, 0.3);
font-size: 18px;
color: white;
}
.btn-item:hover {
color: black;
}
}
// 大区分区
.area{
position: absolute;
bottom: 170px;
left: 240px;
width: 780px;
height: 240px;
transform: rotateX(79deg) rotateZ(-22deg) skew(29deg);
display: flex;
gap: 8px;
.area1 {
width: 170px;
background: rgba(0, 251, 91, 0.3);
border: 2px solid rgb(0, 251, 91);
display: flex;
}
.area2 {
width: 240px;
background: rgba(255, 165, 0, 0.3);
border: 2px solid rgb(255, 165, 0);
display: flex;
}
.area3 {
width: 110px;
background: rgba(255, 0, 0, 0.3);
border: 2px solid rgb(255, 0, 0);
}
.area4 {
flex: 1;
background: rgba(80, 236, 244, 0.3);
border: 2px solid rgb(80, 236, 244);
}
// .area-item:hover {
// transform: scale(1.05);
// }
.area-item {
cursor: pointer;
transition: all ease 0.2s;
>.light-group {
height: 100%;
flex: 1;
display:flex;
justify-content: center;
align-items: center;
.group-shadow1 {
width: 35px;
height: 150px;
border-radius: 20px;
background: rgba(0, 0, 0, 0.1);
}
.group-shadow2 {
width: 35px;
height: 180px;
border-radius: 20px;
background: rgba(0, 0, 0, 0.1);
}
.group-shadow3 {
width: 40px;
height: 180px;
border-radius: 20px;
background: rgba(0, 0, 0, 0.1);
}
.group-shadow4 {
width: 40px;
height: 160px;
border-radius: 20px;
background: rgba(0, 0, 0, 0.1);
}
}
// .group-shadow {
// transition: all ease 0.2s;
// }
// .group-shadow:hover {
// transform: scale(1.05);
// }
}
}
}
}
// 总容器与抽屉按钮
.ns-content-main {
position: relative;
// 抽屉打开按钮
.drawer-box-in {
width: 30px;
height: 40px;
border-radius: 2px;
position: fixed;
right: 0;
top: 0;
bottom: 0;
margin: auto;
background: rgba(0, 0 ,0 ,0.5);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
// 抽屉关闭按钮
.drawer-box-out {
width: 30px;
height: 40px;
border-radius: 2px;
position: fixed;
right: 496px;
top: 0;
bottom: 0;
margin: auto;
background: rgba(0, 0 ,0 ,0.5);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
}

View File

@@ -3,19 +3,19 @@
<div class="lighting-img-box">
<!-- 左上角区域切换 -->
<div class="btn-box">
<button class="btn-item">1F</button>
<button class="btn-item">2F</button>
<button class="btn-item">站台</button>
<button class="btn-item" @click=changeArea(1)>1F</button>
<button class="btn-item" @click=changeArea(2)>2F</button>
<button class="btn-item" @click=changeArea(3)>站台</button>
</div>
<!-- 楼层区域 -->
<div class="area">
<div
v-for="(item, index) in treeData"
:class="computedClass(item.id)"
@click="getArea(item.id)"
:class="computedClass(String(item.id))"
@click="getArea(item)"
:key="index">
<div v-for="(v, i) in item.children" :key="i" class="light-group">
<div class="group-shadow group-shadow1" :class="computedClass(v.id)" @click.stop="getArea(v.id)"></div>
<div class="group-shadow group-shadow1" :class="computedClass(v.id)" @click.stop="getArea(v)"></div>
</div>
</div>
</div>
@@ -40,16 +40,18 @@
placement="right"
:body-style="{ background: 'rgba(0, 0, 0)', opacity: 0.8 }"
:closable="false"
id="Odrawer"
id="drawer"
:maskStyle="{ 'background-color': 'rgba(0, 0, 0, 0)' }">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="控制面板">
<tabs1 @changeArea="getArea" :treeData="treeData"></tabs1>
</a-tab-pane>
<a-tab-pane key="2" tab="计划列表" force-render>
<tabs2></tabs2>
</a-tab-pane>
<a-tab-pane key="3" tab="日志">
<tabs3></tabs3>
</a-tab-pane>
<a-tab-pane key="3" tab="日志"></a-tab-pane>
</a-tabs>
</a-drawer>
</div>
@@ -61,15 +63,17 @@ import { ref } from 'vue';
import { treeData } from './treeData'
import light from './light.vue';
import tabs1 from './tabs1.vue'
import tabs2 from './tabs2.vue'
import tabs3 from './tabs3.vue'
// ICON
import {
DoubleLeftOutlined,
DoubleRightOutlined
} from '@ant-design/icons-vue';
// 分区 - 当前选择的分区 - 默认选择一区
let area = ref(['1'])
// 分区 - 分区小灯泡
// 分区 - 当前选择的分区序号 - 默认选择一区
const area = ref(['1'])
// 分区 - 分区小灯泡 - 此处位置需前端写死
const bulbs = ref([
{
styleText: { left: '190px', bottom: '200px' },
@@ -121,6 +125,16 @@ const bulbs = ref([
},
])
// 分区 - 左上角分区切换
const changeArea = (area: number) => {
if (area == 1) {
console.log('1F')
} else if (area == 2) {
console.log('2F')
} else if (area == 3) {
console.log('站台')
}
}
// 分区 - 单个分区切换
const getArea = (result: any) => {
// 如果传入的值是数组
@@ -132,16 +146,14 @@ const getArea = (result: any) => {
area.value[0] = String(result)
}
}
// 分区 - 多个分区切换,只有照明回路能够触发多个分区
// 分区 - 样式函数
const computedClass = (number: number) => {
if (area.value.indexOf(number) != -1) {
return `isActive area-item area${number}`
const computedClass = (string: string) => {
if (area.value.indexOf(string) != -1) {
return `isActive area-item area${string}`
} else {
return `area-item area${number}`
return `area-item area${string}`
}
}
// 抽屉 - 当前选择的tab
let activeKey = ref('1');
// 抽屉 - 打开状态
@@ -153,7 +165,7 @@ const toggleDrawer = () => {
</script>
<style lang="less" scoped>
@import "./indexs.less";
@import "./index.less";
.isActive {
border: 3px solid white !important;
}

View File

@@ -1162,16 +1162,11 @@
selectedButton.value = null;
showControlMode.value = false;
showControlScene.value = false;
selectedButton3.value = null;
selectedButton4.value = null;
selectedButton3.value = '';
selectedButton4.value = '';
};
// 操作日志
const logModalVisible = ref(false);
const handlerefClick1 = () => {
console.log(1111);
executevisible.value = true;
};
let trindex = ref('-1');
const handleRowClick = (index: any) => {
@@ -1192,9 +1187,7 @@
};
// 添加模态框
const addvisible = ref<boolean>(false);
const addModal = () => {
addvisible.value = true;
};
// 穿梭框
interface MockData {
key: string;

View File

@@ -12,7 +12,7 @@
<div class="area">
<template v-if="!showAllButtonsArea">
<button v-for="(button, index) in limitedButtons1" :key="index"
:class="{ btn: true, selected: button.selected }" @click="selectButton(button, index)">
:class="{ btn: true, selected: button.selected }" @click="selectButton(button)">
{{ button.label }}
</button>
<div style="margin-top: 10px">
@@ -20,8 +20,8 @@
</div>
</template>
<template v-else>
<button v-for="(button, index) in props.treeData" :key="index"
:class="{ btn: true, selected: button.selected }" @click="selectButton(button)">
<button v-for="(button, index) in props.treeData" :key="index" :class="{ btn: true, selected: button.selected }"
@click="selectButton(button)">
{{ button.label }}
</button>
<div style="margin-top: 10px">
@@ -36,7 +36,7 @@
<div class="circuit-tab"></div>
<span class="circuit-text">照明回路</span>
<div class="btn2">
<button class="openplan" :class="{ enabled2: isPlanEnabled2, disabled2: !isPlanEnabled2 }" @click="togglePlan2">
<button class="openPlan" :class="{ enabled2: isPlanEnabled2, disabled2: !isPlanEnabled2 }" @click="togglePlan2">
{{ isPlanEnabled2 ? '启用面板' : '禁用面板' }}
</button>
<a-switch v-model:checked="selectAllCheckbox" :disabled="singleSelection" :class="{
@@ -49,7 +49,7 @@
</button>
</div>
</div>
<div class="btnarea" v-if="selectedButton">
<div class="btnArea" v-if="selectedButton">
<template v-if="!showAllButtons">
<button v-for="(button2, index) in limitedButtons2" :key="index"
:class="{ btn: true, selected: button2.selected }" class="zmhlbtn" @click="toggleSelection(button2)">
@@ -80,7 +80,7 @@
<!-- 控制模式按钮部分 -->
<div class="control-mode-btn-area" v-show="showControlMode">
<button v-for="(button3, index) in controlType" :key="index"
:class="{ btn: true, selected: button3.type === thisButtonType }" @click="selectButton3(button3)">
:class="{ btn: true, selected: button3.type == thisButton.type }" @click="selectButton3(button3)">
{{ button3.name }}
</button>
</div>
@@ -119,7 +119,7 @@
:style="{ height: '50px' }">
<td>{{ row.num }}</td>
<td>{{ row.light }}</td>
<td>{{ row.tempruter }}</td>
<td>{{ row.temperature }}</td>
</tr>
</tbody>
</table>
@@ -130,170 +130,71 @@
<button class="flushed" @click="refresh">刷新</button>
<button class="execute" @click="showModal">执行</button>
</div>
<!-- <a-modal :v-if="cxlist.length === 0" v-model:visible="executevisible" width="500px" height="792px" okText="执行"
:closable="false" @ok="handleOk2" :mask="false" :bodyStyle="{
opacity: 1,
background: ' rgba(0,0,0)',
color: 'rgba(255,83,0)',
boxSizing: 'borderBox',
display: 'flex',
flexDirection: 'column',
position: 'fixed',
right: '496px',
top: '0',
}">
<div :v-if="cxlist.length === 0" style="height: 792px">
<div class="out-dialog" v-if="executeVisible">
<div class="content" v-if="executeVisible">
<div>
<div class="div-operation"></div>
<span class="text-operation">变更内容 </span>
</div>
<div class="jbox" v-for="item in cxlist" :key="item.id">
<div class="j-box" v-for="item in cxList" :key="item.id">
<div class="journal" style="margin-top: 20px">
<div class="imgText">
<div class="zjzm">
<img class="titleimg" src="/asset/image//bulbLogo/21961.png" alt="" />&nbsp;
<span class="titletext" style="font-size: 20px; font-weight: 500; color: rgba(255, 255, 255, 1)">{{
item.name }}</span>
<img class="title-img" src="/asset/image//bulbLogo/21961.png" alt="" />&nbsp;
<span class="title-text" style="font-size: 20px; font-weight: 500; color: rgba(255, 255, 255, 1)">{{ item.name }}</span>
</div>
<button class="cxbtn" @click="delbtn(item.id)">撤销</button>
<button class="cxbtn" @click="delBtn(item.id)">撤销</button>
</div>
<div class="control-btn">
<button style="
width: 96px;
height: 40px;
border-radius: 4px;
background: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
);
border: 1px solid rgba(95, 154, 251, 1);
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
">
控制模式
</button>
<span style="
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
margin-left: 12px;
margin-right: 8px;
">手动</span>
<div class="btn-box">
<div class="btn-item">
<div class="left">控制模式</div>
<div class="right">
<span>手动</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span style="
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
margin-left: 8px;
margin-right: 40px;
">自动</span>
<span>自动</span>
</div>
<div class="light-btn">
<button style="
width: 68px;
height: 40px;
border-radius: 4px;
background: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
);
border: 1px solid rgba(95, 154, 251, 1);
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
">
</div>
<div class="btn-item">
<div class="left">
亮度
</button>
<span style="
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
margin-left: 12px;
margin-right: 8px;
">100lux</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span style="
color: rgba(243, 97, 99, 1);
font-size: 14px;
font-weight: 400;
margin-left: 8px;
">30lux</span>
</div>
<div class="control-area-btn">
<button style="
width: 96px;
height: 40px;
border-radius: 4px;
background: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
);
border: 1px solid rgba(95, 154, 251, 1);
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
">
<div class="right">
<span>100lux</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>30lux</span>
</div>
</div>
<div class="btn-item">
<div class="left">
控制场景
</button>
<span style="
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
margin-left: 12px;
margin-right: 8px;
">手动</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span style="
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
margin-left: 8px;
margin-right: 40px;
">自动</span>
</div>
<div class="temp-btn">
<button style="
width: 68px;
height: 40px;
border-radius: 4px;
background: linear-gradient(
180deg,
rgba(1, 206, 255, 1) 0%,
rgba(0, 150, 229, 1) 100%
);
border: 1px solid rgba(95, 154, 251, 1);
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
">
<div class="right">
<span>手动</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>自动</span>
</div>
</div>
<div class="btn-item">
<div class="left">
色温
</button>
<span style="
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
margin-left: 12px;
margin-right: 8px;
">4000k</span>
</div>
<div class="right">
<span>4000k</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span style="
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: 400;
margin-left: 8px;
">3800k</span>
<span>3800k</span>
</div>
</div>
</div>
</div>
</a-modal> -->
</div>
</div>
<div style="width: 100%; height: 60px;"></div>
<div class="button-box">
<button class="cancel" @click="executeVisible = false">取消</button>
<button class="execute">执行</button>
</div>
</div>
</template>
<script setup lang="ts">
@@ -307,19 +208,23 @@ import {
// 初始化
onMounted(() => {
const tree = props.treeData
const data = props.treeData[0]
// 默认选中第一个分区
tree[0].selected = true
data.selected = true
// 默认展示第一个分区的回路
buttons2.value = tree[0].children
buttons2.value = data.children
})
// 与父组件的交互 ===================================================================
const props = defineProps({
// 分区结构(照明区域 > 照明回路)
treeData: {
type: Array
type: Array,
default: []
},
line: {
type: Array
}
});
const emit = defineEmits(['changeArea']);
@@ -336,20 +241,13 @@ const togglePlan = () => {
const showAllButtonsArea = ref(false);
// 照明区域 - 分区 - 被选中的按钮 默认为1
const selectedButton = ref('1');
// 照明区域 - 区域切换(按钮点击)事件
const selectButton = (button: any, index: number) => {
// 照明区域 - 区域切换(按钮点击-单选)事件
const selectButton = (button: any) => {
// 当前选中按钮
selectedButton.value = button;
// 分区只能单选
props.treeData.forEach(item => {
item.selected = false
})
// 重置
initMenu(1)
button.selected = true
// 切换1级分区时重置二级分区
buttons2.value.forEach(item => {
item.selected = false
})
showControlMode.value = false
buttons2.value = button.children
// 反选area
emit('changeArea', []);
@@ -375,16 +273,7 @@ const togglePlan2 = () => {
isPlanEnabled2.value = !isPlanEnabled2.value;
// 禁用 - 则取消选中,增加禁止图标
if (isPlanEnabled2.value) {
item.selected = false;
item.stop = true
let level1 = String(selectedButton.value.id)
let arr = [level1]
buttons2.value.forEach(item => {
if (item.selected) {
arr.push(item.id)
}
})
emit('changeArea', arr)
// 启用 - 去除禁止图标
} else {
item.stop = false
@@ -410,10 +299,8 @@ const toggleAllSelection = () => {
// 全选
if (selectAllCheckbox.value) {
buttons2.value.forEach((item) => {
if (!item.stop) {
item.selected = true;
arr.push(item.id)
}
});
// 全不选
} else {
@@ -431,7 +318,7 @@ const toggleSelection = (button: any) => {
zmhlid.value = button.id
// 如果已被锁定,则展示禁用/启用状态跳出
if (button.stop) {
return isPlanEnabled2.value = true
isPlanEnabled2.value = true
} else {
isPlanEnabled2.value = false
}
@@ -455,17 +342,22 @@ const toggleSelection = (button: any) => {
arr.push(item.id)
}
});
console.log(arr)
emit('changeArea', arr)
}
// 用于展示控制模式
if (button.selected) {
// 发生了选中事件
showControlMode.value = true
thisButtonType.value = button.type
thisButton.value = button
if (button.type == 3) {
showControlScene.value = true
} else {
showControlScene.value = false
}
} else {
// 未发生选中 或 多选被取消
showControlMode.value = false
thisButton.value = null
thisButton.value = { type: '0' }
}
};
@@ -479,16 +371,12 @@ const buttons2 = ref([]);
// 控制模式 =======================================================================
// 控制模式 - 决定该区域是否显示
const showControlMode = ref(false);
const thisButtonType = ref(null)
const thisButton = ref(null)
// 控制模式 - 当前选择按钮
const selectedButton3 = ref('');
const thisButton = ref({ type: '0' })
// 控制模式 - 按钮切换
const selectButton3 = (button3) => {
thisButton.value.type = button3.type
thisButtonType.value = button3.type
showControlScene.value = button3.name === '手动';
selectedButton4.value = ''; // 清空选中的控制场景按钮
selectedButton4.value = null; // 清空选中的控制场景按钮
};
// 控制场景 =======================================================================
@@ -502,7 +390,7 @@ const controlSceneButtons = ref([
{ label: '客流高峰', selected: false },
]);
// 控制场景 - 当前选择按钮
const selectedButton4 = ref('');
const selectedButton4 = ref(null);
// 控制场景 - 按钮切换
const selectButton4 = (button4) => {
selectedButton4.value = button4.label;
@@ -510,33 +398,112 @@ const selectButton4 = (button4) => {
// 灯具参数 ========================================================================
// 灯具参数 - 表格数据
const lightSource = ref([{ num: '8/10', light: '100', tempruter: '4200' }]);
const lightSource = ref([{ num: '8/10', light: '100', temperature: '4200' }]);
let trIndex = ref('-1');
const handleRowClick = (index: any) => {
trindex.value = index;
if (index === trindex.value) {
trIndex.value = index;
if (index === trIndex.value) {
console.log('tri');
}
// 显示模态框
logModalVisible.value = true;
};
};
// 底部表格 ========================================================================
// 内侧弹窗 ========================================================================
const cxList = ref([
{
id: '1',
name: '站厅照明 1区',
manual: '手动',
automatic: '自动',
brightness: '100lux',
brightness2: '30lux',
manual2: '手动',
automatic2: '自动',
brightness3: '4000k',
brightness4: '3800k',
},
{
id: '2',
name: '站厅照明 2区',
manual: '手动',
automatic: '自动',
brightness: '100lux',
brightness2: '30lux',
manual2: '手动',
automatic2: '自动',
brightness3: '4000k',
brightness4: '3800k',
},
{
id: '3',
name: '站厅照明 3区',
manual: '手动',
automatic: '自动',
brightness: '100lux',
brightness2: '30lux',
manual2: '手动',
automatic2: '自动',
brightness3: '4000k',
brightness4: '3800k',
},
]);
// const cxList = cxList.value.concat(cxList.value)
//撤销
const delBtn = (id: any) => {
console.log(id);
cxList.value.pop(id);
console.log(cxList.value.length);
if (cxList.value.length === 0) {
executeVisible.value = false;
}
};
// 其他业务 ========================================================================
// 用于初始化5级联动的菜单
const initMenu = (tier: number) => {
// 1 = 照明区域 - 切换时,重置以下所有菜单
if (tier == 1) {
// 切换前,清除照明回路的选中状态
buttons2.value.forEach(item => {
item.selected = false
})
// 切换后,清空所有选中状态
props.treeData.forEach(item => {
item.selected = false
})
// 控制模式 隐藏
showControlMode.value = false;
// 控制场景 隐藏
showControlScene.value = false;
// 灯具参数 隐藏
selectedButton4.value = null;
} else if (tier == 2) {
// 控制场景 隐藏
showControlScene.value = false;
// 灯具参数 隐藏
selectedButton4.value = null;
}
}
// 刷新
const refresh = () => {
selectedButton.value = {};
executeVisible.value = false
selectedButton.value = '1';
emit('changeArea', []);
emit('changeArea', [1]);
showControlMode.value = false;
showControlScene.value = false;
selectedButton3.value = null;
selectedButton4.value = null;
};
// 执行确认
const executevisible = ref<boolean>(false);
const showModal = () => {
executevisible.value = true;
};
const executeVisible = ref<boolean>(false);
const showModal = () => {
executeVisible.value = true;
};
</script>
<style lang="less" scoped>
@import "./dialogStyle.less";
// 抽屉
.drawer-item {
@@ -614,7 +581,7 @@ const executevisible = ref<boolean>(false);
cursor: not-allowed;
}
.openplan.enabled2 {
.openPlan.enabled2 {
border: none;
font-size: 14px;
font-weight: 400;
@@ -627,7 +594,7 @@ const executevisible = ref<boolean>(false);
color: white;
}
.openplan.disabled2 {
.openPlan.disabled2 {
border: none;
font-size: 14px;
font-weight: 400;
@@ -639,7 +606,7 @@ const executevisible = ref<boolean>(false);
background-color: red;
}
.openplan:disabled {
.openPlan:disabled {
cursor: not-allowed;
}
@@ -714,7 +681,7 @@ const executevisible = ref<boolean>(false);
}
.area,
.btnarea,
.btnArea,
.control-mode-btn-area,
.control-scene-btn-area {
margin-left: -17px;
@@ -828,7 +795,6 @@ const executevisible = ref<boolean>(false);
position: fixed;
bottom: 0;
right: 0;
margin-bottom: 10px;
}
.execute {
@@ -836,6 +802,7 @@ const executevisible = ref<boolean>(false);
width: 74px;
height: 40px;
opacity: 1;
cursor: pointer;
border-radius: 4px;
background: rgba(67, 136, 251, 1);
font-size: 14px;
@@ -848,6 +815,7 @@ const executevisible = ref<boolean>(false);
.flushed {
width: 74px;
height: 40px;
cursor: pointer;
opacity: 1;
border-radius: 4px;
font-size: 14px;
@@ -857,50 +825,6 @@ const executevisible = ref<boolean>(false);
border: 1px solid rgba(193, 197, 204, 1);
}
.tabreboot,
.tabdelete {
border: none;
background-color: rgba(0, 0, 0, 0);
font-size: 14px;
font-weight: 400;
letter-spacing: 0;
line-height: 20px;
color: rgba(67, 136, 251, 1);
}
.tabreboot {
margin-right: 8px;
}
.add {
width: 74px;
height: 40px;
opacity: 1;
border-radius: 4px;
background: rgba(67, 136, 251, 1);
border: rgba(67, 136, 251, 1);
font-size: 14px;
font-weight: 400;
color: white;
}
.divadd {
width: 100%;
height: 64px;
display: flex;
justify-content: flex-end;
align-items: center;
position: fixed;
bottom: 0;
right: 0;
margin-bottom: 10px;
margin-right: 20px;
}
.atable {
margin-top: 20px;
}
:deep(.ant-table-pagination) {
visibility: hidden;
}
@@ -1006,9 +930,12 @@ const executevisible = ref<boolean>(false);
display: flex;
align-items: center;
}
.zmhlbtn {
position: relative;
}
// 禁用图标
.anticon-stop {
position: absolute;
right: 3px;

View File

@@ -0,0 +1,216 @@
<template>
<div class="div-add">
<button class="add" @click="addModal">添加</button>
</div>
<table class="custom-table table1">
<thead>
<tr :style="{ background: 'rgba(35,45,69)' }">
<th>序号</th>
<th>执行时间</th>
<th>计划名称</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in dataSource" :key="index">
<td>{{ row.key }}</td>
<td>{{ row.data }}</td>
<td>{{ row.planName }}</td>
<td v-if="row.status === '待执行'">
<button
style="
font-size: 12px;
background: rgba(57, 215, 187, 0.1);
color: rgb(57, 215, 187);
border: 1px solid rgb(57, 215, 187);
">
{{ row.status }}
</button>
</td>
<td v-if="row.status !== '待执行'">
<button
style="
font-size: 12px;
background: rgba(243, 97, 99, 0.1);
border: 1px solid rgba(243, 97, 99);
color: rgba(243, 97, 99);
">
{{ row.status }}
</button>
</td>
<td>
<button class="tabReboot" @click="handleRefClick1">重启</button>
<button class="tabDelete">删除</button>
</td>
</tr>
</tbody>
</table>
<div class="out-dialog" v-if="addVisible">
<div class="content" v-if="addVisible">
<div class="div-operation"></div>
<span class="text-operation">计划库</span>
</div>
<div style="margin-top: 20px">
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
show-search
:filter-option="filterOption"
:render="(item) => item.title"
@change="handleChange"
:style="{ color: 'rgba(255,255,255,1)' }"
@search="handleSearch"
:listStyle="{ border: '2px solid rgba(25,74,125,1)', height: 'calc(100vh - 200px)' }" />
</div>
<div style="width: 100%; height: 60px;"></div>
<div class="button-box">
<button class="cancel" @click="addVisible = false">取消</button>
<button class="execute">确定</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
// 初始化
onMounted(() => {
})
// 表格数据
const dataSource = ref([
{
key: '1',
data: '2024-05-01',
planName: '劳动节',
status: '暂停中',
},
{
key: '2',
data: '2024-05-01',
planName: '国庆节',
status: '待执行',
},
{
key: '3',
data: '2024-05-01',
planName: '元旦',
status: '待执行',
},
]);
const addVisible = ref<boolean>(false);
// 操作日志
const handleRefClick1 = () => {
alert(111)
};
const addModal = () => {
addVisible.value = true;
};
const handleChange = (keys: string[], direction: string, moveKeys: string[]) => {
console.log(keys, direction, moveKeys);
};
const handleSearch = (dir: string, value: string) => {
console.log('search:', dir, value);
};
// 穿梭框 相关业务
interface MockData {
key: string;
title: string;
description: string;
chosen: boolean;
}
const mockData = ref([
{
key: '1',
title: '计划再开',
},
{
key: '2',
title: '检修模式',
},
{
key: '3',
title: '设备变更',
},
]);
const targetKeys = ref<string[]>([]);
const filterOption = (inputValue: string, option: MockData) => {
console.log(option.description);
return option.description.indexOf(inputValue) > -1;
};
</script>
<style lang="less" scoped>
@import "./dialogStyle.less";
// 右下角添加按钮
.div-add {
height: 64px;
display: flex;
justify-content: flex-end;
align-items: center;
position: fixed;
bottom: 0;
right: 0;
margin-right: 20px;
.add {
width: 74px;
height: 40px;
opacity: 1;
border-radius: 4px;
background: rgba(67, 136, 251, 1);
border: rgba(67, 136, 251, 1);
font-size: 14px;
font-weight: 400;
color: rgba(255, 255, 255, 1);
}
}
// 表格
.custom-table {
border-collapse: collapse;
width: 416px;
height: 60px;
color: rgba(255, 255, 255, 1);
}
.custom-table th,
.custom-table td {
border: 1px solid rgba(163, 192, 243, 1);
text-align: left;
padding: 8px;
text-align: center;
}
.table1 {
margin-top: 20px;
width: 100%;
border: 1px solid rgba(255, 255, 255);
border-radius: 5px;
background: rgba(255, 255, 255, 0.1);
// 表格中的操作按钮
.tabReboot,
.tabDelete {
border: none;
background-color: rgba(0, 0, 0, 0);
font-size: 14px;
font-weight: 400;
letter-spacing: 0;
line-height: 20px;
color: rgba(67, 136, 251, 1);
}
.tabReboot {
margin-right: 8px;
}
}
::v-deep(.ant-transfer) {
// 屏蔽自带的hover效果
.ant-transfer-list-content-item:hover {
background: black;
}
}
</style>

View File

@@ -0,0 +1,255 @@
<template>
<table class="custom-table table1">
<thead>
<tr :style="{ background: 'rgba(35,45,69)' }">
<th>序号</th>
<th>执行时间</th>
<th>操作内容</th>
<th>操作人</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in dataSource1" :key="index" @click="handleRowClick(row.key)"
:class="row.key === trIndex ? 'isTrIndex' : ''">
<td>{{ row.key }}</td>
<td>{{ row.data }}</td>
<td>{{ row.planName }}</td>
<td>{{ row.status }}</td>
</tr>
</tbody>
</table>
<div class="out-dialog" v-if="logModalVisible">
<div class="content" v-if="logModalVisible">
<div>
<div class="div-operation"></div>
<span class="text-operation">变更内容 </span>
</div>
<div class="jbox" v-for="item in cxList" :key="item.id">
<div class="journal" style="margin-top: 20px">
<div class="imgText">
<div class="zjzm">
<img class="title-img" src="/asset/image//bulbLogo/21961.png" alt="" />&nbsp;
<span class="title-text" style="font-size: 20px; font-weight: 500; color: rgba(255, 255, 255, 1)">{{
item.name }}</span>
</div>
</div>
<div class="btn-box">
<div class="btn-item">
<div class="left">控制模式</div>
<div class="right">
<span>手动</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>自动</span>
</div>
</div>
<div class="btn-item">
<div class="left">
亮度
</div>
<div class="right">
<span>100lux</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>30lux</span>
</div>
</div>
<div class="btn-item">
<div class="left">
控制场景
</div>
<div class="right">
<span>手动</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>自动</span>
</div>
</div>
<div class="btn-item">
<div class="left">
色温
</div>
<div class="right">
<span>4000k</span>
<img src="/asset/image/bulbLogo/22406.png" alt="" />
<span>3800k</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="height: 60px;"></div>
<div class="button-box">
<button class="cancel" @click="logModalVisible = false">取消</button>
</div>
</div>
<div class="div-add">
<button class="add">刷新</button>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
// 初始化
onMounted(() => {
})
// 与父组件的交互 ===================================================================
const props = defineProps({
// 分区结构(照明区域 > 照明回路)
treeData: {
type: Array
},
});
const emit = defineEmits(['changeArea']);
// 表格数据
const dataSource1 = ref([
{
key: '1',
data: '2024-05-01',
planName: '计划再开',
status: '张三111',
},
{
key: '2',
data: '2024-05-01',
planName: '检修模式',
status: '李四12',
},
{
key: '3',
data: '2024-05-01',
planName: '设备变更',
status: '王五33',
},
]);
const cxList = ref([
{
id: '1',
name: '站厅照明 1区',
manual: '手动',
automatic: '自动',
brightness: '100lux',
brightness2: '30lux',
manual2: '手动',
automatic2: '自动',
brightness3: '4000k',
brightness4: '3800k',
},
{
id: '2',
name: '站厅照明 2区',
manual: '手动',
automatic: '自动',
brightness: '100lux',
brightness2: '30lux',
manual2: '手动',
automatic2: '自动',
brightness3: '4000k',
brightness4: '3800k',
},
{
id: '3',
name: '站厅照明 3区',
manual: '手动',
automatic: '自动',
brightness: '100lux',
brightness2: '30lux',
manual2: '手动',
automatic2: '自动',
brightness3: '4000k',
brightness4: '3800k',
},
]);
let trIndex = ref('-1');
const logModalVisible = ref(false);
const handleRowClick = (index: any) => {
trIndex.value = index;
if (index === trIndex.value) {
console.log('tri');
}
// 显示模态框
logModalVisible.value = true;
};
</script>
<style lang="less" scoped>
@import "./dialogStyle.less";
// 右下角添加按钮
.div-add {
height: 64px;
display: flex;
justify-content: flex-end;
align-items: center;
position: fixed;
bottom: 0;
right: 0;
margin-right: 20px;
.add {
width: 74px;
height: 40px;
opacity: 1;
border-radius: 4px;
background: rgba(67, 136, 251, 1);
border: rgba(67, 136, 251, 1);
font-size: 14px;
font-weight: 400;
color: rgba(255, 255, 255, 1);
}
}
// 表格
.custom-table {
border-collapse: collapse;
width: 416px;
height: 60px;
color: rgba(255, 255, 255, 1);
}
.custom-table th,
.custom-table td {
border: 1px solid rgba(163, 192, 243, 1);
text-align: left;
padding: 8px;
text-align: center;
}
.table1 {
margin-top: 20px;
width: 100%;
border: 1px solid rgba(255, 255, 255);
border-radius: 5px;
background: rgba(255, 255, 255, 0.1);
.tabReboot,
.tabDelete {
border: none;
background-color: rgba(0, 0, 0, 0);
font-size: 14px;
font-weight: 400;
letter-spacing: 0;
line-height: 20px;
color: rgba(67, 136, 251, 1);
}
.tabReboot {
margin-right: 8px;
}
.isTrIndex {
background: rgba(67, 136, 251, 1);
}
}
::v-deep(.ant-transfer) {
// 屏蔽自带的hover效果
.ant-transfer-list-content-item:hover {
background: black;
}
}
</style>

View File

@@ -1,8 +1,9 @@
import { NsSelect } from './../../../../../lib/component/form/select/index';
import { dateUtil } from '/nerv-lib/util/date-util';
import data from './mock.json';
import { http } from '/nerv-lib/util';
import { ref } from 'vue';
import { group } from '/@/api/deviceManage';
import { device, group } from '/@/api/deviceManage';
import { dict } from '/@/api';
import { origanizemanage } from '/@/api/origanizemanage';
const tableCalKeyMap = [
@@ -122,7 +123,7 @@ export const formSchema = [
immediate: true,
// resultField: 'data.COUNT_POINT',
labelField: 'cnValue',
valueField: 'cnValue',
valueField: 'dicKey',
},
rules: [
{
@@ -198,7 +199,7 @@ export const treeConfig = (orgId) => {
immediate: true,
// resultField: 'data.ENERGY_TYPE',
labelField: 'cnValue',
valueField: 'cnValue',
valueField: 'dicKey',
placeholder: '请选择能耗种类',
autoSelectFirst: true,
},
@@ -303,9 +304,22 @@ export const tableConfig = (el, elGroup, elFormula, defaultParams) => {
schemas: [
{
field: 'orgName',
component: 'NsInput',
component: 'NsSelectApi',
defaultParams: defaultParams.value,
componentProps: {
placeholder: '请选择公司',
mode: 'multiple',
api: group.dropGroupFilter,
resultField: 'data',
params: { filterField: 'ORG' },
labelField: 'orgName',
valueField: 'orgId',
filterOption: (input: string, option: any) => {
return option.deviceName.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
showSearch: true,
dropdownReload: true,
allowClear: true,
},
},
{
@@ -433,11 +447,23 @@ export const tableConfigCal = (el, elGroup, elFormula, defaultParams) => {
formConfig: {
schemas: [
{
field: 'name',
label: '公司',
component: 'NsInput',
field: 'orgIds',
component: 'NsSelectApi',
defaultParams: defaultParams.value,
componentProps: {
placeholder: '请选择公司',
mode: 'multiple',
api: group.dropGroupInfoFilter,
resultField: 'data',
params: { filterField: 'ORG' },
labelField: 'orgName',
valueField: 'orgId',
filterOption: (input: string, option: any) => {
return option.deviceName.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
showSearch: true,
dropdownReload: true,
allowClear: true,
},
},
{
@@ -455,17 +481,40 @@ export const tableConfigCal = (el, elGroup, elFormula, defaultParams) => {
},
},
{
field: 'provider',
component: 'NsInput',
field: 'deviceStatus',
component: 'NsSelectApi',
componentProps: {
placeholder: '请输入设备状态',
placeholder: '请选择设备状态',
api: () => dict({ params: { dicKey: 'DEVICE_STATUS' } }),
immediate: true,
labelField: 'cnValue',
valueField: 'dicKey',
},
},
{
field: 'deviceNameType',
component: 'NsInput',
component: 'NsSelectApi',
defaultParams: defaultParams.value,
componentProps: {
placeholder: '请输入设备型号',
placeholder: '请选择设备型号',
mode: 'multiple',
api: (params: any) => {
return http.post(group.dropGroupInfoFilter, params).then((res: any) => {
const result = [...new Set(res.data)];
return { data: result };
});
},
resultField: 'data',
params: { filterField: 'DEVICE_TYPE' },
// labelField: 'orgName',
// valueField: 'orgId',
filterOption: (input: string, option: any) => {
return option.deviceName.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
showSearch: true,
dropdownReload: true,
allowClear: true,
},
},
{

View File

@@ -104,7 +104,7 @@
item['key'] = item.id.toString();
return item;
});
targetKeys.value = res.data.linkGroups?.map((item) => {
targetKeys.value = res.data.linkDeviceInfos?.map((item) => {
return item.id.toString();
});
});

View File

@@ -7,6 +7,7 @@
:cancel="() => (visible = false)"
placement="right">
<ns-form
ref="editGroupRef"
:schemas="schemas(inputDisabled)"
:model="model"
formLayout="vertical"
@@ -15,7 +16,7 @@
<template #addonAfter="data">
<template v-if="data.field === 'groupName'">
<div class="iconOP">
<EditOutlined v-if="inputDisabled" @click="() => (inputDisabled = false)" />
<EditOutlined v-if="inputDisabled" @click="editGroup" />
<CheckOutlined v-else @click="op" />
</div>
</template>
@@ -53,6 +54,7 @@
const emit = defineEmits(['sure']);
const props = defineProps({ params: Object });
const visible = ref(false);
const editGroupRef = ref();
const model = ref({});
const inputDisabled = ref(true);
const selectedRowKeys = ref([]);
@@ -63,6 +65,7 @@
};
const startCararcter = 65;
// 点击新增按钮新增分组
const addGroup = () => {
opType = true;
getCode();
@@ -71,20 +74,31 @@
model.value = { groupName: '' };
// if(mockDataSource.value.length)
};
const op = () => {
inputDisabled.value = !inputDisabled.value;
add();
};
//新增
const editGroup = () => {
if (!selectedRowKeys.value?.length) {
NsMessage.warn('请先选择需要编辑的分组');
return;
}
inputDisabled.value = false;
};
//点击对钩按钮处理保存逻辑
const add = () => {
const finalParams = { ...props.params, ...model.value };
if (opType) {
delete finalParams.id;
}
editGroupRef.value.triggerSubmit().then(() => {
http.post(group.saveComputeGroup, finalParams).then(() => {
getTableData();
NsMessage.success('操作成功');
});
});
};
const delGroup = (id) => {

View File

@@ -16,7 +16,14 @@
<template #title="data">
<div class="treeRow">
<div>
<ns-icon :name="data.pointType !== '计算节点' ? 'fenzujiedian' : 'jisuanjiedian'" />
<ns-icon
:name="
data?.id === 'all'
? 'common'
: data.pointType === 'GROUPING_NODE'
? 'fenzujiedian'
: 'jisuanjiedian'
" />
<span style="padding-left: 8px">{{ data.pointName }}</span>
</div>
<a-dropdown>
@@ -64,7 +71,12 @@
const treeRef = ref();
const defaultType = ref(true);
const result = JSON.parse(sessionStorage.getItem('ORGID')!);
const defaultParams = ref({ orgId: result, energyType: '用电量', id: '', hxDeviceGroupId: '' });
const defaultParams = ref({
orgId: result,
energyType: 'ELECTRICITY_USAGE',
id: '',
hxDeviceGroupId: '',
});
const config = tableConfig(editDrawerRef, editGroupRef, editFormulaRef, defaultParams);
const configCal = tableConfigCal(editDrawerCalRef, editGroupRef, editFormulaRef, defaultParams);
@@ -165,7 +177,7 @@
defaultParams.value.energyType = energyType;
defaultParams.value.id = id;
defaultParams.value.hxDeviceGroupId = id;
defaultType.value = pointType === '分组节点';
defaultType.value = pointType === 'GROUPING_NODE';
defaultType.value
? tableRef.value?.nsTableRef.reload()
: tableCalRef.value?.nsTableRef.reload();
@@ -215,9 +227,16 @@
}
}
.treeRow {
.common-style {
display: flex;
justify-content: space-between;
align-items: center;
}
.treeRow {
.common-style;
> div:first-child {
.common-style;
}
}
</style>

View File

@@ -138,9 +138,21 @@ export const tableConfig = (orgId) => {
{
field: 'deviceName',
label: '设备名称',
component: 'NsInput',
component: 'NsSelectApi',
componentProps: {
placeholder: '请输入设备名称',
placeholder: '请选择设备名称',
api: device.dropArea,
resultField: 'data',
params: { orgId, filterField: 'DEVICE_NAME_FACTORY' },
labelField: 'deviceName',
valueField: 'deviceName',
filterOption: (input: string, option: any) => {
return option.deviceName.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
showSearch: true,
immediate: true,
dropdownReload: true,
allowClear: true,
},
},
{
@@ -166,7 +178,7 @@ export const tableConfig = (orgId) => {
loadData: (selectedOptions, options) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
if (!selectedOptions.length) {
http.post(device.dropArea, { orgId }).then((res) => {
http.post(device.dropArea, { orgId, filterField: 'DEVICE_AREA' }).then((res) => {
options.value = res.data?.map((item) => {
return { label: item, value: item, children: [], isLeaf: false };
});
@@ -175,7 +187,9 @@ export const tableConfig = (orgId) => {
const value = targetOption?.value;
if (targetOption) {
targetOption.loading = true;
http.post(device.dropArea, { device1Area: value, orgId }).then((res) => {
http
.post(device.dropArea, { device1Area: value, orgId, filterField: 'DEVICE_AREA' })
.then((res) => {
targetOption.loading = false;
targetOption.children = res.data?.map((item) => {
return { label: item, value: item, children: [], isLeaf: true };
@@ -188,15 +202,21 @@ export const tableConfig = (orgId) => {
{
field: 'manufacturer',
label: '设备厂商',
component: 'NsInput',
component: 'NsSelectApi',
componentProps: {
placeholder: '请输入设备厂商',
options: [
{
label: '全部',
value: '',
placeholder: '请选择设备厂商',
api: device.dropArea,
resultField: 'data',
params: { orgId, filterField: 'DEVICE_NAME_FACTORY' },
labelField: 'manufacturer',
valueField: 'manufacturer',
filterOption: (input: string, option: any) => {
return option.manufacturer.toLowerCase().indexOf(input.toLowerCase()) >= 0;
},
],
showSearch: true,
immediate: true,
dropdownReload: true,
allowClear: true,
},
},
{

View File

@@ -1,506 +0,0 @@
{
"data":[
{
"title": "家居照明",
"key": "1",
"children": [
{
"title": "灯泡",
"key": "1-1",
"children": [
{
"title": "LED灯泡",
"key": "1-1-1",
"children": [
{
"title": "E27 LED灯泡",
"key": "1-1-1-1",
"attr": {
"瓦特": "7W",
"光通量": "500lm",
"色温": "2700K"
}
},
{
"title": "E14 小灯泡",
"key": "1-1-1-2",
"attr": {
"瓦特": "4W",
"光通量": "250lm",
"色温": "6500K"
}
}
]
},
{
"title": "节能灯",
"key": "1-1-2",
"children": [
{
"title": "E27 节能灯泡",
"key": "1-1-2-1",
"attr": {
"瓦特": "11W",
"光通量": "800lm",
"色温": "6500K"
}
}
]
}
]
},
{
"title": "灯具",
"key": "1-2",
"children": [
{
"title": "吊灯",
"key": "1-2-1",
"children": [
{
"title": "水晶吊灯",
"key": "1-2-1-1",
"attr": {
"尺寸": "Φ60cm",
"适用面积": "15-20㎡"
}
},
{
"title": "现代简约吊灯",
"key": "1-2-1-2",
"attr": {
"尺寸": "Φ52cm",
"适用面积": "10-15㎡"
}
}
]
},
{
"title": "台灯",
"key": "1-2-2",
"children": [
{
"title": "护眼台灯",
"key": "1-2-2-1",
"attr": {
"瓦特": "18W",
"调光调色": "是"
}
},
{
"title": "折叠臂台灯",
"key": "1-2-2-2",
"attr": {
"瓦特": "14W",
"调光调色": "否"
}
}
]
}
]
},
{
"title": "开关插座",
"key": "1-3",
"children": [
{
"title": "智能开关",
"key": "1-3-1",
"children": [
{
"title": "触控式智能开关",
"key": "1-3-1-1",
"attr": {
"控制方式": "触控/远程",
"兼容性": "ZigBee/WiFi"
}
}
]
},
{
"title": "插座",
"key": "1-3-2",
"children": [
{
"title": "多功能插座",
"key": "1-3-2-1",
"attr": {
"插孔类型": "2/3插",
"USB接口": "有"
}
}
]
}
]
}
]
},
{
"title": "电梯",
"key": "3",
"children": [
{
"title": "扶梯",
"key": "301"
},
{
"title": "直梯",
"key": "302"
}
]
},
{
"title": "冷源源",
"key": "4",
"children": [
{
"title": "通风及空调设备",
"key": "5",
"children": [
{
"title": "组合式空调机组",
"key": "501"
},
{
"title": "新风机组",
"key": "502"
},
{
"title": "精密空调",
"key": "503"
},
{
"title": "风机盘管",
"key": "504"
},
{
"title": "VAV",
"key": "505"
},
{
"title": "室外多联机",
"key": "506"
},
{
"title": "风幕机",
"key": "507"
},
{
"title": "球喷",
"key": "508"
},
{
"title": "送风机",
"key": "509"
},
{
"title": "排风机",
"key": "510"
},
{
"title": "排风兼排烟机",
"key": "511"
},
{
"title": "通风机",
"key": "512"
},
{
"title": "风阀",
"key": "513"
},
{
"title": "风柱式空调",
"key": "514"
}
]
}
]
},
{
"title": "照明",
"key": "6",
"children": [
{
"title": "多功能传感器",
"key": "701"
},
{
"title": "照度传感器",
"key": "702"
},
{
"title": "噪声传感器",
"key": "703"
}
]
}
],
"dataSource":[
{
"id": "d4",
"isDel": "0",
"officesId": "84",
"deviceCode": "37430200143",
"deviceName": "地听测试电表",
"category": "1",
"type": "1001",
"energyCount": "1",
"serialNumber": "69",
"pidCode": null,
"brand": "",
"types": "",
"manufacturer": "elit non in",
"contacts": "ad reprehenderit",
"phonenumber": "34",
"position": "in esse commodo",
"activeState": "1",
"measurementDirection": "1",
"deviceMagnification": 62,
"deviceAccuracy": "89",
"frequency": "anim consequat irure",
"standardFrequency": "ut elit",
"deviceHead": "pariatur ex velit",
"constructor": "84566",
"voltageType": "cillum aliquip reprehenderit",
"pt": 61,
"ct": 64,
"communicationProtocol": "cupidatat nisi ea ad",
"ip": "",
"port": "",
"com": "",
"slaveAddress": "",
"dlt": "",
"conversionIdentifier": "48",
"multiplicationAdjustment": "1",
"accessMethod": "1",
"replacementFrequency": "0",
"dataDetail": "sit",
"insertTime": null,
"children": null,
"devicePointList": null,
"insertUser": null
},
{
"id": "d1",
"isDel": "0",
"officesId": "84",
"deviceCode": "37430200144",
"deviceName": "地听测试2",
"category": "1",
"type": "1001",
"energyCount": "1",
"serialNumber": "69",
"pidCode": null,
"brand": "",
"types": "",
"manufacturer": "elit non in",
"contacts": "ad reprehenderit",
"phonenumber": "34",
"position": "in esse commodo",
"activeState": "1",
"measurementDirection": "1",
"deviceMagnification": 62,
"deviceAccuracy": "89",
"frequency": "anim consequat irure",
"standardFrequency": "ut elit",
"deviceHead": "pariatur ex velit",
"constructor": "84566",
"voltageType": "cillum aliquip reprehenderit",
"pt": 61,
"ct": 64,
"communicationProtocol": "802",
"ip": "10.5.36.0",
"port": "6000",
"com": "",
"slaveAddress": "123测试",
"dlt": "",
"conversionIdentifier": "48",
"multiplicationAdjustment": "1",
"accessMethod": "1",
"replacementFrequency": "0",
"dataDetail": "sit",
"insertTime": "2024-02-28 11:26:58",
"children": null,
"devicePointList": null,
"insertUser": null
},
{
"id": "d2",
"isDel": "0",
"officesId": "84",
"deviceCode": "1235623",
"deviceName": "测试设备2",
"category": "1",
"type": "1001",
"energyCount": "是",
"serialNumber": "69",
"pidCode": null,
"brand": "",
"types": "",
"manufacturer": "elit non in",
"contacts": "ad reprehenderit",
"phonenumber": "34",
"position": "in esse commodo",
"activeState": "1",
"measurementDirection": "1",
"deviceMagnification": 62,
"deviceAccuracy": "89",
"frequency": "anim consequat irure",
"standardFrequency": "ut elit",
"deviceHead": "pariatur ex velit",
"constructor": null,
"voltageType": "cillum aliquip reprehenderit",
"pt": 61,
"ct": 64,
"communicationProtocol": "cupidatat nisi ea ad",
"ip": "",
"port": "",
"com": "",
"slaveAddress": "",
"dlt": "",
"conversionIdentifier": "48",
"multiplicationAdjustment": "1",
"accessMethod": "1",
"replacementFrequency": "0",
"dataDetail": "sit",
"insertTime": "2024-02-28 11:31:57",
"children": null,
"devicePointList": null,
"insertUser": null
},
{
"id": "d7",
"isDel": "0",
"officesId": "",
"deviceCode": "0213",
"deviceName": "测试",
"category": "1",
"type": "1001",
"energyCount": "1",
"serialNumber": "",
"pidCode": null,
"brand": "6da085e5-956d-4000-bd3c-ebb01a9c99a1",
"types": "d7a8aede-b821-4ff2-953d-601a20e5a948",
"manufacturer": "",
"contacts": "",
"phonenumber": null,
"position": "",
"activeState": "",
"measurementDirection": "",
"deviceMagnification": null,
"deviceAccuracy": null,
"frequency": "",
"standardFrequency": "",
"deviceHead": "",
"constructor": "",
"voltageType": "",
"pt": null,
"ct": null,
"communicationProtocol": "",
"ip": "",
"port": "",
"com": "",
"slaveAddress": "",
"dlt": "",
"conversionIdentifier": "1",
"multiplicationAdjustment": "1",
"accessMethod": "",
"replacementFrequency": "0",
"dataDetail": "",
"insertTime": "2024-03-14 20:01:53",
"children": null,
"devicePointList": null,
"insertUser": ""
},
{
"id": "d3",
"isDel": "0",
"officesId": "84",
"deviceCode": "81",
"deviceName": "设备名称1111",
"category": "1",
"type": "1001",
"energyCount": "1",
"serialNumber": "69",
"pidCode": null,
"brand": "6da085e5-956d-4000-bd3c-ebb01a9c99a1",
"types": "d7a8aede-b821-4ff2-953d-601a20e5a948",
"manufacturer": "elit non in",
"contacts": "ad reprehenderit",
"phonenumber": "34",
"position": "in esse commodo",
"activeState": "1",
"measurementDirection": "1",
"deviceMagnification": 62,
"deviceAccuracy": "89",
"frequency": "anim consequat irure",
"standardFrequency": "ut elit",
"deviceHead": "pariatur ex velit",
"constructor": "84566",
"voltageType": "cillum aliquip reprehenderit",
"pt": 61,
"ct": 64,
"communicationProtocol": "cupidatat nisi ea ad",
"ip": "",
"port": "",
"com": "",
"slaveAddress": "",
"dlt": "",
"conversionIdentifier": "48",
"multiplicationAdjustment": "1",
"accessMethod": "1",
"replacementFrequency": "0",
"dataDetail": "sit",
"insertTime": "2024-03-15 17:34:24",
"children": null,
"devicePointList": null,
"insertUser": null
},
{
"id": "d43fdfff_02_0001",
"isDel": "0",
"officesId": "843fdffff213d2d3",
"deviceCode": "00037430200143",
"deviceName": "应感者酸严",
"category": "1",
"type": "1001",
"energyCount": "esse consequat",
"serialNumber": "69",
"pidCode": null,
"brand": "",
"types": "",
"manufacturer": "elit non in",
"contacts": "ad reprehenderit",
"phonenumber": "34",
"position": "in esse commodo",
"activeState": "1",
"measurementDirection": "1",
"deviceMagnification": 62,
"deviceAccuracy": "89",
"frequency": "anim consequat irure",
"standardFrequency": "ut elit",
"deviceHead": "pariatur ex velit",
"constructor": "84566",
"voltageType": "cillum aliquip reprehenderit",
"pt": 61,
"ct": 64,
"communicationProtocol": "cupidatat nisi ea ad",
"ip": "",
"port": "",
"com": "",
"slaveAddress": "",
"dlt": "",
"conversionIdentifier": "48",
"multiplicationAdjustment": "1",
"accessMethod": "1",
"replacementFrequency": "0",
"dataDetail": "sit",
"insertTime": "2024-04-29 11:12:43",
"children": null,
"devicePointList": null,
"insertUser": null
}
]
}

View File

@@ -48,6 +48,8 @@
<script lang="ts">
import { defineComponent, onMounted, ref, inject, watch } from 'vue';
import * as echarts from 'echarts';
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
export default defineComponent({
name: 'AnalysisGraph',
@@ -413,17 +415,45 @@
});
// 下载图表
const downloadChart = () => {
if (chartInstance) {
const base64 = chartInstance.getDataURL({
const downloadChart = async () => {
// if (chartInstance) {
// const base64 = chartInstance.getDataURL({
// type: 'png',
// backgroundColor: '#fff',
// });
// const link = document.createElement('a');
// link.href = base64;
// link.download = 'chart.png';
// link.click();
// }
const zip = new JSZip();
const chartInstances = [chartInstance, chartRight1, chartRight2];
const imagePromises = chartInstances.map((chart: any, index) => {
return new Promise((resolve) => {
const base64 = chart.getDataURL({
type: 'png',
backgroundColor: '#fff',
});
const link = document.createElement('a');
link.href = base64;
link.download = 'chart.png';
link.click();
// 将 Base64 转换为二进制
const binary = atob(base64.split(',')[1]);
const array = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
const uint8Array = new Uint8Array(array);
resolve({ name: `chart${index + 1}.png`, data: uint8Array });
});
});
const images = await Promise.all(imagePromises);
images.forEach((image: any) => {
zip.file(image.name, image.data);
});
zip.generateAsync({ type: 'blob' }).then((content) => {
saveAs(content, 'charts.zip'); // 使用 FileSaver.js 保存 ZIP 文件
});
};
return {

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,24 @@
<template>
<a-tabs v-model:activeKey="activeKey" style="height: 5%">
<a-tab-pane key="1" tab="综合数据" />
<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>
</a-tabs>
<!-- <ns-view-list-table v-bind="tableConfig" v-if="activeKey == '1'" /> -->
<aggregate-data ref="aggregateDataRef" v-if="activeKey == '1'" style="height: 85%" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { tableConfig } from './config';
import aggregateData from './aggregateData/index.vue';
const aggregateDataRef = ref();
var activeKey = ref('1');
defineOptions({
name: 'EnvironmentMonitorIndex', // 与页面路由name一致缓存才可生效
});
</script>
<style lang="less" scoped></style>

View File

@@ -129,7 +129,7 @@
accountNo: userName.value.trim(),
password: password.value.trim(),
});
validator(null, value.code)
validator(null, value?.code)
.then(() => {
// 记住密码
rememberFunc(data);