Browse Source

feat:项目初始化;

fuyang
YOU 11 months ago
parent
commit
44f528c352
  1. 125
      README.md
  2. BIN
      public/cdn/blue-icon.png
  3. BIN
      public/cdn/blue-marker.png
  4. BIN
      public/cdn/buildings.png
  5. BIN
      public/cdn/green-icon.png
  6. BIN
      public/cdn/green-marker.png
  7. 262
      public/cdn/njhl_bg copy.svg
  8. 176
      public/cdn/njhl_bg.svg
  9. BIN
      public/cdn/white-icon.png
  10. BIN
      public/cdn/white-marker.png
  11. BIN
      public/cdn/yellow-icon.png
  12. BIN
      public/cdn/yellow-marker.png
  13. BIN
      public/favicon.ico
  14. BIN
      src/assets/images/njhl/air.png
  15. BIN
      src/assets/images/njhl/bg.png
  16. BIN
      src/assets/images/njhl/block.png
  17. BIN
      src/assets/images/njhl/dd.png
  18. BIN
      src/assets/images/njhl/elec.png
  19. BIN
      src/assets/images/njhl/gas.png
  20. BIN
      src/assets/images/njhl/mm.png
  21. BIN
      src/assets/images/njhl/pop.png
  22. BIN
      src/assets/images/njhl/pop_0.png
  23. BIN
      src/assets/images/njhl/pop_1.png
  24. BIN
      src/assets/images/njhl/pop_2.png
  25. BIN
      src/assets/images/njhl/steam.png
  26. BIN
      src/assets/images/njhl/title.png
  27. BIN
      src/assets/images/njhl/tooltip/airValue.png
  28. BIN
      src/assets/images/njhl/tooltip/bg.png
  29. BIN
      src/assets/images/njhl/tooltip/close.png
  30. BIN
      src/assets/images/njhl/tooltip/electricValue.png
  31. BIN
      src/assets/images/njhl/tooltip/steamValue.png
  32. BIN
      src/assets/images/njhl/tooltip/waterValue.png
  33. BIN
      src/assets/images/njhl/water.png
  34. BIN
      src/assets/images/njhl/yy.png
  35. 2
      src/layout/header/base-header.vue
  36. 378
      src/views/dashboard/common/model.ts
  37. 289
      src/views/dashboard/common/style.ts
  38. 110
      src/views/dashboard/comp/bar.vue
  39. 117
      src/views/dashboard/comp/block.vue
  40. 102
      src/views/dashboard/comp/pop.vue
  41. 212
      src/views/dashboard/comp/preview.vue
  42. 122
      src/views/dashboard/comp/tooltip.vue
  43. 503
      src/views/dashboard/main.vue

125
README.md

@ -1,125 +0,0 @@
#### 2022-07-22 变更
```
1. 物管理-物属性extendData字段判空处理。
2. 物管理-物计算文案字段以及ui布局调整。
3. 告警管理页面布局开发。
4. 中机首页模型更换以及增加动画效果。
```
#### 2022-07-21 变更
```
1. 中机首页模型压缩体积。
2. 中机首页看板设备状态api调整。
```
#### 2022-07-20 变更
```
1. 中机首页看板异步加载修复。
2. 中机 首页看板bug修复。
```
#### 2022-07-19 变更
```
1. 中机看板联调及优化。
```
#### 2022-07-18 变更
```
1. 中机首页模型标注开发。
```
#### 2022-07-15 变更
```
1. 中机首页看板前端界面开发完成,待联调。
2. 中机设备管理属性新增/编辑弹框。
3. 系统设置-文件上传对接字段。
```
#### 2022-07-14 变更
```
1. 数据集交互优化。
2. 优化上传图片组件。
3. 系统设置-文件上传对接字段。
4. 登录页新增通过接口拿到登录框图片链接。
5. 设备首页看板界面开发。
```
#### 2022-07-13 变更
```
1. 设备控制-协议管理抽屉框标题修改也显示新增的bug。
2. 消息中心-消息模板类型格式问题修复。
3. 数据处理-规则链修复自动登录问题。
```
#### 2022-07-12 变更
```
1. 设备管理-中机首页模型加载。
2. 增加退出登录时重置title和icon的功能。
3. 数据集修复bug 6046 6047 6048 6052 6057。
```
#### 2022-07-11 变更
```
1. 设备管理-看板和设置等页面初始化。
2. 推送设置-提醒人字段在全部推送情况下不为必填项。
```
#### 2022-07-08 变更
```
1. 设备控制-计算引擎回显bug修复。
2. 企业管理-企业管理筛选全部类型下拉无数据的问题。
3. 设备控制-设备模板描述字段bug。
4. 设备控制-协议管理表格内容居中。
5. 设备控制-设备管理-通讯协议调试提示字段放大。
```
#### 2022-07-07 变更
```
1. 缓存设置-修改物页面中点击查询报错修复。
2. 缓存设置-勾选的属性没有被添加。
3. 控制模板-指令导入,存在重复指令应无法导入,且导入后提示报错修复。
4. 租户配置新增表单置空修复。
5. 物管里-物属性/物标签修改判断页面数据数组的逻辑。
6. 物管里-物分组/物属性/物标签/组管理优化导入逻辑。
7. 修改utilService判断对象逻辑。
8. 完善上传文件代码抛错逻辑。
9. 配置中心-能源品种对接导入导出。
10. 配置中心-属性设置对接导入导出。
11. 新增导入JSON组件传入url并手动上传功能。
12. 设备控制-设备控制调试页面去掉取消按钮。
13. 设备控制-设备控制调试表单增计算模块字段加校验。
14. 隐藏主题设置入口。
15. 物管理-物属性弹框去掉回车提交事件,避免误操作。
16. 设备控制-通讯协议布局和设计图不一致的问题。
```
#### 2022-07-06 变更
```
1. 企业管理-企业分组状态文案修改(正常 => 启用)。
2. 企业管理-租户账户状态文案修改(正常 => 启用)。
3. 物管理-物分组弹框打开重置相关数据;切换分页保存table(dataListSelections)所选数据。
4. 物管理-物分组中el-popover交互修改,支持点击空白处隐藏该组件。
5. 企业管理-企业账户状态更换为按钮形式。
6. 企业管理-租户账户状态更换为按钮形式并优化了企业账户按钮的代码。
7. 设备管理-设备清单添加异常设备数。
8. 设备清单-设备属性详情图表和列表开发并联调完成。
9. 物管理-物实体状态时间格式化。
10. 计算模块api联调更新,设备管理-设备清单属性详情api调整。
11. 优化并修复租户配置bug。
12. 通讯协议-新增页面点击保存关闭抽屉。
13. 通讯协议-调试中不可点击model外关闭 按钮更换字段(测试-订阅 测试-推送 取消-退出调试)。
14. 设备控制-控制模板描述添加字数限制。
15. 设备控制-通讯设备描述字段添加字数限制。
16. 物管里-物管里,数据缓存-物管里去除多余的按钮。
17. 设备控制-通讯协议点击调试按钮 点击model外不关闭的bug。
18. 设备控制-控制模板-指令添加描述字段。
19. 物管理-物关系详情页面按钮替换成图片。
20. 物管理-选择物名称弹框table物名称字段改为“名称-code”。
```

BIN
public/cdn/blue-icon.png

After

Width: 66  |  Height: 74  |  Size: 11 KiB

BIN
public/cdn/blue-marker.png

After

Width: 18  |  Height: 23  |  Size: 950 B

BIN
public/cdn/buildings.png

After

Width: 1368  |  Height: 862  |  Size: 365 KiB

BIN
public/cdn/green-icon.png

After

Width: 68  |  Height: 75  |  Size: 10 KiB

BIN
public/cdn/green-marker.png

After

Width: 18  |  Height: 23  |  Size: 1011 B

262
public/cdn/njhl_bg copy.svg
File diff suppressed because it is too large
View File

176
public/cdn/njhl_bg.svg
File diff suppressed because it is too large
View File

BIN
public/cdn/white-icon.png

After

Width: 67  |  Height: 73  |  Size: 9.4 KiB

BIN
public/cdn/white-marker.png

After

Width: 18  |  Height: 23  |  Size: 840 B

BIN
public/cdn/yellow-icon.png

After

Width: 66  |  Height: 73  |  Size: 9.6 KiB

BIN
public/cdn/yellow-marker.png

After

Width: 18  |  Height: 23  |  Size: 951 B

BIN
public/favicon.ico

Before

Width: 1024  |  Height: 1024  |  Size: 36 KiB

After

Width: 277  |  Height: 280  |  Size: 27 KiB

BIN
src/assets/images/njhl/air.png

After

Width: 60  |  Height: 59  |  Size: 1.1 KiB

BIN
src/assets/images/njhl/bg.png

After

Width: 1309  |  Height: 783  |  Size: 332 KiB

BIN
src/assets/images/njhl/block.png

After

Width: 484  |  Height: 318  |  Size: 3.6 KiB

BIN
src/assets/images/njhl/dd.png

After

Width: 45  |  Height: 51  |  Size: 1.9 KiB

BIN
src/assets/images/njhl/elec.png

After

Width: 60  |  Height: 59  |  Size: 1.1 KiB

BIN
src/assets/images/njhl/gas.png

After

Width: 55  |  Height: 55  |  Size: 2.0 KiB

BIN
src/assets/images/njhl/mm.png

After

Width: 45  |  Height: 51  |  Size: 2.0 KiB

BIN
src/assets/images/njhl/pop.png

After

Width: 199  |  Height: 86  |  Size: 711 B

BIN
src/assets/images/njhl/pop_0.png

After

Width: 41  |  Height: 41  |  Size: 1.6 KiB

BIN
src/assets/images/njhl/pop_1.png

After

Width: 41  |  Height: 41  |  Size: 1.6 KiB

BIN
src/assets/images/njhl/pop_2.png

After

Width: 40  |  Height: 40  |  Size: 1.3 KiB

BIN
src/assets/images/njhl/steam.png

After

Width: 60  |  Height: 60  |  Size: 1.3 KiB

BIN
src/assets/images/njhl/title.png

After

Width: 1920  |  Height: 88  |  Size: 13 KiB

BIN
src/assets/images/njhl/tooltip/airValue.png

After

Width: 26  |  Height: 26  |  Size: 936 B

BIN
src/assets/images/njhl/tooltip/bg.png

After

Width: 289  |  Height: 253  |  Size: 8.7 KiB

BIN
src/assets/images/njhl/tooltip/close.png

After

Width: 18  |  Height: 18  |  Size: 445 B

BIN
src/assets/images/njhl/tooltip/electricValue.png

After

Width: 26  |  Height: 26  |  Size: 864 B

BIN
src/assets/images/njhl/tooltip/steamValue.png

After

Width: 26  |  Height: 26  |  Size: 907 B

BIN
src/assets/images/njhl/tooltip/waterValue.png

After

Width: 26  |  Height: 26  |  Size: 874 B

BIN
src/assets/images/njhl/water.png

After

Width: 60  |  Height: 60  |  Size: 1.1 KiB

BIN
src/assets/images/njhl/yy.png

After

Width: 45  |  Height: 51  |  Size: 1.9 KiB

2
src/layout/header/base-header.vue

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import logo from '@/assets/images/logo.png';
import logo from '/public/favicon.ico';
import { EMitt, ESidebarLayoutEnum, EThemeSetting } from '@/constants/enum'; import { EMitt, ESidebarLayoutEnum, EThemeSetting } from '@/constants/enum';
import emits from '@/utils/emits'; import emits from '@/utils/emits';
import { getThemeConfigCacheByKey } from '@/utils/theme'; import { getThemeConfigCacheByKey } from '@/utils/theme';

378
src/views/dashboard/common/model.ts

@ -0,0 +1,378 @@
import dateService from '@/service/dateService';
import utilService from '@/service/utilService';
import { IObject } from '@/types/interface';
import { smooth } from '@antv/x6/lib/registry/connector/smooth';
import moment from 'moment';
export const monthEnergyUsageTrend = (data = []): IObject => {
const length = new dateService().getDateCountByMonth()[12];
const xAxisData = Array.from({ length }, (x, i) => i + 1);
const seriesData = [];
if (utilService.isValidArray(data)) {
for (const day of xAxisData) {
let val;
for (const item of data) {
if (day === +moment(item.date).format('D')) {
val = item.value;
}
}
seriesData.push(val);
}
}
return {
grid: {
bottom: 45,
top: 30,
left: 60,
},
tooltip: { trigger: 'axis' },
xAxis: {
name: '(日)',
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#fff' } },
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#fff' }, show: true },
splitLine: { lineStyle: { type: 'dashed', color: 'rgba(255,255,255,0.3)' } },
},
series: [
{
data: seriesData,
type: 'line',
smooth: true,
lineStyle: { color: '#00EFF6', width: 3 },
areaStyle: {
opacity: 0.8,
color: new window.echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(1, 73, 134)',
},
{
offset: 1,
color: 'rgba(1, 73, 134, 0.1)',
},
]),
},
emphasis: {
focus: 'series',
},
showSymbol: false,
},
],
};
};
export const costStatistic = (data = []): IObject => {
//颜色16进制换算rgba,添加透明度
function hexToRgba(hex, opacity) {
return (
'rgba(' +
parseInt('0x' + hex.slice(1, 3)) +
',' +
parseInt('0x' + hex.slice(3, 5)) +
',' +
parseInt('0x' + hex.slice(5, 7)) +
',' +
opacity +
')'
);
}
// 数据
const chartdata = [
{
name: '电',
value: undefined,
},
{
name: '水',
value: undefined,
},
// {
// name: "压缩空气",
// value: undefined
// },
{
name: '蒸汽',
value: undefined,
},
];
if (utilService.isValidArray(data)) {
for (const item of data) {
for (const val of chartdata) {
if (item.name === val.name) {
val.value = item.value;
}
}
}
}
const radius = ['45%', '50%'];
// 颜色系列
//const color = ["#0084FF", "#37FFC9", "#FFE777", "#19D6FF"];
const color = ['#0084FF', '#37FFC9', '#FFE777'];
const color1 = [];
const color2 = [];
const color3 = [];
// 设置每层圆环颜色和添加间隔的透明色
color.forEach((item) => {
color1.push(item, 'transparent');
color2.push(hexToRgba(item, 0.7), 'transparent');
color3.push(hexToRgba(item, 0.4), 'transparent');
});
const data1 = [];
let sum = 0;
// 根据总值设置间隔值大小
chartdata.forEach((item) => {
sum += Number(item.value);
});
// 给每个数据后添加特定的透明的数据形成间隔
chartdata.forEach((item, index) => {
if (item.value !== 0) {
data1.push(item, {
//name: "",
value: sum / 70,
labelLine: {
show: false,
lineStyle: {
color: 'transparent',
},
},
itemStyle: {
color: 'transparent',
},
});
}
});
// 每层圆环大小
const radius2 = [
Number(radius[1].split('%')[0]) + 0 + '%',
Number(radius[1].split('%')[0]) + 4 + '%',
];
const radius3 = [
Number(radius[1].split('%')[0]) + 4 + '%',
Number(radius[1].split('%')[0]) + 8 + '%',
];
return {
legend: {
data: chartdata.map((v) => ({ name: v.name })),
type: 'scroll',
bottom: 15,
textStyle: {
color: '#fff',
},
},
grid: {
top: 0,
bottom: 0,
left: 0,
right: 0,
containLabel: true,
},
tooltip: {
formatter: (params) => {
if (params.name !== '') {
return params.name + ' : ' + params.value + '\n' + '(' + params.percent + '%)';
}
},
},
series: [
// 最外层圆环
{
type: 'pie',
radius: radius3,
center: ['50%', '45%'],
hoverAnimation: false,
startAngle: 90,
selectedMode: 'single',
selectedOffset: 0,
itemStyle: {
normal: {
color: (params) => {
return color1[params.dataIndex];
},
},
},
label: { color: '#fff' },
labelLine: {
show: true,
length: 15,
length2: 30,
lineStyle: {
color: '#fff',
width: 2,
},
},
data: data1,
},
{
type: 'pie',
radius: radius2,
center: ['50%', '45%'],
hoverAnimation: false,
startAngle: 90,
selectedMode: 'single',
selectedOffset: 0,
itemStyle: {
normal: {
color: (params) => {
return color2[params.dataIndex];
},
},
},
label: {
show: false,
formatter: '{b}' + ' ' + '{c}',
},
data: data1,
},
{
type: 'pie',
radius: radius,
center: ['50%', '45%'],
hoverAnimation: false,
startAngle: 90,
selectedMode: 'single',
selectedOffset: 0,
itemStyle: {
normal: {
color: (params) => {
return color3[params.dataIndex];
},
},
},
label: {
show: false,
formatter: '{b}' + ' ' + '{c}',
},
data: data1,
},
],
};
};
export const usageRanking = (data = []): IObject => {
let yAxisData = [];
let seriesData = [];
if (utilService.isValidArray(data)) {
data = data.slice(0, 5).sort((a, b) => a.value - b.value);
yAxisData = data.map((v) => v.thingName);
seriesData = data.map((v) => v.value);
}
return {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
grid: {
bottom: 35,
top: 10,
left: 70,
},
xAxis: {
name: '(万)',
type: 'value',
boundaryGap: [0, 0.01],
axisLine: { lineStyle: { color: '#fff' }, show: true },
splitLine: { show: false },
},
yAxis: {
type: 'category',
axisLine: { lineStyle: { color: '#fff' }, show: true },
data: yAxisData,
axisLabel: {
//fontSize: 10,
formatter: (val: string) => {
return val.length > 5 ? `${val.slice(0, 3)}...` : val;
},
},
},
series: [
{
type: 'bar',
data: seriesData,
barWidth: 16,
itemStyle: {
normal: {
color: '#25DDFF',
barBorderRadius: [10, 10, 10, 10],
},
},
},
],
};
};
export const elecPayload = (data = []): IObject => {
const length = 24;
const xAxisData = Array.from({ length }, (x, i) => i);
const seriesData = [];
if (utilService.isValidArray(data)) {
for (const day of xAxisData) {
let val;
for (const item of data) {
if (
typeof item.date === 'string' &&
item.date.includes(`${new dateService().addZero(day)}:00:00`)
) {
val = item.value;
}
}
seriesData.push(val);
}
}
return {
grid: {
bottom: 45,
top: 30,
left: 40,
},
tooltip: { trigger: 'axis' },
xAxis: {
name: '(时)',
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#fff' } },
axisLabel: { fontSize: 10 },
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#fff' }, show: true },
splitLine: { lineStyle: { type: 'dashed', color: 'rgba(255,255,255,0.3)' } },
},
series: [
{
data: seriesData,
type: 'line',
smooth: true,
lineStyle: { color: '#449DD4', width: 1 },
areaStyle: {
opacity: 0.8,
color: new window.echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgb(1, 73, 134)',
},
{
offset: 1,
color: 'rgba(1, 73, 134, 0.1)',
},
]),
},
emphasis: {
focus: 'series',
},
showSymbol: false,
},
],
};
};

289
src/views/dashboard/common/style.ts

@ -0,0 +1,289 @@
import { useFetch } from '@/hooks/fetch';
import { IObject } from '@/types/interface';
import moment from 'moment';
import dragStretchService from '@/service/dragStretchService';
export const pathStyle = {
show: 'cursor:pointer;fill:#2a9bdd;stroke:#2a9bdd;stroke-width:0.26458333;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-dasharray:none;fill-opacity:0.38039216',
hide: 'cursor:pointer;opacity: 0;',
};
const reset = (state: IObject) => {
if (state.dragStretch) {
state.dragStretch.reset();
}
};
const pathMap: IObject = {
path4830: {
data: [
{ code: 'G06', key: 'A29' },
{ code: 'B_V0000151_27', key: 'B2' },
],
},
path4770: {
data: [
{ code: 'A_016', key: 'A29' },
{ code: 'B_V0000151_21', key: 'B2' },
{ code: 'E_V0000151_5', key: 'E3' },
{ code: 'D_V0000151_2', key: 'D2' },
],
},
path4766: {},
path7032: {
data: [{ code: 'B_V0000151_11', key: 'B2' }],
},
path4666: {},
path3938: {},
path3936: {
data: [{ code: 'B_V0000151_15', key: 'B2' }],
},
path3102: {
data: [
{ code: 'A_009', key: 'A29' },
{ code: 'B_V0000151_16', key: 'B2' },
{ code: 'E_V0000151_6', key: 'E3' },
{ code: 'D_V0000151_4', key: 'D2' },
],
},
path3934: {
data: [
{ code: 'G04', key: 'A29' },
{ code: 'B_V0000151_23', key: 'B2' },
],
},
path3932: {
data: [
{ code: 'A_019', key: 'A29' },
{ code: 'E_11004151_1', key: 'E3' },
],
},
path3100: {
data: [
{ code: 'A_010', key: 'A29' },
{ code: 'B_V0000151_26', key: 'B2' },
{ code: 'E_V0000151_7', key: 'E3' },
{ code: 'D_V0000151_3', key: 'D2' },
],
},
path7030: {
data: [{ code: 'B_V0000151_12', key: 'B2' }],
},
path4768: {
data: [
{ code: 'A_005', key: 'A29' },
{ code: 'B_V0000151_25', key: 'B2' },
],
},
path7028: {
data: [{ code: 'B_V0000151_17', key: 'B2' }],
},
path6300: {
data: [
{ code: 'A_021', key: 'A29' },
{ code: 'E_11004154_1', key: 'E3' },
],
codes: ['A_021', 'E_11004154_1'],
},
path6298: {
data: [
{ code: 'G05', key: 'A29' },
{ code: 'B_V0000151_19', key: 'B2' },
],
},
path6296: {},
path6294: {
data: [{ code: 'A_007', key: 'A29' }],
},
path6292: {
data: [
{ code: 'B_V0000151_13', key: 'B2' },
{ code: 'E_11004154_2', key: 'E3' },
],
},
path5564: {
data: [{ code: 'A_020', key: 'A29' }],
},
path5558: {},
path5562: {},
};
const animateMap: IObject = {
'layer-d-animate': `<animate href="#path7592" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path7592-3" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate> <animate href="#path9046" attributeName="stroke-dashoffset" begin="0.6s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path9046-1" attributeName="stroke-dashoffset" begin="0.6s" dur="15s" to="0" fill="freeze"></animate><animate href="#path18869" attributeName="stroke-dashoffset" begin="0.5s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path18869-1" attributeName="stroke-dashoffset" begin="0.5s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path9046-5" attributeName="stroke-dashoffset" begin="2s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path9046-1-0" attributeName="stroke-dashoffset" begin="2s" dur="15s" to="0" fill="freeze"></animate><animate href="#path23350-62" attributeName="stroke-dashoffset" begin="1.22s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path23350-6-2" attributeName="stroke-dashoffset" begin="1.22s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path23326" attributeName="stroke-dashoffset" dur="3.8s" to="0" fill="freeze"></animate>
<animate href="#path23326-1" attributeName="stroke-dashoffset" dur="3.8s" to="0" fill="freeze"></animate>
<animate href="#path23350" attributeName="stroke-dashoffset" begin="1.42s" dur="25s" to="0" fill="freeze"></animate>
<animate href="#path23350-6" attributeName="stroke-dashoffset" begin="1.42s" dur="25s" to="0" fill="freeze"></animate>
<animate href="#path23374" attributeName="stroke-dashoffset" begin="2.8s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path23374-0" attributeName="stroke-dashoffset" begin="2.8s" dur="15s" to="0" fill="freeze"></animate>
<animate href="#path26736" attributeName="stroke-dashoffset" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path26736-9" attributeName="stroke-dashoffset" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path28441-0" attributeName="stroke-dashoffset" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path28441-8-9" attributeName="stroke-dashoffset" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path30063" attributeName="stroke-dashoffset" begin="0.5s" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path30063-0" attributeName="stroke-dashoffset" begin="0.5s" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path28441" attributeName="stroke-dashoffset" begin="0.5s" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path28441-8-9-2" attributeName="stroke-dashoffset" begin="0.5s" dur="2s" to="0" fill="freeze"></animate>
<animate href="#path23264" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate><animate href="#path23264-3" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate>
<animate href="#image31036-69" attributeName="display" dur="3.2s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-6" attributeName="display" dur="7.8s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-8" attributeName="display" dur="5s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-3" attributeName="display" dur="5s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-69-9" attributeName="display" dur="2.3s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-69-5" attributeName="display" dur="2.8s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-1" attributeName="display" dur="2s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-5112" attributeName="display" dur="3.4s" from="none" to="block" fill="freeze"></animate>
<animate href="#image31036-5" attributeName="display" dur="2.4s" from="none" to="block" fill="freeze"></animate>
`,
};
const pointerElec = { lineId: 'njhl-svg-elec' };
const pointerWater = { lineId: 'njhl-svg-water' };
const pointerGas = { lineId: 'njhl-svg-gas' };
const pointerSteam = { lineId: 'njhl-svg-steam' };
const pointerMap: IObject = {
'image31024-4': pointerElec,
image31024: pointerElec, //电
image4433: pointerWater,
'image4433-8': pointerWater,
image8135: pointerSteam,
'image8135-9': pointerSteam,
image12904: pointerGas,
};
const initToolTip = (state: IObject): any => {
for (const key in pathMap) {
const val = pathMap[key];
if (!val.data) {
continue;
}
const path = document.getElementById(key);
if (path) {
path.onmouseenter = () => {
path.setAttribute('style', pathStyle.show);
};
path.onmouseleave = () => {
path.setAttribute('style', pathStyle.hide);
};
path.onclick = (event: IObject) => {
setToolTipStatus('flex');
const { screenX, screenY } = event;
const target = document.querySelector('.njhl-tool-tip');
if (target) {
useFetch('/board/build/data', {
method: 'post',
data: {
datas: val.data,
month: moment().format('YYYY-MM'),
},
cb: (res: any) => {
state.toolTipData = res;
},
});
target.style.left = screenX - 100 + 'px';
target.style.top = screenY - 100 + 'px';
}
};
}
}
};
const pointerLineMap: IObject = { 'layer-d-animate': ['image31024-4', 'image31024'] };
const setPointerLineStatus = (target: HTMLElement, status: string): void => {
if (target) {
target.style.display = status;
}
};
export const setPointerLineStatusAll = (status: string): void => {
for (const key in pointerMap) {
const item = pointerMap[key];
const target = document.getElementById(item.lineId) as HTMLElement;
setPointerLineStatus(target, status);
}
};
const initLine = (): any => {
// for (const key in pointerLineMap) {
// const ids = pointerLineMap[key];
// for (const item of ids) {
// const pointer = document.getElementById(item);
// if (pointer) {
// pointer.onclick = () => {
// const v = animateMap[key];
// const target = document.getElementById(key);
// if (target) {
// target.innerHTML = v;
// const center = document.getElementById(".center");
// if (center) {
// const content = center.innerHTML;
// center.innerHTML = "";
// center.innerHTML = content;
// }
// }
// };
// }
// }
// }
setPointerLineStatusAll('none');
for (const key in pointerMap) {
const target = document.getElementById(key);
const item = pointerMap[key];
if (target) {
target.onclick = () => {
setPointerLineStatusAll('none');
const t = document.getElementById(item.lineId) as HTMLElement;
setPointerLineStatus(t, 'block');
};
}
}
};
const getToolTipCloseElement = (): HTMLElement => {
return document.querySelector('.njhl-tool-tip .title .close');
};
const getToolTipElement = (): HTMLElement => {
return document.querySelector('.njhl-tool-tip');
};
const setToolTipStatus = (status: string): void => {
const target = getToolTipElement();
if (target) {
target.style.display = status;
}
};
const initCloseToolTip = (): void => {
const target = getToolTipCloseElement();
if (target) {
target.onclick = () => {
setToolTipStatus('none');
};
}
};
export const init = (state: IObject, target: HTMLElement): void => {
initToolTip(state);
initLine();
initCloseToolTip();
state.dragStretch = new dragStretchService(target);
};

110
src/views/dashboard/comp/bar.vue

@ -0,0 +1,110 @@
<template>
<div class="bar">
<div :class="['flex-absolute', item.data && item.data.length === 3 ? 'around' : '']">
<div :class="['item']" v-for="(val, index) in item.data || []" :key="index">
<img :src="$util.getImg('/src/assets/images/njhl/' + val.icon + '.png')" alt="" />
<span class="label">{{ val.label }}</span>
<span class="value">{{ val.value }}</span>
<span class="unit">{{ val.unit }}</span>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { IObject } from '@/types/interface';
export default defineComponent({
name: 'bar',
props: {
item: {
type: Object,
default: () => ({}),
},
},
emits: ['set-val'],
setup() {
const state = reactive({}) as IObject;
const methods = {};
return {
...toRefs(state),
...methods,
};
},
});
</script>
<style scoped lang="less">
.bar {
position: relative;
width: 100%;
height: 100%;
> .flex-absolute {
padding: 0 0 6px 0;
// overflow-y: auto;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
height: 100%;
color: #00caff;
&.around {
justify-content: space-around;
.item {
&:not(:last-child) ::after {
position: absolute;
width: 100%;
height: 1px;
left: 0;
bottom: -20px;
content: '';
background-color: rgba(#70c5ff, 0.1);
}
}
}
> .item {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
width: 394px;
> img {
margin-right: 16px;
width: 39px;
height: 39px;
}
> .label {
width: 45%;
font-size: 20px;
font-weight: bold;
}
> .value {
width: 20%;
color: #ffd200;
font-family: 'Alibaba PuHuiTi Heavy'; //
font-size: 24px;
text-align: left;
padding-right: 10px;
color: #00faa8;
font-weight: bold;
}
> .unit {
width: 6em;
color: #00e5ff;
font-family: 'Alibaba PuHuiTi Heavy'; //
font-size: 16px;
text-align: right;
// font-weight: bold;
}
}
}
}
</style>

117
src/views/dashboard/comp/block.vue

@ -0,0 +1,117 @@
<template>
<div class="block" ref="blockRef">
<div :class="['title']">
<span>{{ item.title || '' }}</span>
<div class="title-right">
<el-select :model-value="dataForm.code" v-if="item.select" @change="$emit('change-code', $event)"><el-option v-for="(val, key, index) in dataForm.codeMap || {}" :key="index" :label="key" :value="key"></el-option></el-select>
<span class="unit" v-if="item.unit">{{ item.unit }}</span>
</div>
</div>
<div class="main">
<slot></slot>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, onMounted, reactive, toRefs } from 'vue';
import { IObject } from '@/types/interface';
import utilService from '@/service/utilService';
export default defineComponent({
name: 'block',
props: { item: { type: Object, default: () => ({}) }, dataForm: { type: Object, default: () => ({}) } },
setup(props) {
const state = reactive({
blockRef: null,
});
return {
...toRefs(state),
};
},
emits: ['change-code'],
});
</script>
<style scoped lang="less">
.block {
width: 484px;
height: 318px;
background: url('@/assets/images/njhl/block.png') no-repeat center;
background-size: 100% 100%;
display: flex;
flex-direction: column;
> .title {
position: relative;
// height: v-bind("getSizeByType.titleHeight");
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 38px;
color: #00e5ff;
> span {
font-size: 20px;
font-family: Microsoft YaHei;
font-weight: bold;
}
> .title-right {
position: absolute;
top: 20px;
right: 20px;
::v-deep .el-select {
width: 100px;
.el-input__wrapper {
background: transparent;
padding-right: 20px;
.el-input__inner {
color: #fff;
}
}
.el-input__suffix-inner i {
color: #0084ff;
}
.el-select:hover,
.el-input__wrapper {
box-shadow: 0 0 0 1px #0084ff;
}
}
.unit {
display: inline-block;
margin-left: 8px;
width: 2em;
}
}
}
> .dot {
> span {
padding-left: 25px;
}
&:before {
position: absolute;
left: 0;
top: 50%;
content: '';
width: 14px;
height: 41px;
transform: translateY(-50%);
background: rgba(#3c8ef5, 0.34);
}
}
> .main {
position: relative;
flex: 1;
}
}
</style>

102
src/views/dashboard/comp/pop.vue

@ -0,0 +1,102 @@
<template>
<div class="njhl-pop">
<div class="icon">
<img :src="getIcon" alt="" />
</div>
<div class="right">
<div class="wrap">
<div class="top">{{ item.label }}</div>
<div class="bottom">
<div class="wrap">
<b class="orange">{{ item.value }}</b>
<span>{{ item.unit }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import utilService from '@/service/utilService';
import { computed, defineComponent } from 'vue';
export default defineComponent({
name: 'pop',
props: { item: { type: Object, default: () => ({}) }, index: { type: Number } },
setup(props) {
const getIcon = computed(() => utilService.getImg('/src/assets/images/njhl/pop_' + props.index + '.png'));
return {
getIcon,
};
},
});
</script>
<style scoped lang="less">
.njhl-pop {
width: 199px;
height: 86px;
background: url('@/assets/images/njhl/pop.png') no-repeat center;
display: flex;
align-items: center;
justify-content: space-between;
color: #fff;
padding: 0 12px;
> .icon {
margin-right: 12px;
img {
width: 41px;
height: 41px;
}
}
> .right {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
> .wrap {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
display: flex;
flex-direction: column;
> .top {
color: #00caff;
font-size: 18px;
margin: 6px 0;
font-weight: bold;
}
> .bottom {
flex: 1;
position: relative;
display: flex;
align-items: center;
color: #00faa8;
> .wrap {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
display: flex;
align-items: baseline;
b {
flex: 1;
font-size: 25px;
margin-right: 7px;
}
span {
font-size: 12px;
}
}
}
}
}
}
</style>

212
src/views/dashboard/comp/preview.vue

@ -62,34 +62,34 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, reactive, toRefs, watch } from "vue";
import utilService from "@/service/utilService";
import baseService from "@/service/baseService";
import { ElMessage } from "element-plus";
import { useRoute } from "vue-router";
import { getToken } from "@/utils/cache";
import app from "@/constants/app";
import WebSocketService from "@/utils/websocket";
import { IFunction, IObject } from "@/types/interface";
import IssuedDialog from "../issued-dialog.vue";
import { useStore } from "vuex";
import dragStretchService from "@/service/dragStretchService";
import SvgIcon from "@/components/base/svg-icon";
import { useI18n } from "vue-i18n";
import Drawer from "@/components/drawer/drawer.vue";
import { clear } from "xe-utils";
import { computed, defineComponent, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, reactive, toRefs, watch } from 'vue';
import utilService from '@/service/utilService';
import baseService from '@/service/baseService';
import { ElMessage } from 'element-plus';
import { useRoute } from 'vue-router';
import { getToken } from '@/utils/cache';
import app from '@/constants/app';
import WebSocketService from '@/utils/websocket';
import { IFunction, IObject } from '@/types/interface';
import IssuedDialog from '../issued-dialog.vue';
import { useStore } from 'vuex';
import dragStretchService from '@/service/dragStretchService';
import SvgIcon from '@/components/base/svg-icon';
import { useI18n } from 'vue-i18n';
import Drawer from '@/components/drawer/drawer.vue';
import { clear } from 'xe-utils';
export default defineComponent({ export default defineComponent({
name: "design-preview-dialog",
name: 'design-preview-dialog',
components: { IssuedDialog, SvgIcon, Drawer }, components: { IssuedDialog, SvgIcon, Drawer },
props: { props: {
dataList: { type: Array, default: () => [] }, dataList: { type: Array, default: () => [] },
layoutIndex: { type: Number, default: 2 }, layoutIndex: { type: Number, default: 2 },
visible: Boolean, visible: Boolean,
params: Object, params: Object,
fullPath: String
fullPath: String,
}, },
emits: ["change-view"],
emits: ['change-view'],
setup(props) { setup(props) {
const route = useRoute(); const route = useRoute();
const store = useStore(); const store = useStore();
@ -100,33 +100,33 @@ export default defineComponent({
drawerVisible: false, drawerVisible: false,
carouselRef: null, carouselRef: null,
superAdmin: null as any, superAdmin: null as any,
cmdId: "",
unit: "",
val: "",
activeId: "",
elementId: "",
name: "",
controlDevice: "",
controlDeviceAttr: "",
cmdId: '',
unit: '',
val: '',
activeId: '',
elementId: '',
name: '',
controlDevice: '',
controlDeviceAttr: '',
bannerData: [] as Array<IObject>, bannerData: [] as Array<IObject>,
deviceList: [], deviceList: [],
roleIdList: [], roleIdList: [],
ctrlCacheMap: {} as IObject, ctrlCacheMap: {} as IObject,
previewRef: null, previewRef: null,
index: 0
index: 0,
}); });
watch( watch(
() => state.drawerVisible, () => state.drawerVisible,
(newVl: boolean) => { (newVl: boolean) => {
setTimer(); setTimer();
}
},
); );
const setTimer = () => { const setTimer = () => {
clearTimer(); clearTimer();
state.timer = setInterval(() => { state.timer = setInterval(() => {
if (route.query.slideshow === "1" && intervalTime.value > 0 && state.bannerData && state.bannerData.length > 0 && !state.issuedVisible && !state.drawerVisible) {
if (route.query.slideshow === '1' && intervalTime.value > 0 && state.bannerData && state.bannerData.length > 0 && !state.issuedVisible && !state.drawerVisible) {
if (state.index >= state.bannerData.length - 1) { if (state.index >= state.bannerData.length - 1) {
state.index = 0; state.index = 0;
changeIndex(state.index); changeIndex(state.index);
@ -143,7 +143,7 @@ export default defineComponent({
if (state.timer) { if (state.timer) {
clearInterval(state.timer); clearInterval(state.timer);
state.timer = null; state.timer = null;
console.log("clearTimer");
console.log('clearTimer');
} }
}; };
@ -152,30 +152,30 @@ export default defineComponent({
const targetDiv = document.getElementById(id); const targetDiv = document.getElementById(id);
if (targetDiv) { if (targetDiv) {
const svg = targetDiv.getElementsByTagName("svg");
const svg = targetDiv.getElementsByTagName('svg');
if (utilService.isValidObject(svg)) { if (utilService.isValidObject(svg)) {
const [target] = svg; const [target] = svg;
switch (layoutIndex) { switch (layoutIndex) {
case 0: { case 0: {
target.setAttribute("width", "");
target.setAttribute("height", "");
target.setAttribute("preserveAspectRatio", "");
target.setAttribute('width', '');
target.setAttribute('height', '');
target.setAttribute('preserveAspectRatio', '');
break; break;
} }
case 1: { case 1: {
target.setAttribute("width", "1810");
target.setAttribute("height", "914");
target.setAttribute("preserveAspectRatio", "none");
target.setAttribute('width', '1810');
target.setAttribute('height', '914');
target.setAttribute('preserveAspectRatio', 'none');
break; break;
} }
case 2: { case 2: {
//console.log(state.previewRef?.clientWidth, "clientWidth"); //console.log(state.previewRef?.clientWidth, "clientWidth");
target.setAttribute("width", state.previewRef?.clientWidth || "1810");
target.setAttribute("height", state.previewRef?.clientHeight || "914");
target.setAttribute("preserveAspectRatio", "");
target.setAttribute('width', state.previewRef?.clientWidth || '1810');
target.setAttribute('height', state.previewRef?.clientHeight || '914');
target.setAttribute('preserveAspectRatio', '');
break; break;
} }
@ -209,12 +209,12 @@ export default defineComponent({
}); });
const changeCallback = (index: number) => { const changeCallback = (index: number) => {
console.log(index, "index");
console.log(index, 'index');
if (route.fullPath === props.fullPath) { if (route.fullPath === props.fullPath) {
state.index = index; state.index = index;
const { id, type, dataSourceList, socketCacheData } = state.bannerData[index]; const { id, type, dataSourceList, socketCacheData } = state.bannerData[index];
if (type === "1") {
if (type === '1') {
return; return;
} }
@ -268,7 +268,7 @@ export default defineComponent({
}; };
const intervalTime = computed(() => { const intervalTime = computed(() => {
return +(route.query?.interval || "0");
return +(route.query?.interval || '0');
}); });
const getDashboardGroupDetail = async () => { const getDashboardGroupDetail = async () => {
@ -305,7 +305,7 @@ export default defineComponent({
// return ElMessage.error(msg); // return ElMessage.error(msg);
// } // }
generateSvg2Dom(svgUrl, async (data: string | undefined) => { generateSvg2Dom(svgUrl, async (data: string | undefined) => {
if (typeof data === "string") {
if (typeof data === 'string') {
state.bannerData[index].detail = data; state.bannerData[index].detail = data;
await getElementAttrById(id, index); await getElementAttrById(id, index);
@ -326,7 +326,7 @@ export default defineComponent({
ws(id, index); ws(id, index);
} }
} else { } else {
state.bannerData[index].detail = "";
state.bannerData[index].detail = '';
} }
}); });
}; };
@ -335,16 +335,16 @@ export default defineComponent({
const data = state.bannerData[index]?.elementAttr || []; const data = state.bannerData[index]?.elementAttr || [];
for (const item of data) { for (const item of data) {
if (item.elementId && item.controlCmdId && item.controlConfig === "1" && item.controlStyle === "text") {
if (item.elementId && item.controlCmdId && item.controlConfig === '1' && item.controlStyle === 'text') {
nextTick(async () => { nextTick(async () => {
//const target = (document.getElementById(dashboardId) as HTMLElement).querySelector("text[id='" + item.elementId + "']") as HTMLElement; //const target = (document.getElementById(dashboardId) as HTMLElement).querySelector("text[id='" + item.elementId + "']") as HTMLElement;
const target = document.getElementById(item.elementId) as HTMLElement; const target = document.getElementById(item.elementId) as HTMLElement;
target && target.setAttribute("controlCmdId", item.controlCmdId);
target && item.unit && target.setAttribute("unit", item.unit);
target && target.setAttribute('controlCmdId', item.controlCmdId);
target && item.unit && target.setAttribute('unit', item.unit);
const { deviceName: name } = utilService.getTargetItemByKey(state.deviceList, item.controlDevice, "deviceCode");
const { deviceName: name } = utilService.getTargetItemByKey(state.deviceList, item.controlDevice, 'deviceCode');
target && name && target.setAttribute("name", name);
target && name && target.setAttribute('name', name);
// test // test
// if (item.elementId === "text15876") { // if (item.elementId === "text15876") {
@ -355,25 +355,25 @@ export default defineComponent({
} }
nextTick(() => { nextTick(() => {
const targetList = (document.getElementById(dashboardId) as HTMLElement)?.querySelectorAll("text[controlCmdId]") || [];
const targetList = (document.getElementById(dashboardId) as HTMLElement)?.querySelectorAll('text[controlCmdId]') || [];
for (const item of targetList) { for (const item of targetList) {
const cmdId = item.getAttribute("controlCmdId");
const cmdId = item.getAttribute('controlCmdId');
const { controlPermissions, controlDevice, controlDeviceAttr } = utilService.getTargetItemByKey(data, item.id, "elementId");
const { controlPermissions, controlDevice, controlDeviceAttr } = utilService.getTargetItemByKey(data, item.id, 'elementId');
if (cmdId && (state.superAdmin || judgeIfHasPermission(controlPermissions))) { if (cmdId && (state.superAdmin || judgeIfHasPermission(controlPermissions))) {
//if (cmdId) { //if (cmdId) {
item.style && (item.style.cursor = "pointer");
item.style && (item.style.cursor = 'pointer');
item.onclick = () => { item.onclick = () => {
state.issuedVisible = true; state.issuedVisible = true;
state.cmdId = cmdId; state.cmdId = cmdId;
state.elementId = item.id; state.elementId = item.id;
state.unit = item.getAttribute("unit") || "";
state.unit = item.getAttribute('unit') || '';
state.val = item.innerHTML; state.val = item.innerHTML;
state.controlDevice = controlDevice; state.controlDevice = controlDevice;
state.controlDeviceAttr = controlDeviceAttr; state.controlDeviceAttr = controlDeviceAttr;
state.name = item.getAttribute("name") || "";
state.name = item.getAttribute('name') || '';
}; };
} }
} }
@ -386,13 +386,13 @@ export default defineComponent({
const target = document.getElementById(item.elementId) as HTMLElement; const target = document.getElementById(item.elementId) as HTMLElement;
// //
if (item.controlConfig === "1" && item.controlStyle === "switch") {
if (item.controlConfig === '1' && item.controlStyle === 'switch') {
if (target) { if (target) {
if (item.elementName) { if (item.elementName) {
target.innerHTML = item.elementName; target.innerHTML = item.elementName;
} }
target.style.cursor = "pointer";
target.style.cursor = 'pointer';
//target.setAttribute("title", ""); //target.setAttribute("title", "");
target.onclick = () => { target.onclick = () => {
let targetValue = assembleCtrlFunc(item.controlRelation, state.bannerData[state.index].map[item.elementId]); let targetValue = assembleCtrlFunc(item.controlRelation, state.bannerData[state.index].map[item.elementId]);
@ -433,7 +433,7 @@ export default defineComponent({
}; };
const getDeviceList = async () => { const getDeviceList = async () => {
const { msg, code, data } = await baseService.get("/device/control/list/device");
const { msg, code, data } = await baseService.get('/device/control/list/device');
if (code !== 0) { if (code !== 0) {
return ElMessage.error(msg); return ElMessage.error(msg);
} }
@ -446,7 +446,7 @@ export default defineComponent({
}; };
const getElementAttrById = async (dashboardId: string, index: number) => { const getElementAttrById = async (dashboardId: string, index: number) => {
const { msg, code, data } = await baseService.get("dashboard/iotdashboardelement/list", { dashboardId: dashboardId });
const { msg, code, data } = await baseService.get('dashboard/iotdashboardelement/list', { dashboardId: dashboardId });
if (code !== 0) { if (code !== 0) {
return ElMessage.error(msg); return ElMessage.error(msg);
@ -457,19 +457,19 @@ export default defineComponent({
const getIsSuperAdmin = async () => { const getIsSuperAdmin = async () => {
if (state.superAdmin !== null) return state.superAdmin; if (state.superAdmin !== null) return state.superAdmin;
const { msg, code, data } = await baseService.get(`/sys/user/super/${store.state?.user?.id || ""}`);
const { msg, code, data } = await baseService.get(`/sys/user/super/${store.state?.user?.id || ''}`);
if (code !== 0) { if (code !== 0) {
ElMessage.error(msg); ElMessage.error(msg);
return false; return false;
} }
if (typeof data === "boolean") {
if (typeof data === 'boolean') {
state.superAdmin = data; state.superAdmin = data;
} }
}; };
const getRoleIdList = async (): Promise<Array<string>> => { const getRoleIdList = async (): Promise<Array<string>> => {
const { msg, code, data } = await baseService.get(`/sys/user/${store.state?.user?.id || ""}`);
const { msg, code, data } = await baseService.get(`/sys/user/${store.state?.user?.id || ''}`);
if (code !== 0) { if (code !== 0) {
ElMessage.error(msg); ElMessage.error(msg);
@ -486,11 +486,11 @@ export default defineComponent({
const judgeIfHasPermission = (controlPermissions: string | null) => { const judgeIfHasPermission = (controlPermissions: string | null) => {
//const judgeIfHasPermission = (roleIdList: Array<string>, controlPermissions: string | null) => { //const judgeIfHasPermission = (roleIdList: Array<string>, controlPermissions: string | null) => {
//if (+store.state.user.superAdmin === 1 || controlPermissions === null) { //if (+store.state.user.superAdmin === 1 || controlPermissions === null) {
if (controlPermissions === null || controlPermissions === "") {
if (controlPermissions === null || controlPermissions === '') {
return true; return true;
} }
if (typeof controlPermissions === "string") {
if (typeof controlPermissions === 'string') {
//const ctrlPermissionList = controlPermissions.split(","); //const ctrlPermissionList = controlPermissions.split(",");
// //
// const intersection = ctrlPermissionList.filter((val) => { // const intersection = ctrlPermissionList.filter((val) => {
@ -499,7 +499,7 @@ export default defineComponent({
// //
// return utilService.isValidArray(intersection); // return utilService.isValidArray(intersection);
const id = store.state?.user?.id || "";
const id = store.state?.user?.id || '';
return controlPermissions.indexOf(id) >= 0; return controlPermissions.indexOf(id) >= 0;
} }
@ -508,7 +508,7 @@ export default defineComponent({
}; };
const getDataSourceFromServer = async (dashboardId: string, index: number) => { const getDataSourceFromServer = async (dashboardId: string, index: number) => {
const { msg, code, data } = await baseService.get("dashboard/iotdashboardelement/getElementByDashboardId", { dashboardId });
const { msg, code, data } = await baseService.get('dashboard/iotdashboardelement/getElementByDashboardId', { dashboardId });
if (code !== 0) { if (code !== 0) {
return ElMessage.error(msg); return ElMessage.error(msg);
@ -529,7 +529,7 @@ export default defineComponent({
const issued = async (cmdId: string, value: string, deviceName: string, attrCode: string, elementId: string, id: string) => { const issued = async (cmdId: string, value: string, deviceName: string, attrCode: string, elementId: string, id: string) => {
// //
if (state.ctrlCacheMap[elementId]) { if (state.ctrlCacheMap[elementId]) {
return ElMessage.error("命令已下发,新值接收到之前或十秒之内,禁止二次下发!");
return ElMessage.error('命令已下发,新值接收到之前或十秒之内,禁止二次下发!');
} }
//const { msg, code, data } = await baseService.post(`/device/iotdevicecmd/updateIotDeviceStatus?cmdId=${cmdId}`, { //const { msg, code, data } = await baseService.post(`/device/iotdevicecmd/updateIotDeviceStatus?cmdId=${cmdId}`, {
@ -537,10 +537,10 @@ export default defineComponent({
condition: JSON.stringify({ condition: JSON.stringify({
value, value,
deviceName, deviceName,
attrCode
attrCode,
}), }),
controlId: cmdId, controlId: cmdId,
elementId: id
elementId: id,
}); });
if (code !== 0) { if (code !== 0) {
@ -558,19 +558,19 @@ export default defineComponent({
}, 1000 * 10); }, 1000 * 10);
ElMessage({ ElMessage({
message: data || t("dashboard.issuedSuccess"),
type: "success",
duration: 3000
message: data || t('dashboard.issuedSuccess'),
type: 'success',
duration: 3000,
}); });
}; };
const ws = (dashboardId: string, index: number) => { const ws = (dashboardId: string, index: number) => {
let socketUrl = "";
let socketUrl = '';
const token = getToken(); const token = getToken();
if (import.meta.env.PROD) { if (import.meta.env.PROD) {
socketUrl = "ws://" + window.location.host + "/thing/websocket?token=" + token + "&dashboardId=" + dashboardId;
socketUrl = 'ws://' + window.location.host + '/thing/websocket?token=' + token + '&dashboardId=' + dashboardId;
} else { } else {
socketUrl = app.websocket + "?token=" + token + "&dashboardId=" + dashboardId;
socketUrl = app.websocket + '?token=' + token + '&dashboardId=' + dashboardId;
} }
state.bannerData[index].socket = new WebSocketService(socketUrl); state.bannerData[index].socket = new WebSocketService(socketUrl);
state.bannerData[index].map = {}; state.bannerData[index].map = {};
@ -579,7 +579,7 @@ export default defineComponent({
}; };
const findCalcBodyByElementId = (elementId: string, index: number) => { const findCalcBodyByElementId = (elementId: string, index: number) => {
return utilService.getTargetItemByKey(state.bannerData[index].dataSourceList, elementId, "elementId");
return utilService.getTargetItemByKey(state.bannerData[index].dataSourceList, elementId, 'elementId');
}; };
const assembleStyleFunc = (calcBody: string, value: string, elementId: string) => { const assembleStyleFunc = (calcBody: string, value: string, elementId: string) => {
@ -594,21 +594,21 @@ export default defineComponent({
} }
const funcStr = `function t(data, target){${calcBody}}`; const funcStr = `function t(data, target){${calcBody}}`;
const func = new Function("return " + funcStr);
const func = new Function('return ' + funcStr);
//console.log(funcStr, "funcStr"); //console.log(funcStr, "funcStr");
//console.log(func, "func"); //console.log(func, "func");
if (typeof calcBody === "string" && calcBody.indexOf("target") >= 0 && !target) {
if (typeof calcBody === 'string' && calcBody.indexOf('target') >= 0 && !target) {
return value; return value;
} }
try { try {
//console.log(func()(value, target), "func()(value, target)"); //console.log(func()(value, target), "func()(value, target)");
return func()(value, target); return func()(value, target);
} catch (e) { } catch (e) {
console.error(funcStr, "组装函数");
console.error(func, "生成组装函数");
console.error(calcBody, "计算体");
console.error(value, "原始值");
console.error(funcStr, '组装函数');
console.error(func, '生成组装函数');
console.error(calcBody, '计算体');
console.error(value, '原始值');
return value; return value;
} }
}; };
@ -618,16 +618,16 @@ export default defineComponent({
return value; return value;
} }
const funcStr = `function t(data){${calcBody};}`; const funcStr = `function t(data){${calcBody};}`;
const func = new Function("return " + funcStr);
const func = new Function('return ' + funcStr);
//console.log(funcStr, "funcStr"); //console.log(funcStr, "funcStr");
//console.log(func, "func"); //console.log(func, "func");
try { try {
return func()(value); return func()(value);
} catch (e) { } catch (e) {
console.error(funcStr, "组装函数");
console.error(func, "生成组装函数");
console.error(calcBody, "计算体");
console.error(value, "原始值");
console.error(funcStr, '组装函数');
console.error(func, '生成组装函数');
console.error(calcBody, '计算体');
console.error(value, '原始值');
return value; return value;
} }
}; };
@ -656,21 +656,21 @@ export default defineComponent({
state.bannerData[index].loading = true; state.bannerData[index].loading = true;
state.bannerData[index].socket.onError(() => { state.bannerData[index].socket.onError(() => {
console.log("onError");
console.log('onError');
const { id } = state.bannerData[index]; const { id } = state.bannerData[index];
ws(id, index); ws(id, index);
}); });
state.bannerData[index].socket.onOpen(() => { state.bannerData[index].socket.onOpen(() => {
console.log("open");
console.log('open');
if (state.bannerData[index].socket && !state.bannerData[index].socket.onMessage) return; if (state.bannerData[index].socket && !state.bannerData[index].socket.onMessage) return;
state.bannerData[index].socket.onMessage((data) => { state.bannerData[index].socket.onMessage((data) => {
console.log("onMessage");
console.log('onMessage');
if (utilService.isValidObject(data) && utilService.isValidObject(data.data)) { if (utilService.isValidObject(data) && utilService.isValidObject(data.data)) {
const ret = JSON.parse(data.data || "{}");
const ret = JSON.parse(data.data || '{}');
//console.log(ret.data); //console.log(ret.data);
if (utilService.isValidArray(ret.data)) { if (utilService.isValidArray(ret.data)) {
// //
@ -685,7 +685,7 @@ export default defineComponent({
} }
setData2Board(state.bannerData[index].socketCacheData, index); setData2Board(state.bannerData[index].socketCacheData, index);
} else if (typeof ret.data === "string") {
} else if (typeof ret.data === 'string') {
ElMessage.warning(ret.data); ElMessage.warning(ret.data);
} }
} }
@ -695,7 +695,7 @@ export default defineComponent({
state.bannerData[index].socket.onClose((e: IObject) => { state.bannerData[index].socket.onClose((e: IObject) => {
if (+e.code !== 1000) { if (+e.code !== 1000) {
console.log("try");
console.log('try');
const { id } = state.bannerData[index]; const { id } = state.bannerData[index];
ws(id, index); ws(id, index);
} }
@ -731,9 +731,9 @@ export default defineComponent({
}; };
const close = (index: number) => { const close = (index: number) => {
console.log("try to close");
console.log('try to close');
if (utilService.isValidObject(state.bannerData[index].socket)) { if (utilService.isValidObject(state.bannerData[index].socket)) {
console.log("closed", index);
console.log('closed', index);
state.bannerData[index].socket.close(); state.bannerData[index].socket.close();
} }
state.bannerData[index].socket = {}; state.bannerData[index].socket = {};
@ -750,7 +750,7 @@ export default defineComponent({
if (utilService.isValidArray(newVal)) { if (utilService.isValidArray(newVal)) {
(async () => { (async () => {
for (const [index, item] of newVal.entries()) { for (const [index, item] of newVal.entries()) {
if (item.type === "1") {
if (item.type === '1') {
continue; continue;
} }
await getDataSourceFromServer(item.id, index); await getDataSourceFromServer(item.id, index);
@ -793,7 +793,7 @@ export default defineComponent({
const addListener = () => { const addListener = () => {
if (props.visible) return; if (props.visible) return;
const target = document.querySelector(".dashboard-preview-index") as HTMLElement;
const target = document.querySelector('.dashboard-preview-index') as HTMLElement;
if (target) { if (target) {
target.onmouseenter = clearTimer; target.onmouseenter = clearTimer;
@ -802,7 +802,7 @@ export default defineComponent({
}; };
const resize = () => { const resize = () => {
console.log("onResize");
console.log('onResize');
setSvgLayout(props.layoutIndex, state.bannerData[state.index].id); setSvgLayout(props.layoutIndex, state.bannerData[state.index].id);
}; };
@ -815,22 +815,22 @@ export default defineComponent({
onActivated(() => { onActivated(() => {
initBoardDetailData(props.dataList as Array<IObject>); initBoardDetailData(props.dataList as Array<IObject>);
state.ctrlCacheMap = {}; state.ctrlCacheMap = {};
window.addEventListener("resize", resize);
window.addEventListener('resize', resize);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
closeAll(); closeAll();
clearTimer(); clearTimer();
state.ctrlCacheMap = {}; state.ctrlCacheMap = {};
console.log("onBeforeUnmount");
console.log('onBeforeUnmount');
}); });
onDeactivated(() => { onDeactivated(() => {
closeAll(); closeAll();
clearTimer(); clearTimer();
console.log("onDeactivated");
console.log('onDeactivated');
state.ctrlCacheMap = {}; state.ctrlCacheMap = {};
window.removeEventListener("resize", resize);
window.removeEventListener('resize', resize);
}); });
return { return {
@ -842,9 +842,9 @@ export default defineComponent({
intervalTime, intervalTime,
changeIndex, changeIndex,
item, item,
...toRefs(state)
...toRefs(state),
}; };
}
},
}); });
</script> </script>

122
src/views/dashboard/comp/tooltip.vue

@ -0,0 +1,122 @@
<template>
<div class="njhl-tool-tip">
<div class="title">
<span>此建筑月用能统计</span>
<div class="close"></div>
</div>
<div class="content">
<div class="item" v-for="(item, index) in list" :key="index">
<div class="left">
<img :src="$util.getImg('/src/assets/images/njhl/tooltip/' + item.key + '.png')" alt="" />
<span>{{ item.label }}</span>
</div>
<div class="right">{{ item.value }}</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { IObject } from '@/types/interface';
import { defineComponent, reactive, toRefs, watch } from 'vue';
export default defineComponent({
props: { data: { type: Object, default: () => ({}) } },
setup(props) {
const state = reactive({
list: [
{ label: '电用量(kWh):', value: '--', key: 'electricValue' },
{ label: '水用量(t):', value: '--', key: 'waterValue' },
{ label: '蒸汽用量(t):', value: '--', key: 'steamValue' },
{ label: '压缩空气(Nm³):', value: '--', key: 'airValue' },
],
});
watch(
() => props.data,
(newVal: IObject) => {
for (const item of state.list) {
item.value = newVal ? newVal[item.key] ?? '--' : '--';
}
},
);
return {
...toRefs(state),
};
},
});
</script>
<style lang="less" scoped>
.njhl-tool-tip {
position: absolute;
left: 0;
top: 0;
width: 289px;
height: 253px;
color: #fff;
background: url('@/assets/images/njhl/tooltip/bg.png') no-repeat center;
background-size: 100% 100%;
transition: all 0.4s;
z-index: 9999;
flex-direction: column;
display: none;
> .title {
position: relative;
color: #34f857;
font-size: 18px;
height: 74px;
line-height: 90px;
font-weight: bold;
padding: 0 16px;
> .close {
cursor: pointer;
position: absolute;
width: 18px;
height: 18px;
top: 33px;
right: 20px;
background: url('@/assets/images/njhl/tooltip/close.png') no-repeat center;
transition: all 0.4s;
&:hover {
transform: scale(1.2);
}
}
}
> .content {
flex: 1;
padding: 0 16px;
display: flex;
flex-direction: column;
justify-content: center;
> .item {
display: flex;
align-items: center;
margin: 6px 0;
> .left {
width: 160px;
font-size: 14px;
white-space: nowrap;
> img {
margin-right: 6px;
}
}
> .right {
flex: 1;
text-align: left;
font-weight: bold;
font-size: 18px;
color: #d8e2ff;
line-height: 24px;
text-shadow: 14px 17px 13px rgba(255, 126, 0, 0.06);
background: linear-gradient(0deg, #ff5700 0%, #ffde00 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
}
}
</style>

503
src/views/dashboard/main.vue

@ -0,0 +1,503 @@
<template>
<div class="borads-index">
<screen-adapter class="adapter">
<div class="boards-container">
<div class="center" title="使用鼠标左键、滚轮实现拖拽、放大缩小。"></div>
<div class="top">
<div class="title" @click="reset" title="点击此标题将鸟瞰图及管线恢复成初始状态。">南京海陵药业能源管理平台</div>
<div class="time" v-if="timeCount && timeCount.ts > 0">
<div class="time-left">
{{ $m(timeCount.ts).format('HH:mm:ss') }}
</div>
<div class="time-right">
<div>{{ $date.getDay(timeCount.ts) }}</div>
<div>
{{ $m(timeCount.ts).format('YYYY/MM/DD') }}
</div>
</div>
</div>
</div>
<div class="pop">
<pop v-for="(item, index) in pop" :key="index" :item="item" :index="index"></pop>
</div>
<div class="left">
<block v-for="(item, index) in left" :key="index" :item="item" :data-form="dataForm" @change-code="dataForm.code = $event">
<component :is="item.comp" :key="index" :item="item" :option="item.option" tip-loop></component>
</block>
</div>
<div class="right">
<block v-for="(item, index) in right" :key="index" :item="item" :data-form="dataForm" @change-code="dataForm.code = $event">
<component :is="item.comp" :key="index" :item="item" :option="item.option" tip-loop></component>
</block>
</div>
</div>
<tooltip :data="toolTipData" />
</screen-adapter>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, onMounted, nextTick, watch, onBeforeUnmount } from 'vue';
import ScreenAdapter from '@/components/screen-adapter.vue';
import Block from './comp/block.vue';
import Bar from './comp/bar.vue';
import Pop from './comp/pop.vue';
import timeCountHooks from '@/hooks/time-count';
import { useFetch } from '@/hooks/fetch';
import utilService from '@/service/utilService';
import Chart from '@/components/chart.vue';
import { costStatistic, elecPayload, monthEnergyUsageTrend, usageRanking } from './common/model';
import { IObject } from '@/types/interface';
import moment from 'moment';
import toolHooks from '@/hooks/tool';
import { init, setPointerLineStatusAll } from './common/style';
import Tooltip from './comp/tooltip.vue';
import switchScreenHooks from '@/hooks/switch-screen';
export default defineComponent({
components: {
ScreenAdapter,
Tooltip,
Block,
Chart,
Bar,
Pop,
},
setup() {
const { timeCount } = timeCountHooks();
const { generateSvg2Dom } = toolHooks();
const state = reactive({
dragStretch: null,
toolTipData: {},
timer: null,
dataForm: {
code: '电',
codeMap: { : 'kWh', : 't', 蒸汽: 't', 压缩空气: 'Nm³' },
},
timeCount,
pop: [
{
key: 'yearCarbon',
icon: '',
label: '年碳排放量',
value: '--',
unit: 'tCO₂',
},
{
key: 'monthConsumptionValue',
icon: '',
label: '月综合能耗',
value: '--',
unit: 'tce',
},
{
key: 'yearConsumptionValue',
icon: '',
label: '年综合能耗',
value: '--',
unit: 'tce',
},
],
left: [
{
comp: 'bar',
title: '月用能统计',
data: [
{
key: 'electricDayValue',
icon: 'elec',
label: '电用量',
value: '',
unit: '万kWh',
},
{
key: 'waterDayValue',
icon: 'water',
label: '水用量',
value: '',
unit: 't',
},
{
key: 'steamDayValue',
icon: 'steam',
label: '蒸汽用量',
value: '',
unit: 't',
},
{
key: 'airDayValue',
icon: 'air',
label: '压缩空气用量',
value: '',
unit: 'Nm³',
},
{
key: 'gasValue',
icon: 'gas',
label: '天然气用量',
value: '',
unit: 'Nm³',
},
],
},
{
title: '年用能统计',
comp: 'bar',
data: [
{
key: 'electricDayValue',
icon: 'elec',
label: '电用量',
value: '',
unit: '万kWh',
},
{
key: 'waterDayValue',
icon: 'water',
label: '水用量',
value: '',
unit: 't',
},
{
key: 'steamDayValue',
icon: 'steam',
label: '蒸汽用量',
value: '',
unit: 't',
},
{
key: 'airDayValue',
icon: 'air',
label: '压缩空气用量',
value: '',
unit: 'Nm³',
},
{
key: 'gasValue',
icon: 'gas',
label: '天然气用量',
value: '',
unit: 'Nm³',
},
],
},
{
title: '当月用能趋势',
comp: 'chart',
option: monthEnergyUsageTrend(),
unit: 'kWh',
select: true,
},
],
right: [
{
comp: 'chart',
title: '能耗费用统计(月)',
option: costStatistic(),
unit: '元',
},
{
comp: 'chart',
title: '用能排行(月)',
option: usageRanking(),
unit: 'kWh',
select: true,
},
{
comp: 'chart',
title: '当日电力负荷',
option: elecPayload(),
unit: 'kWh',
},
],
}) as IObject;
const changeType = (type: string) => {
const c = state.dataForm.code;
const u = state.dataForm.codeMap[c];
state.left[2].unit = u;
state.right[1].unit = u;
//
useFetch('/board/monthEnergy/analyse', {
data: {
month: moment().format('YYYY-MM'),
type,
},
cb: (res: any) => {
if (utilService.isValidObject(res) && utilService.isValidArray(res.currentMonthData)) {
const d = res.currentMonthData;
state.left[2].option = monthEnergyUsageTrend(d);
}
},
});
//
useFetch('/board/monthEnergy/ranking', {
data: {
day: moment().format('YYYY-MM'),
type,
},
cb: (res: any) => {
if (utilService.isValidArray(res)) {
state.right[1].option = usageRanking(res);
}
},
});
};
const fn = () => {
//
useFetch('/board/carbonAndConsumption', {
cb: (res: any) => {
if (utilService.isValidObject(res)) {
for (const item of state.pop) {
item.value = res[item.key] ?? '--';
}
}
},
});
//
useFetch('/board/eachEnergy', {
cb: (res: any) => {
if (utilService.isValidObject(res)) {
for (const item of state.left[0].data) {
item.value = res[item.key] ?? '--';
}
}
},
});
//
useFetch('/board/eachEnergyYear', {
cb: (res: any) => {
if (utilService.isValidObject(res)) {
for (const item of state.left[1].data) {
item.value = res[item.key] ?? '--';
}
}
},
});
//
useFetch('/board/energyCost', {
data: {
day: moment().format('YYYY-MM-DD'),
},
cb: (res: any) => {
if (utilService.isValidArray(res)) {
state.right[0].option = costStatistic(res);
}
},
});
//
useFetch('/board/qcDayLoad/analyse', {
data: {
day: moment().format('YYYY-MM-DD'),
},
cb: (res: any) => {
if (utilService.isValidObject(res) && utilService.isValidArray(res.currentMonthData)) {
const d = res.currentMonthData;
state.right[2].option = elecPayload(d);
}
},
});
};
changeType(state.dataForm.code);
fn();
const reset = () => {
setPointerLineStatusAll('none');
state.dragStretch.reset();
};
const methods = { changeType, reset };
watch(
() => state.dataForm.code,
(newCode: string) => {
changeType(newCode);
},
);
const getSvg = () => {
debugger;
const target = document.querySelector('.boards-container .center') as HTMLElement;
generateSvg2Dom('/cdn/njhl_bg.svg', target, () => {
target.style.transform = 'scale(1.2)';
nextTick(() => {
init(state, target);
// document.getElementById("layer-d-animate").innerHTML = '<animate href="#path23264" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate><animate href="#path23264-3" attributeName="stroke-dashoffset" dur="15s" to="0" fill="freeze"></animate>';
});
});
};
onMounted(() => {
getSvg();
state.timer = setInterval(() => {
changeType(state.dataForm.code);
fn();
}, 1 * 60 * 1000);
});
onBeforeUnmount(() => {
clearInterval(state.timer);
state.timer = null;
});
return { ...toRefs(state), ...methods, ...switchScreenHooks({ screen: '1' }) };
},
});
</script>
<!-- <style lang="scss">
.rr-view-ctx-card {
height: 100%;
.el-card__body {
position: relative;
height: 100%;
}
}
</style> -->
<style lang="less" scoped>
.borads-index {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: #000b40;
.adapter {
.boards-container {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
> .top {
position: relative;
z-index: 2;
height: 88px;
> .title {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
color: #e2edff;
letter-spacing: 6px;
background: url('@/assets/images/njhl/title.png') no-repeat center;
background-size: 100% 100%;
text-align: center;
font-weight: bold;
font-size: 44px;
cursor: pointer;
}
> .time {
position: absolute;
left: 0;
top: 3px;
display: flex;
align-items: center;
> .time-left {
position: relative;
font-size: 32px;
margin: 0 60px;
background: linear-gradient(to top, #fff 30%, #3e576f 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-family: 'Alibaba PuHuiTi Heavy'; //
&:after {
position: absolute;
content: '';
background: #3e576f;
right: -30px;
top: 50%;
width: 1px;
height: 40px;
transform: translate(-50%, -50%);
}
}
> .time-right {
color: rgba(#fff, 0.7);
}
}
}
> .center {
position: absolute;
left: 0;
top: 0;
width: 1920px;
height: 1080px;
cursor: move;
//background: url("@/assets/images/njhl/bg.png");
//transform: translate(-50%, -50%);
}
.icon {
opacity: 0.4;
position: absolute;
top: 8px;
right: 120px;
z-index: 999;
::v-deep svg {
transition: all 80ms;
// opacity: 0.8;
//&:hover {
// width: 35px !important;
// height: 35px !important;
// opacity: 1;
//}
}
}
.pop {
position: absolute;
display: flex;
align-items: center;
justify-content: space-between;
height: 86px;
left: 50%;
top: 88px;
transform: translate(-50%);
> * {
margin: 0 40px;
}
}
.left,
.right {
position: absolute;
top: 88px;
height: calc(100% - 108px);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.left {
//left: 0;
left: 16px;
}
.right {
//right: 0;
right: 16px;
}
}
}
}
</style>
Loading…
Cancel
Save