28 Commits

Author SHA1 Message Date
guanj
cd2be0f4aa Merge branch 'master' of http://192.168.1.22:3000/Web/admin-sjzx
# Conflicts:
#	src/views/pqs/business/terminal/terminaCkeck/index.vue
2026-07-09 13:57:58 +08:00
guanj
855c569535 全局表格添加列配置功能 2026-07-09 13:54:18 +08:00
6a84d28101 添加辽宁周期检测功能 2026-07-09 11:41:01 +08:00
guanj
1a69be62a0 修改监测点信息 2026-07-09 11:08:56 +08:00
guanj
02caca969e 修改用户性质 2026-07-09 10:33:22 +08:00
guanj
3f1af5e4cc 监测点信息添加终端型号 2026-07-08 16:08:02 +08:00
guanj
88e9812d5f 联调周期监测 2026-07-08 14:37:19 +08:00
guanj
83a827ba5b 修改文件上传问题 2026-07-08 11:14:32 +08:00
40b1092718 添加周期 2026-07-08 09:21:59 +08:00
guanj
83a1128884 用户管理添加监测报告,评估报告 2026-07-07 13:52:53 +08:00
1a44a2f9e0 添加周期 2026-07-07 10:18:22 +08:00
guanj
5044c88946 优化全景展示页面 2026-07-07 10:15:33 +08:00
guanj
51a862ed00 修改进程显示 2026-07-06 10:58:02 +08:00
guanj
1ca8e536dd 修改限值显示问题 2026-07-06 10:41:33 +08:00
guanj
2445cfbff3 修改辽宁全景展示问题 2026-07-06 10:12:51 +08:00
guanj
21ac0611cd 修改地图api 2026-06-29 14:41:12 +08:00
guanj
ff841cfa93 修改Socket路径问题 2026-06-26 10:17:43 +08:00
guanj
01cc29c8b2 修改电网一张图 2026-06-24 09:57:48 +08:00
guanj
a324f7afcf 修改算法库拖动问题 2026-06-23 21:36:43 +08:00
guanj
4272f7756e 修改配置 2026-06-23 21:28:35 +08:00
guanj
070e404bbb 修改辽宁反馈问题 2026-06-23 21:27:15 +08:00
ed60e2b7f3 台账新增修改添加objName 2026-06-23 16:09:41 +08:00
7785d4f708 台账新增修改添加objName 2026-06-23 15:12:58 +08:00
0c64711dd8 辽宁页面调整 2026-06-22 15:50:31 +08:00
guanj
d85dba8bf8 修改补召 2026-06-16 09:56:38 +08:00
guanj
56b8158f4a 优化波形 2026-06-16 08:36:22 +08:00
cdf
765507398e 辽宁单点登录 2026-06-15 17:57:14 +08:00
cdf
9224ea17b8 区域报告调整 2026-06-07 15:56:31 +08:00
172 changed files with 10324 additions and 16828 deletions

6
.env.LN Normal file
View File

@@ -0,0 +1,6 @@
# 辽宁嵌入
NODE_ENV = LN
VITE_NAME="LN"
# 电网一张图 地图图层
VITE_NARIMAP=null
VITE_NRGISCOMMON=null

8813
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,105 +1,108 @@
{
"name": "canneng-admin",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --mode dev",
"dev:jibei": "vite --mode jibei",
"dev:hainan": "vite --mode hainan",
"dev:qujing": "vite --mode qujing",
"dev:LNqr": "vite --mode LNqr",
"dev:removeMode": "vite --mode removeMode",
"build": "vite build --mode dev",
"build:jibei": "vite build --mode jibei",
"build:hainan": "vite build --mode hainan",
"build:qujing": "vite build --mode qujing",
"build:LNqr": "vite build --mode LNqr",
"build:removeMode": "vite build --mode removeMode",
"preview": "vite preview"
},
"dependencies": {
"@ant-design/colors": "^7.0.2",
"@element-plus/icons-vue": "^2.3.1",
"@form-create/designer": "^3.1.3",
"@form-create/element-ui": "^3.1.24",
"@fortawesome/fontawesome-free": "^6.5.1",
"@kjgl77/datav-vue3": "^1.7.4",
"@vue-office/docx": "^1.6.3",
"@vue-office/excel": "^1.7.8",
"@vue-office/pdf": "^2.0.2",
"@vueuse/core": "^10.7.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"@wangeditor/plugin-formula": "^1.0.11",
"axios": "^1.6.2",
"bpmn-js": "8.9.0",
"bpmn-js-properties-panel": "0.46.0",
"bpmn-js-token-simulation": "^0.10.0",
"bpmn-moddle": "^6.0.0",
"camunda-bpmn-moddle": "^7.0.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
"diagram-js": "^11.4.1",
"diagram-js-minimap": "^2.0.4",
"echarts": "^5.4.3",
"echarts-gl": "^2.0.9",
"echarts-liquidfill": "^3.1.0",
"echarts4": "npm:echarts@^4.9.0",
"element-plus": "^2.9.11",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"grid-layout-plus": "^1.1.0",
"html2canvas": "^1.4.1",
"jquery": "^3.7.1",
"js-pinyin": "^0.2.5",
"jsencrypt": "^3.3.2",
"katex": "^0.16.11",
"less": "^4.2.0",
"lodash-es": "^4.17.21",
"luckyexcel": "^1.0.1",
"luckysheet": "^2.1.13",
"mathjax": "^3.2.2",
"min-dash": "^4.2.1",
"mitt": "^3.0.1",
"mqtt": "^5.13.3",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"qs": "^6.12.0",
"screenfull": "^6.0.2",
"splitpanes": "^3.1.5",
"steady-xml": "0.1.0",
"svg-pan-zoom": "^3.6.2",
"use-element-plus-theme": "^0.0.5",
"vexip-ui": "^2.3.28",
"vue": "^3.3.11",
"vue-baidu-map-3x": "^1.0.35",
"vue-baidu-map-offline": "^1.0.7",
"vue-demi": "^0.14.8",
"vue-draggable-resizable": "3.0.0-beta.2",
"vue-i18n": "9.10.2",
"vue-katex": "^0.5.0",
"vue-router": "4",
"vue-types": "^5.1.1",
"vxe-table": "^4.5.17",
"vxe-table-plugin-export-xlsx": "^4.0.1",
"web-storage-cache": "^1.1.1",
"xe-utils": "^3.5.14",
"yarn": "^1.22.22"
},
"devDependencies": {
"@purge-icons/generated": "^0.9.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.10.5",
"@types/splitpanes": "^2.2.6",
"@vitejs/plugin-vue": "^4.5.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"sass": "^1.69.5",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.16.7",
"vite": "^5.0.8",
"vite-plugin-mkcert": "^1.17.3",
"vue-tsc": "^1.8.25"
}
"name": "canneng-admin",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --mode dev",
"dev:jibei": "vite --mode jibei",
"dev:hainan": "vite --mode hainan",
"dev:qujing": "vite --mode qujing",
"dev:LN": "vite --mode LN",
"dev:LNqr": "vite --mode LNqr",
"dev:removeMode": "vite --mode removeMode",
"build": "vite build --mode dev",
"build:jibei": "vite build --mode jibei",
"build:hainan": "vite build --mode hainan",
"build:qujing": "vite build --mode qujing",
"build:LN": "vite build --mode LN",
"build:LNqr": "vite build --mode LNqr",
"build:removeMode": "vite build --mode removeMode",
"preview": "vite preview"
},
"dependencies": {
"@ant-design/colors": "^7.0.2",
"@element-plus/icons-vue": "^2.3.1",
"@form-create/designer": "^3.1.3",
"@form-create/element-ui": "^3.1.24",
"@fortawesome/fontawesome-free": "^6.5.1",
"@kjgl77/datav-vue3": "^1.7.4",
"@vue-office/docx": "^1.6.3",
"@vue-office/excel": "^1.7.8",
"@vue-office/pdf": "^2.0.2",
"@vueuse/core": "^10.7.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"@wangeditor/plugin-formula": "^1.0.11",
"axios": "^1.6.2",
"bpmn-js": "8.9.0",
"bpmn-js-properties-panel": "0.46.0",
"bpmn-js-token-simulation": "^0.10.0",
"bpmn-moddle": "^6.0.0",
"camunda-bpmn-moddle": "^7.0.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
"diagram-js": "^11.4.1",
"diagram-js-minimap": "^2.0.4",
"echarts": "^5.4.3",
"echarts-gl": "^2.0.9",
"echarts-liquidfill": "^3.1.0",
"echarts4": "npm:echarts@^4.9.0",
"element-plus": "^2.9.11",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"grid-layout-plus": "^1.1.0",
"html2canvas": "^1.4.1",
"jquery": "^3.7.1",
"js-pinyin": "^0.2.5",
"jsencrypt": "^3.3.2",
"katex": "^0.16.11",
"less": "^4.2.0",
"lodash-es": "^4.17.21",
"luckyexcel": "^1.0.1",
"luckysheet": "^2.1.13",
"mathjax": "^3.2.2",
"min-dash": "^4.2.1",
"mitt": "^3.0.1",
"mqtt": "^5.13.3",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"qs": "^6.12.0",
"screenfull": "^6.0.2",
"splitpanes": "^3.1.5",
"steady-xml": "0.1.0",
"svg-pan-zoom": "^3.6.2",
"use-element-plus-theme": "^0.0.5",
"vexip-ui": "^2.3.28",
"vue": "^3.3.11",
"vue-baidu-map-3x": "^1.0.35",
"vue-baidu-map-offline": "^1.0.7",
"vue-demi": "^0.14.8",
"vue-draggable-resizable": "3.0.0-beta.2",
"vue-i18n": "9.10.2",
"vue-katex": "^0.5.0",
"vue-router": "4",
"vue-types": "^5.1.1",
"vxe-pc-ui": "~4.15.19",
"vxe-table": "^4.19.22",
"vxe-table-plugin-export-xlsx": "^4.0.7",
"web-storage-cache": "^1.1.1",
"xe-utils": "^3.5.14",
"yarn": "^1.22.22"
},
"devDependencies": {
"@purge-icons/generated": "^0.9.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.10.5",
"@types/splitpanes": "^2.2.6",
"@vitejs/plugin-vue": "^4.5.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"sass": "^1.69.5",
"typescript": "^5.2.2",
"unplugin-auto-import": "^0.16.7",
"vite": "^5.0.8",
"vite-plugin-mkcert": "^1.17.3",
"vue-tsc": "^1.8.25"
}
}

View File

@@ -4747,10 +4747,11 @@ window.BMAP_AUTHENTIC_KEY = ''
c.Nm
? c.Nm.sa(b)
: ((c.Nm = new U(b, {
icon: new qc(G.qa + 'blank.gif', {
width: 1,
height: 1
}),
// icon: new qc(G.qa + 'blank.gif', {
// width: 1,
// height: 1
// }),
icon: '',
offset: new O(0, 0),
clickable: t
})),
@@ -5886,7 +5887,7 @@ window.BMAP_AUTHENTIC_KEY = ''
Qc.prototype.co = function () {
this.Gb.src =
0 < z.ca.ia && 6 >= z.ca.ia && this.GJ
? G.qa + 'blank.gif'
? '' //G.qa + 'blank.gif'
: '' !== this.src && this.Gb.src == this.src
? this.src + '&t = ' + Date.now()
: this.src

View File

@@ -9,16 +9,20 @@ import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import useSetTheme from '@/utils/setTheme'
import { onMounted } from 'vue'
useSetTheme()
onMounted(async () => {
const fetchMqttUrl = async () => {
const response = await fetch('/')
const WebSocketUrl:any = response.headers.get('X-WebSocket-Url')
const WebSocketUrl2:any = response.headers.get('X-WebSocket-Url2')
const WebSocketUrl3:any = response.headers.get('X-WebSocket-Url3')
const MqttUrl:any = response.headers.get('X-MqttUrl-Url')
const WebSocketUrl: any = response.headers.get('X-WebSocket-Url')
const WebSocketUrl2: any = response.headers.get('X-WebSocket-Url2')
const WebSocketUrl3: any = response.headers.get('X-WebSocket-Url3')
const MqttUrl: any = response.headers.get('X-MqttUrl-Url')
localStorage.setItem('WebSocketUrl', WebSocketUrl)
localStorage.setItem('WebSocketUrl2', WebSocketUrl2)
localStorage.setItem('WebSocketUrl3', WebSocketUrl3)
localStorage.setItem('MqttUrl', MqttUrl)
}
onMounted(() => {
fetchMqttUrl()
})
</script>

View File

@@ -1,7 +1,7 @@
import request from '@/utils/request'
// 获取区域中断终端
export function getAreaOffDev(data) {
export function getAreaOffDev(data:any) {
return request({
url: '/device-boot/device/getAreaOffDev',
method: 'post',
@@ -9,10 +9,28 @@ export function getAreaOffDev(data) {
})
}
// 获取区域中断终端
export function getYwZtSubstation(data) {
export function getYwZtSubstation(data:any) {
return request({
url: '/device-boot/ywZt/getYwZtSubstation',
method: 'GET',
params: data
})
}
//终端周期检测
export function terminalCheckPage(data:any) {
return request({
url: '/device-boot/alarm/terminalCheckPage',
method: 'post',
data
})
}
export function userCheckUpload(data:any) {
return request({
url: '/device-boot/userReport/userCheckUpload',
method: 'post',
data
})
}

View File

@@ -4,6 +4,10 @@ import request from '@/utils/request'
export function getSubLineGiveAnAlarm(data: any) {
return request({ url: '/harmonic-boot/gridDiagram/getSubLineGiveAnAlarm', method: 'post', data })
}
export function getAreaObjAlarm(data: any) {
return request({ url: '/harmonic-boot/gridDiagram/getAreaObjAlarm', method: 'post', data })
}
// 变电站详细列表
export function getPollutionAlarmPageData(data: any) {
return request({ url: '/harmonic-boot/gridDiagram/getPollutionAlarmPageData', method: 'post', data })

View File

@@ -44,13 +44,30 @@ export async function login(params: any) {
})
}
//辽宁嵌入登录获取token
export async function loginLNqr() {
export async function loginLNqr(data: any) {
return request({
url: '/pqs-auth/oauth/lnLogin',
method: 'get',
params: data
})
}
export async function lnRefreshToken(data: any) {
return request({
url: '/pqs-auth/oauth/lnRefreshToken',
method: 'get',
params: data
})
}
export async function logout() {
return request({
url: '/pqs-auth/oauth/logout',
method: 'DELETE',
})
}
//获取用户信息
export function getUserById() {
const adminInfo = useAdminInfo()
@@ -77,6 +94,17 @@ export function refreshToken(): Promise<any> {
})
}
export function lnRefreshTokenTo(): Promise<any> {
// 调用useAdminInfo函数获取管理员信息并将其赋值给adminInfo变量
const adminInfo = useAdminInfo()
return lnRefreshToken({
refreshToken: adminInfo.refresh_token,
clientId: "njcn",
clientSecret:"njcnpqs"
})
}
/**
* 获取营销用户列表
* @returns {AxiosPromise}

View File

@@ -30,7 +30,7 @@ import { downloadFile } from '@/api/system-boot/file'
import { previewFile } from '@/utils/fileDownLoad'
const { push, options, currentRoute } = useRouter()
const VITE_FLAG = import.meta.env.VITE_NAME == 'jibei'
const VITE_FLAG = false//import.meta.env.VITE_NAME == 'jibei'
// const url = 'http://192.168.1.22:9009/excelreport' + currentRoute.value.href?.split('?')[1]
const url = ref('')
const excelOptions = ref({})

View File

@@ -126,7 +126,8 @@ const prop = defineProps({
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: [String, Number] },
timeValue: { type: Object }
timeValue: { type: Object },
searchKey: { type: [String, Number] }
})
const headerHeight = ref(57)
@@ -347,22 +348,22 @@ onMounted(() => {
tableStore.index()
})
watch(
() => prop.timeKey,
() => prop.searchKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
// watch(
// () => prop.timeValue,
val => {
tableStore.index()
},
// val => {
// tableStore.index()
// },
{
deep: true
}
)
// {
// deep: true
// }
// )
</script>
<style lang="scss" scoped>
@import '@/assets/font/iconfont.css';

View File

@@ -122,7 +122,8 @@ const prop = defineProps({
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: [String, Number] },
timeValue: { type: Object }
timeValue: { type: Object },
searchKey: { type: [String, Number] }
})
const headerHeight = ref(57)
@@ -329,22 +330,22 @@ onMounted(() => {
tableStore.index()
})
watch(
() => prop.timeKey,
() => prop.searchKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
// watch(
// () => prop.timeValue,
val => {
tableStore.index()
},
// val => {
// tableStore.index()
// },
{
deep: true
}
)
// {
// deep: true
// }
// )
</script>
<style lang="scss" scoped>
@import '@/assets/font/iconfont.css';

View File

@@ -7,7 +7,8 @@
@selectChange="selectChange"
datePicker
v-if="fullscreen"
></TableHeader>
>
</TableHeader>
<div
class="monitoringPoints"
:style="{
@@ -120,7 +121,8 @@ const prop = defineProps({
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: [String, Number] },
timeValue: { type: Object }
timeValue: { type: Object },
searchKey: { type: [String, Number] }
})
const headerHeight = ref(57)
@@ -328,22 +330,22 @@ onMounted(() => {
tableStore.index()
})
watch(
() => prop.timeKey,
() => prop.searchKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
// watch(
// () => prop.timeValue,
val => {
tableStore.index()
},
// val => {
// tableStore.index()
// },
{
deep: true
}
)
// {
// deep: true
// }
// )
</script>
<style lang="scss" scoped>
@import '@/assets/font/iconfont.css';

View File

@@ -150,7 +150,8 @@ const prop = defineProps({
width: { type: [String, Number] },
height: { type: [String, Number] },
timeKey: { type: [String, Number] },
timeValue: { type: Object }
timeValue: { type: Object },
searchKey: { type: [String, Number] }
})
const headerHeight = ref(57)
@@ -233,7 +234,7 @@ const tableStore: any = new TableStore({
if (tableStore.table.params.statisticalType.name == '终端厂家') {
return k.count != 0
} else {
return !k.citName.includes('上送')
return !k.name.includes('上送')
}
})
}
@@ -246,22 +247,28 @@ onMounted(() => {
tableStore.index()
})
watch(
() => prop.timeKey,
() => prop.searchKey,
val => {
tableStore.index()
}
)
watch(
() => prop.timeValue,
// watch(
// () => prop.timeKey,
// val => {
// tableStore.index()
// }
// )
// watch(
// () => prop.timeValue,
val => {
tableStore.index()
},
// val => {
// tableStore.index()
// },
{
deep: true
}
)
// {
// deep: true
// }
// )
</script>
<style lang="scss" scoped>
@import '@/assets/font/iconfont.css';

View File

@@ -0,0 +1,322 @@
// 辅助函数
const getMax = (temp, tempA, tempB, tempC) => {
temp = temp > tempA ? temp : tempA
temp = temp > tempB ? temp : tempB
if (tempC !== undefined) {
temp = temp > tempC ? temp : tempC
}
return temp
}
const getMaxTwo = (temp, tempA, tempB) => {
temp = temp > tempA ? temp : tempA
temp = temp > tempB ? temp : tempB
return temp
}
const getMin = (temp, tempA, tempB, tempC) => {
temp = temp < tempA ? temp : tempA
temp = temp < tempB ? temp : tempB
if (tempC !== undefined) {
temp = temp < tempC ? temp : tempC
}
return temp
}
const getMinOpen = (temp, tempA, tempB) => {
temp = temp < tempA ? temp : tempA
temp = temp < tempB ? temp : tempB
return temp
}
// 数据处理函数
const fliteWaveData = (wp, step, iphasicValue, isOpen) => {
const rmsData = wp.listRmsData
const pt = Number(wp.pt) / 1000
const ct = Number(wp.ct)
const titleList = wp.waveTitle
let xishu = pt
let aTitle = '',
bTitle = '',
cTitle = '',
unit = '电压'
let rmsvFirstX = 0,
rmsvFirstY = 0,
rmsvSecondX = 0,
rmsvSecondY = 0,
firstZhou = 'a',
secondeZhou = 'a'
let ifmax = 0,
ifmin = 0,
ismax = 0,
ismin = 0,
rfmax = 0,
rfmin = 0,
rsmax = 0,
rsmin = 0
const shunshiFA = []
const shunshiFB = []
const shunshiFC = []
const shunshiSA = []
const shunshiSB = []
const shunshiSC = []
const rmsFA = []
const rmsFB = []
const rmsFC = []
const rmsSA = []
const rmsSB = []
const rmsSC = []
if (titleList[iphasicValue * step + 1]?.substring(0, 1) !== 'U') {
xishu = ct
unit = '电流'
}
for (let i = 1; i <= iphasicValue; i++) {
switch (i) {
case 1:
aTitle = titleList[iphasicValue * step + i]?.substring(1) || ''
break
case 2:
bTitle = titleList[iphasicValue * step + i]?.substring(1) || ''
break
case 3:
cTitle = titleList[iphasicValue * step + i]?.substring(1) || ''
break
}
}
if (rmsData[0] && rmsData[0][iphasicValue * step + 1] !== undefined) {
rfmax = rmsData[0][iphasicValue * step + 1] * xishu
rfmin = rmsData[0][iphasicValue * step + 1] * xishu
rmsvFirstY = rmsData[0][iphasicValue * step + 1] * xishu
rmsvFirstX = rmsData[0][0]
rsmax = rmsData[0][iphasicValue * step + 1]
rsmin = rmsData[0][iphasicValue * step + 1]
rmsvSecondY = rmsData[0][iphasicValue * step + 1]
rmsvSecondX = rmsData[0][0]
}
for (let rms = 0; rms < rmsData.length; rms++) {
if (!rmsData[rms] || rmsData[rms][iphasicValue * step + 1] === undefined) {
break
}
switch (iphasicValue) {
case 1:
const rmsFirstA = rmsData[rms][iphasicValue * step + 1] * xishu
rmsFA.push([rmsData[rms][0], rmsFirstA])
rfmax = rfmax > rmsFirstA ? rfmax : rmsFirstA
rfmin = rfmin < rmsFirstA ? rfmin : rmsFirstA
if (rfmin < rmsvFirstY) {
rmsvFirstY = rfmin
firstZhou = 'a'
rmsvFirstX = rmsData[rms][0]
}
const rmsSecondA = rmsData[rms][iphasicValue * step + 1]
rmsSA.push([rmsData[rms][0], rmsSecondA])
rsmax = rsmax > rmsSecondA ? rsmax : rmsSecondA
rsmin = rsmin < rmsSecondA ? rsmin : rmsSecondA
if (rsmin < rmsvSecondY) {
rmsvSecondY = rsmin
secondeZhou = 'a'
rmsvSecondX = rmsData[rms][0]
}
break
case 2:
const rmsFirstA2 = rmsData[rms][iphasicValue * step + 1] * xishu
const rmsFirstB2 = rmsData[rms][iphasicValue * step + 2] * xishu
rmsFA.push([rmsData[rms][0], rmsFirstA2])
rmsFB.push([rmsData[rms][0], rmsFirstB2])
rfmax = getMaxTwo(rfmax, rmsFirstA2, rmsFirstB2)
rfmin = getMinOpen(rfmin, rmsFirstA2, rmsFirstB2)
if (rfmin < rmsvFirstY) {
rmsvFirstY = rfmin
if (rfmin === rmsFirstA2) {
firstZhou = 'a'
} else if (rfmin === rmsFirstB2) {
firstZhou = 'b'
}
rmsvFirstX = rmsData[rms][0]
}
const rmsSecondA2 = rmsData[rms][iphasicValue * step + 1]
const rmsSecondB2 = rmsData[rms][iphasicValue * step + 2]
rmsSA.push([rmsData[rms][0], rmsSecondA2])
rmsSB.push([rmsData[rms][0], rmsSecondB2])
rsmax = getMaxTwo(rsmax, rmsSecondA2, rmsSecondB2)
rsmin = getMinOpen(rsmin, rmsSecondA2, rmsSecondB2)
if (rsmin < rmsvSecondY) {
rmsvSecondY = rsmin
if (rsmin === rmsSecondA2) {
secondeZhou = 'a'
} else if (rsmin === rmsSecondB2) {
secondeZhou = 'b'
}
rmsvSecondX = rmsData[rms][0]
}
break
case 3:
const rmsFirstA3 = rmsData[rms][iphasicValue * step + 1] * xishu
const rmsFirstB3 = rmsData[rms][iphasicValue * step + 2] * xishu
const rmsFirstC3 = rmsData[rms][iphasicValue * step + 3] * xishu
rmsFA.push([rmsData[rms][0], rmsFirstA3])
rmsFB.push([rmsData[rms][0], rmsFirstB3])
rmsFC.push([rmsData[rms][0], rmsFirstC3])
rfmax = getMax(rfmax, rmsFirstA3, rmsFirstB3, rmsFirstC3)
rfmin = isOpen
? getMinOpen(rfmin, rmsFirstA3, rmsFirstC3)
: getMin(rfmin, rmsFirstA3, rmsFirstB3, rmsFirstC3)
if (rfmin < rmsvFirstY) {
rmsvFirstY = rfmin
if (rfmin === rmsFirstA3) {
firstZhou = 'a'
} else if (rfmin === rmsFirstB3) {
firstZhou = 'b'
} else {
firstZhou = 'c'
}
rmsvFirstX = rmsData[rms][0]
}
const rmsSecondA3 = rmsData[rms][iphasicValue * step + 1]
const rmsSecondB3 = rmsData[rms][iphasicValue * step + 2]
const rmsSecondC3 = rmsData[rms][iphasicValue * step + 3]
rmsSA.push([rmsData[rms][0], rmsSecondA3])
rmsSB.push([rmsData[rms][0], rmsSecondB3])
rmsSC.push([rmsData[rms][0], rmsSecondC3])
rsmax = getMax(rsmax, rmsSecondA3, rmsSecondB3, rmsSecondC3)
rsmin = isOpen
? getMinOpen(rsmin, rmsSecondA3, rmsSecondC3)
: getMin(rsmin, rmsSecondA3, rmsSecondB3, rmsSecondC3)
if (rsmin < rmsvSecondY) {
rmsvSecondY = rsmin
if (rsmin === rmsSecondA3) {
secondeZhou = 'a'
} else if (rsmin === rmsSecondB3) {
secondeZhou = 'b'
} else {
secondeZhou = 'c'
}
rmsvSecondX = rmsData[rms][0]
}
break
}
}
const instantF = { max: ifmax, min: ifmin }
const instantS = { max: ismax, min: ismin }
const RMSF = { max: rfmax, min: rfmin }
const RMSS = { max: rsmax, min: rsmin }
const RMSFMinDetail = { rmsvFirstX, rmsvFirstY, firstZhou }
const RMSSMinDetail = { rmsvSecondX, rmsvSecondY, secondeZhou }
const shunshiF = { shunshiFA, shunshiFB, shunshiFC }
const shunshiS = { shunshiSA, shunshiSB, shunshiSC }
const RMSFWave = { rmsFA, rmsFB, rmsFC }
const RMSSWave = { rmsSA, rmsSB, rmsSC }
const title = { aTitle, bTitle, cTitle, unit }
return {
instantF,
instantS,
RMSF,
RMSS,
RMSFMinDetail,
RMSSMinDetail,
shunshiF,
shunshiS,
RMSFWave,
RMSSWave,
title,
unit
}
}
// 监听消息
self.onmessage = function (e) {
const { wp, isOpen, value, boxoList, requestId } = e.data
try {
const iphasicValue = wp.iphasic || 1
const picCounts = (wp.waveTitle.length - 1) / iphasicValue
const waveDatas = []
for (let i = 0; i < picCounts; i++) {
const data = fliteWaveData(wp, i, iphasicValue, isOpen, boxoList)
waveDatas.push(data)
}
// 处理标题
let titles = ''
if (boxoList.systemType == 'pms') {
titles =
'变电站名称:' +
boxoList.powerStationName +
' 监测点名称:' +
boxoList.measurementPointName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'%  持续时间:' +
boxoList.duration +
's'
} else if (boxoList.systemType == 'ZL') {
titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.equipmentName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
boxoList.evtParamVVaDepth +
'% 持续时间:' +
boxoList.evtParamTm +
's'
} else if (boxoList.systemType == 'YPT') {
titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.persistTime +
's'
} else {
titles =
' 变电站名称:' +
boxoList.subName +
' 监测点名称:' +
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.duration +
's'
}
// 发送处理结果回主线程
self.postMessage({
requestId,
titles: titles,
success: true,
waveDatas,
time: wp.time,
type: wp.waveType,
severity: wp.yzd,
iphasic: iphasicValue
})
} catch (error) {
self.postMessage({
requestId,
success: false,
error: error.message
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
// waveData.worker.js
self.addEventListener('message', function (e) {
const { wp, value, iphasic, isOpen, boxoList, requestId } = e.data
// 处理波形数据的函数
const fliteWaveData = (wp, step) => {
// 将原有的fliteWaveData函数实现复制到这里
const shunData = wp.listWaveData
const pt = Number(wp.pt) / 1000
const ct = Number(wp.ct)
const titleList = wp.waveTitle
let xishu = pt
let aTitle = '',
bTitle = '',
cTitle = '',
unit = '电压'
let ifmax = 0,
ifmin = 0,
ismax = 0,
ismin = 0
const shunshiFA = []
const shunshiFB = []
const shunshiFC = []
const shunshiSA = []
const shunshiSB = []
const shunshiSC = []
if (shunData.length > 0) {
if (titleList[iphasic * step + 1]?.substring(0, 1) !== 'U') {
xishu = ct
unit = '电流'
}
for (let i = 1; i <= iphasic; i++) {
switch (i) {
case 1:
aTitle = titleList[iphasic * step + i]?.substring(1) || ''
break
case 2:
bTitle = titleList[iphasic * step + i]?.substring(1) || ''
break
case 3:
cTitle = titleList[iphasic * step + i]?.substring(1) || ''
break
}
}
if (shunData[0][iphasic * step + 1] !== undefined) {
ifmax = shunData[0][iphasic * step + 1] * xishu
ifmin = shunData[0][iphasic * step + 1] * xishu
ismax = shunData[0][iphasic * step + 1]
ismin = shunData[0][iphasic * step + 1]
}
for (let shun = 0; shun < shunData.length; shun++) {
if (shunData[shun][iphasic * step + 1] === undefined) {
break
}
switch (iphasic) {
case 1:
const shunFirstA = shunData[shun][iphasic * step + 1] * xishu
shunshiFA.push([shunData[shun][0], shunFirstA])
ifmax = Math.max(ifmax, shunFirstA)
ifmin = Math.min(ifmin, shunFirstA)
const shunSecondA = shunData[shun][iphasic * step + 1]
shunshiSA.push([shunData[shun][0], shunSecondA])
ismax = Math.max(ismax, shunSecondA)
ismin = Math.min(ismin, shunSecondA)
break
case 2:
const shunFirstA2 = shunData[shun][iphasic * step + 1] * xishu
const shunFirstB2 = shunData[shun][iphasic * step + 2] * xishu
shunshiFA.push([shunData[shun][0], shunFirstA2])
shunshiFB.push([shunData[shun][0], shunFirstB2])
ifmax = Math.max(ifmax, shunFirstA2, shunFirstB2)
ifmin = Math.min(ifmin, shunFirstA2, shunFirstB2)
const shunSecondA2 = shunData[shun][iphasic * step + 1]
const shunSecondB2 = shunData[shun][iphasic * step + 2]
shunshiSA.push([shunData[shun][0], shunSecondA2])
shunshiSB.push([shunData[shun][0], shunSecondB2])
ismax = Math.max(ismax, shunSecondA2, shunSecondB2)
ismin = Math.min(ismin, shunSecondA2, shunSecondB2)
break
case 3:
const shunFirstA3 = shunData[shun][iphasic * step + 1] * xishu
const shunFirstB3 = shunData[shun][iphasic * step + 2] * xishu
const shunFirstC3 = shunData[shun][iphasic * step + 3] * xishu
shunshiFA.push([shunData[shun][0], shunFirstA3])
shunshiFB.push([shunData[shun][0], shunFirstB3])
shunshiFC.push([shunData[shun][0], shunFirstC3])
ifmax = Math.max(ifmax, shunFirstA3, shunFirstB3, shunFirstC3)
ifmin = isOpen
? Math.min(ifmin, shunFirstA3, shunFirstC3)
: Math.min(ifmin, shunFirstA3, shunFirstB3, shunFirstC3)
const shunSecondA3 = shunData[shun][iphasic * step + 1]
const shunSecondB3 = shunData[shun][iphasic * step + 2]
const shunSecondC3 = shunData[shun][iphasic * step + 3]
shunshiSA.push([shunData[shun][0], shunSecondA3])
shunshiSB.push([shunData[shun][0], shunSecondB3])
shunshiSC.push([shunData[shun][0], shunSecondC3])
ismax = Math.max(ismax, shunSecondA3, shunSecondB3, shunSecondC3)
ismin = isOpen
? Math.min(ismin, shunSecondA3, shunSecondC3)
: Math.min(ismin, shunSecondA3, shunSecondB3, shunSecondC3)
break
}
}
}
const instantF = { max: ifmax, min: ifmin }
const instantS = { max: ismax, min: ismin }
const shunshiF = { shunshiFA, shunshiFB, shunshiFC }
const shunshiS = { shunshiSA, shunshiSB, shunshiSC }
const title = { aTitle, bTitle, cTitle, unit }
return { instantF, instantS, shunshiF, shunshiS, title, unit }
}
// 处理标题
let titles = ''
if (boxoList.systemType == 'pms') {
titles =
'变电站名称:' +
boxoList.powerStationName +
' 监测点名称:' +
boxoList.measurementPointName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.duration +
's'
} else if (boxoList.systemType == 'ZL') {
titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.equipmentName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
boxoList.evtParamVVaDepth +
'% 持续时间:' +
boxoList.evtParamTm +
's'
} else if (boxoList.systemType == 'YPT') {
titles =
(boxoList.engineeringName == undefined ? '' : ' 项目名称:' + boxoList.engineeringName) +
' 监测点名称:' +
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.persistTime +
's'
} else {
titles =
'变电站名称:' +
boxoList.subName +
' 监测点名称:' +
boxoList.lineName +
' 发生时刻:' +
boxoList.startTime +
' 暂降(骤升)幅值:' +
(boxoList.featureAmplitude * 100).toFixed(2) +
'% 持续时间:' +
boxoList.duration +
's'
}
const iphasicValue = wp.iphasic || 1
const picCounts = (wp.waveTitle.length - 1) / iphasicValue
const waveDatas = []
for (let i = 0; i < picCounts; i++) {
const data = fliteWaveData(wp, i)
waveDatas.push(data)
}
const time = wp.time
const type = wp.waveType
let severity = wp.yzd
if (severity < 0) {
severity = '/'
type = '/'
}
// 将处理结果发送回主线程
self.postMessage({
requestId,
success: true,
waveDatas,
time,
type,
severity,
titles,
iphasic: iphasicValue
})
})

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
<div v-if="view2">
<el-row>
<el-col :span="12">
<span style="font-size: 14px; line-height: 30px">值类型选择:</span>
<!-- <span style="font-size: 14px; line-height: 30px">值类型选择:</span>
<el-select
style="min-width: 200px; width: 200px"
@change="changeView"
@@ -15,7 +15,11 @@
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-select> -->
<el-radio-group v-model.trim="value" @change="changeView">
<el-radio-button label="一次值" :value="1" />
<el-radio-button label="二次值" :value="2" />
</el-radio-group>
<!-- <el-button v-if="view2 && senior" class="ml10" type="primary" @click="AdvancedAnalytics">
高级分析
</el-button> -->
@@ -24,37 +28,18 @@
<el-button @click="backbxlb" icon="el-icon-Back" style="float: right">返回</el-button>
</el-col>
</el-row>
<div v-loading="loading" style="height: calc(100vh - 190px)">
<el-tabs v-if="view4" class="default-main" v-model="bxactiveName" @tab-click="bxhandleClick">
<el-tab-pane
label="瞬时波形"
name="ssbx"
class="boxbx pt10 pb10"
:style="'height:' + bxecharts + ';overflow-y: scroll;'"
>
<shushiboxi
ref="shushiboxiRef"
v-if="bxactiveName == 'ssbx' && showBoxi"
:value="value"
:parentHeight="parentHeight"
:boxoList="boxoList"
:wp="wp"
></shushiboxi>
<div v-loading="loading" style="height: calc(100vh - 190px)" class="mt10">
<el-tabs v-if="view4" type="border-card" v-model="bxactiveName"
@tab-click="bxhandleClick">
<el-tab-pane label="瞬时波形" name="ssbx" class="boxbx pt10 pb10"
:style="'height:' + bxecharts + ';overflow-y: scroll;'">
<shushiboxi ref="shushiboxiRef" v-if="bxactiveName == 'ssbx' && showBoxi" :value="value"
:parentHeight="parentHeight" :boxoList="boxoList" :wp="wp"></shushiboxi>
</el-tab-pane>
<el-tab-pane
label="RMS波形"
class="boxbx pt10 pb10"
name="rmsbx"
:style="'height:' + bxecharts + ';overflow-y: scroll;'"
>
<rmsboxi
ref="rmsboxiRef"
v-if="bxactiveName == 'rmsbx' && showBoxi"
:value="value"
:parentHeight="parentHeight"
:boxoList="boxoList"
:wp="wp"
></rmsboxi>
<el-tab-pane label="RMS波形" class="boxbx pt10 pb10" name="rmsbx"
:style="'height:' + bxecharts + ';overflow-y: scroll;'">
<rmsboxi ref="rmsboxiRef" v-if="bxactiveName == 'rmsbx' && showBoxi" :value="value"
:parentHeight="parentHeight" :boxoList="boxoList" :wp="wp"></rmsboxi>
</el-tab-pane>
</el-tabs>
<el-empty v-else description="暂无数据" style="height: calc(100vh - 190px)" />
@@ -70,10 +55,12 @@
import shushiboxi from '@/components/echarts/shushiboxi.vue'
import rmsboxi from '@/components/echarts/rmsboxi.vue'
import analytics from '@/components/echarts/analytics.vue'
import { ref, reactive } from 'vue'
import { ref, shallowRef } from 'vue'
import { analysis } from '@/api/advance-boot/analyse'
import { mainHeight } from '@/utils/layout'
import { getMonitorEventAnalyseWave, downloadWaveFile } from '@/api/event-boot/transient'
import { buildWaveStoreKey, registerWaveRaw, buildSlimWp, releaseWaveRaw } from '@/utils/waveRawStore'
import { clearWaveCache } from '@/utils/waveCache'
const emit = defineEmits(['backbxlb'])
interface Props {
// boxoList: any
@@ -100,10 +87,10 @@ const options = ref([
}
])
const shushiboxiRef = ref()
const bxecharts = ref(mainHeight(95).height as any)
const bxecharts = ref(mainHeight(145).height as any)
const view2 = ref(true)
const boxoList: any = ref(null)
const wp = ref(null)
const wp = shallowRef<any>(null)
const showBoxi = ref(true)
const view3 = ref(false)
const view4 = ref(false)
@@ -117,7 +104,9 @@ const open = async (row: any) => {
if (res != undefined) {
boxoList.value = row
boxoList.value.pt = res.data.pt
wp.value = res.data
const storeKey = buildWaveStoreKey(row)
registerWaveRaw(storeKey, res.data)
wp.value = buildSlimWp(res.data, storeKey)
loading.value = false
view4.value = true
}
@@ -128,9 +117,6 @@ const open = async (row: any) => {
})
}
const bxhandleClick = (tab: any) => {
if (shushiboxiRef.value) shushiboxiRef.value.backbxlb()
if (rmsboxiRef.value) rmsboxiRef.value.backbxlb()
loading.value = true
if (tab.name == 'ssbx') {
bxactiveName.value = 'ssbx'
@@ -143,10 +129,12 @@ const bxhandleClick = (tab: any) => {
// console.log(tab, event);
}
const backbxlb = () => {
const storeKey = wp.value?.waveStoreKey
releaseWaveRaw(storeKey, 'all')
clearWaveCache()
boxoList.value = null
wp.value = null
if (shushiboxiRef.value) shushiboxiRef.value.backbxlb()
if (rmsboxiRef.value) rmsboxiRef.value.backbxlb()
emit('backbxlb')
}
@@ -173,8 +161,8 @@ const AdvancedAnalytics = () => {
view2.value = false
}
const changeView = () => {
if (shushiboxiRef.value) shushiboxiRef.value.backbxlb()
if (rmsboxiRef.value) rmsboxiRef.value.backbxlb()
shushiboxiRef.value?.backbxlb?.()
rmsboxiRef.value?.backbxlb?.()
showBoxi.value = false
setTimeout(() => {
showBoxi.value = true

View File

@@ -1,22 +1,22 @@
<template>
<div style="width: 540px">
<div :style="{ width: isMinuteData ? '750px' : '610px' }">
<el-select v-model="interval" style="min-width: 90px; width: 90px; margin-right: 10px" @change="timeChange">
<el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-date-picker
v-model="timeValue"
type="daterange"
:type="pickerType"
:disabled="disabledPicker"
:disabled-date="isFutureDate"
style="width: 220px; margin-right: 10px"
:style="{ width: isMinuteData ? '360px' : '220px', marginRight: '10px' }"
unlink-panels
:clearable="false"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
:shortcuts="shortcuts"
:start-placeholder="isMinuteData ? '开始时间' : '开始日期'"
:end-placeholder="isMinuteData ? '结束时间' : '结束日期'"
:value-format="pickerValueFormat"
:shortcuts="isMinuteData ? undefined : shortcuts"
/>
<el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button>
<el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button>
@@ -26,18 +26,41 @@
<script lang="ts" setup>
import { DArrowLeft, VideoPause, DArrowRight } from '@element-plus/icons-vue'
import { ref, onMounted, nextTick, watch } from 'vue'
import { ref, onMounted, computed, watch } from 'vue'
interface Props {
nextFlag?: boolean
theCurrentTime?: boolean
isStatisticData?: number | string
}
const props = withDefaults(defineProps<Props>(), {
nextFlag: false,
theCurrentTime: false
theCurrentTime: false,
isStatisticData: 0
})
const isMinuteData = computed(() => Number(props.isStatisticData) === 1)
const pickerType = computed(() => (isMinuteData.value ? 'datetimerange' : 'daterange'))
const pickerValueFormat = computed(() => (isMinuteData.value ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'))
const toDatePart = (value: string) => (value?.includes(' ') ? value.split(' ')[0] : value)
const toMinuteStart = (date: string) => `${toDatePart(date)} 00:00:00`
const toMinuteEnd = (date: string) => `${toDatePart(date)} 23:59:59`
const normalizeRange = (start: string, end: string) => {
if (!isMinuteData.value) {
return [toDatePart(start), toDatePart(end)]
}
return [toMinuteStart(start), toMinuteEnd(end)]
}
const setRangeValue = (start: string, end: string) => {
timeValue.value = normalizeRange(start, end)
}
const interval = ref(3)
const timeFlag = ref(1)
const count = ref(0)
@@ -95,13 +118,13 @@ const timeChange = (e: number) => {
if (e == 1) {
disabledPicker.value = true
timeValue.value = [setTime(1), setTime()]
setRangeValue(setTime(1), setTime())
} else if (e == 2) {
disabledPicker.value = true
timeValue.value = [setTime(2), setTime()]
setRangeValue(setTime(2), setTime())
} else if (e == 3) {
disabledPicker.value = true
timeValue.value = [setTime(3), setTime()]
setRangeValue(setTime(3), setTime())
} else if (e == 4) {
let year = parseInt(setTime().substring(0, 4))
let month = parseInt(setTime().substring(5, 7))
@@ -111,12 +134,12 @@ const timeChange = (e: number) => {
var dayOfWeek = start.getDay() == 0 ? 7 : start.getDay() - 1 // 如果为周日则置为7天
disabledPicker.value = true
timeValue.value = [setTime(0, dayOfWeek), setTime(0, -6 + dayOfWeek)]
setRangeValue(setTime(0, dayOfWeek), setTime(0, -6 + dayOfWeek))
} else if (e == 5) {
disabledPicker.value = false
backDisabled.value = true
preDisabled.value = props.nextFlag ? false : true
timeValue.value = [setTime(), setTime()]
setRangeValue(setTime(), setTime())
}
if (e == 1 || e == 2) {
timeFlag.value = 0
@@ -186,7 +209,7 @@ const preClick = () => {
startTime = year + '-01-01'
endTime = year + '-12-31'
}
timeValue.value = [startTime, endTime]
setRangeValue(startTime, endTime)
// 判断向后键的状态
// var temp = NowgetEndTime()
@@ -415,16 +438,27 @@ const next = () => {
}
if (!props.nextFlag) {
if (
new Date(endTime + ' 00:00:00').getTime() >=
new Date(toDatePart(endTime) + ' 00:00:00').getTime() >=
new Date(window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd ') + ' 00:00:00').getTime()
) {
preDisabled.value = props.nextFlag ? false : true
}
}
timeValue.value = [startTime, endTime]
setRangeValue(startTime, endTime)
}
watch(
() => props.isStatisticData,
() => {
if (timeValue.value?.length === 2) {
setRangeValue(timeValue.value[0], timeValue.value[1])
} else {
timeChange(interval.value)
}
}
)
const setTime = (flag = 0, e = 0) => {
let dd = window.XEUtils.toDateString(new Date().getTime() - e * 3600 * 1000 * 24, 'dd')

View File

@@ -9,11 +9,8 @@ export const defaultAttribute: VxeTableProps = {
columnConfig: { resizable: true },
rowConfig: { isCurrent: true, isHover: true },
scrollX: { scrollToLeftOnChange: true },
scrollY: { scrollToTopOnChange: true, enabled: true },
treeConfig: {
reserve: true
},
showOverflow: true,
showFooter:true
scrollY: { scrollToTopOnChange: true, enabled: true, gt: 100 },
customConfig: { enabled: true, allowFixed: false, showFooter: false, immediate: true ,mode:'default'},
showOverflow: 'tooltip',
showHeaderOverflow: false
}

View File

@@ -1,20 +1,39 @@
<template>
<div ref="tableHeader" class="cn-table-header">
<div class="table-header ba-scroll-style">
<el-form style="flex: 1; height: 32px; display: flex; flex-wrap: wrap" ref="headerForm" @submit.prevent=""
@keyup.enter="onComSearch" label-position="left" :inline="true">
<el-form-item v-if="datePicker" style="grid-column: span 2; max-width: 630px">
<el-form
style="flex: 1; height: 32px; display: flex; flex-wrap: wrap"
ref="headerForm"
@submit.prevent=""
@keyup.enter="onComSearch"
label-position="left"
:inline="true"
>
<el-form-item
v-if="datePicker"
style="grid-column: span 2"
:style="{ maxWidth: isStatisticData == 1 ? '750px' : '610px' }"
>
<template #label>
<el-checkbox v-if="showTimeAll" v-model="timeAll" label="统计时间" />
<span v-else>{{ dateLabel }}</span>
</template>
<DatePicker ref="datePickerRef" v-if="timeAll" :nextFlag="nextFlag"
:theCurrentTime="theCurrentTime"></DatePicker>
<DatePicker
ref="datePickerRef"
v-if="timeAll"
:nextFlag="nextFlag"
:theCurrentTime="theCurrentTime"
:isStatisticData="isStatisticData"
></DatePicker>
</el-form-item>
<el-form-item label="区域" v-if="area">
<Area ref="areaRef" v-model="tableStore.table.params.deptIndex" @change-value="onAreaChange"
style="width: 200px;" />
<Area
ref="areaRef"
v-model="tableStore.table.params.deptIndex"
@change-value="onAreaChange"
style="width: 200px"
/>
</el-form-item>
<slot name="select"></slot>
</el-form>
@@ -23,24 +42,44 @@
<Icon size="14" name="el-icon-ArrowUp" style="color: #fff" v-if="showSelect" />
<Icon size="14" name="el-icon-ArrowDown" style="color: #fff" v-else />
</el-button>
<el-button @click="onComSearch" v-if="showSearch" :loading="tableStore.table.loading" type="primary"
:icon="Search">
<el-button
@click="onComSearch"
v-if="showSearch"
:loading="tableStore.table.loading"
type="primary"
:icon="Search"
>
查询
</el-button>
<el-button @click="onResetForm" v-if="showSearch && showReset" :loading="tableStore.table.loading"
:icon="RefreshLeft">
<el-button
@click="onResetForm"
v-if="showSearch && showReset"
:loading="tableStore.table.loading"
:icon="RefreshLeft"
>
重置
</el-button>
<el-button @click="onExport" v-if="showExport" :loading="tableStore.table.exportLoading" type="primary"
icon="el-icon-Download">
<el-button
@click="onExport"
v-if="showExport"
:loading="tableStore.table.exportLoading"
type="primary"
icon="el-icon-Download"
>
导出
</el-button>
</template>
<slot name="operation"></slot>
<el-button class="ml10" v-if="showCustomColumn" :icon="Setting" @click="openCustomColumn">列设置</el-button>
</div>
<el-form :style="showSelect && showUnfoldButton ? headerFormSecondStyleOpen : headerFormSecondStyleClose"
ref="headerFormSecond" @submit.prevent="" @keyup.enter="onComSearch" label-position="left"
:inline="true"></el-form>
<el-form
:style="showSelect && showUnfoldButton ? headerFormSecondStyleOpen : headerFormSecondStyleClose"
ref="headerFormSecond"
@submit.prevent=""
@keyup.enter="onComSearch"
label-position="left"
:inline="true"
></el-form>
</div>
</template>
@@ -51,8 +90,7 @@ import DatePicker from '@/components/form/datePicker/index.vue'
import Area from '@/components/form/area/index.vue'
import { mainHeight } from '@/utils/layout'
import { useDictData } from '@/stores/dictData'
import { Search, RefreshLeft } from '@element-plus/icons-vue'
import { defineProps } from 'vue'
import { Search, RefreshLeft, Setting } from '@element-plus/icons-vue'
const emit = defineEmits(['selectChange', 'areaChange'])
const tableStore = inject('tableStore') as TableStore
const tableHeader = ref()
@@ -72,6 +110,8 @@ interface Props {
showExport?: boolean //导出控制
showTimeAll?: boolean //控制时间是否显示
dateLabel?: string //设置时间名称
isStatisticData?: number | string // 0统计数据 1分钟数据
showCustomColumn?: boolean // 列设置
}
const props = withDefaults(defineProps<Props>(), {
@@ -83,7 +123,9 @@ const props = withDefaults(defineProps<Props>(), {
showReset: true,
showExport: false,
showTimeAll: false,
dateLabel: '统计日期'
dateLabel: '统计日期',
isStatisticData: 0,
showCustomColumn: false
})
// 动态计算table高度
const resizeObserver = new ResizeObserver(entries => {
@@ -125,7 +167,13 @@ watch(
}, 500)
}
)
const openCustomColumn = () => {
tableStore?.table?.ref?.openCustom?.()
}
onMounted(() => {
if (props.showCustomColumn && tableStore?.table) {
tableStore.table.customColumnInHeader = true
}
timeAll.value = props.showTimeAll ? false : true
if (props.datePicker && timeAll.value) {
tableStore.table.params.searchBeginTime = datePickerRef.value.timeValue[0]
@@ -297,7 +345,7 @@ defineExpose({
margin-left: 12px;
}
.mlr-12+.el-button {
.mlr-12 + .el-button {
margin-left: 12px;
}
@@ -332,7 +380,7 @@ defineExpose({
border-radius: 0;
}
.el-button+.el-button {
.el-button + .el-button {
margin: 0;
}
@@ -343,7 +391,6 @@ defineExpose({
html.dark {
.table-search-button-group {
button:focus,
button:active {
background-color: var(--el-color-info-dark-2);

View File

@@ -1,24 +1,46 @@
<template>
<div :style="{ height: typeof props.height === 'string' ? props.height : tableStore.table.height }">
<vxe-table ref="tableRef" height="auto" :key="key" :data="tableStore.table.data"
v-loading="tableStore.table.loading" v-bind="Object.assign({}, defaultAttribute, $attrs)"
@checkbox-all="selectChangeEvent" @checkbox-change="selectChangeEvent" :showOverflow="showOverflow"
@sort-change="handleSortChange">
<div v-if="showCustomColumn && !tableStore?.table?.customColumnInHeader" class="table-custom-toolbar">
<el-button icon="el-icon-Setting" @click="openCustomColumn">列设置</el-button>
</div>
<vxe-table
ref="tableRef"
height="auto"
:key="key"
:data="tableStore.table.data"
v-loading="tableStore.table.loading"
v-bind="tableBindProps"
@checkbox-all="selectChangeEvent"
@checkbox-change="selectChangeEvent"
@cell-click="onCellClick"
>
<!-- @sort-change="handleSortChange" -->
<!-- Column 组件内部是 el-table-column -->
<template v-if="isGroup">
<GroupColumn :column="tableStore.table.column" />
</template>
<template v-else>
<Column :attr="item" :key="key + '-column'" v-for="(item, key) in tableStore.table.column"
:tree-node="item.treeNode">
<Column
:attr="item"
:key="key + '-column'"
v-for="(item, key) in tableStore.table.column"
:tree-node="item.treeNode"
>
<!-- tableStore 预设的列 render 方案 -->
<template v-if="item.render" #default="scope">
<FieldRender :field="item" :row="scope.row" :column="scope.column" :index="scope.rowIndex" :key="key +
'-' +
item.render +
'-' +
(item.field ? '-' + item.field + '-' + scope.row[item.field] : '')
" />
<FieldRender
:field="item"
:row="scope.row"
:column="scope.column"
:index="scope.rowIndex"
:key="
key +
'-' +
item.render +
'-' +
(item.field ? '-' + item.field + '-' + scope.row[item.field] : '')
"
/>
</template>
</Column>
</template>
@@ -27,17 +49,22 @@
</div>
<div v-if="tableStore.showPage" class="table-pagination">
<el-pagination :currentPage="tableStore.table.params!.pageNum" :page-size="tableStore.table.params!.pageSize"
:page-sizes="pageSizes" background
<el-pagination
:currentPage="tableStore.table.params!.pageNum"
:page-size="tableStore.table.params!.pageSize"
:page-sizes="pageSizes"
background
:layout="config.layout.shrink ? 'prev, next, jumper' : 'sizes,total, ->, prev, pager, next, jumper'"
:total="tableStore.table.total" @size-change="onTableSizeChange"
@current-change="onTableCurrentChange"></el-pagination>
:total="tableStore.table.total"
@size-change="onTableSizeChange"
@current-change="onTableCurrentChange"
></el-pagination>
</div>
<slot name="footer"></slot>
</template>
<script setup lang="ts">
import { ref, nextTick, inject, computed, onMounted, watch } from 'vue'
import { ref, nextTick, inject, computed, onMounted, watch, useAttrs } from 'vue'
import type { ElTable } from 'element-plus'
import { VxeTableEvents, VxeTableInstance } from 'vxe-table'
import FieldRender from '@/components/table/fieldRender/index.vue'
@@ -53,17 +80,32 @@ const tableRef = ref<VxeTableInstance>()
const tableStore = inject('tableStore') as TableStoreClass
const router = useRouter()
const key = ref(0)
const emit = defineEmits<{
(e: 'cell-click', params: any): void
}>()
interface Props extends /* @vue-ignore */ Partial<InstanceType<typeof ElTable>> {
isGroup?: boolean
showOverflow?: boolean
showOverflow?: boolean | 'ellipsis' | 'title' | 'tooltip'
height?: string | number
showCustomColumn?: boolean
}
const props = withDefaults(defineProps<Props>(), {
isGroup: false,
showOverflow: true,
height: undefined
showOverflow: 'tooltip',
height: undefined,
showCustomColumn: false
})
const attrs = useAttrs()
const tableBindProps = computed(() => {
return Object.assign({}, defaultAttribute, attrs, {
showOverflow: props.showOverflow,
showHeaderOverflow: false
})
})
const onCellClick: VxeTableEvents.CellClick<any> = params => {
emit('cell-click', params)
}
onMounted(() => {
tableStore.table.ref = tableRef.value as VxeTableInstance
})
@@ -93,7 +135,9 @@ const selectChangeEvent: VxeTableEvents.CheckboxChange<any> = ({ checked }) => {
const records = (tableRef.value as VxeTableInstance).getCheckboxRecords()
tableStore.onTableAction('selection-change', records)
}
const openCustomColumn = () => {
tableRef.value?.openCustom?.()
}
const getRef = () => {
return tableRef.value
}

View File

@@ -3,167 +3,177 @@
<el-tabs type="border-card" v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="监测点详情" name="tab1">
<el-descriptions :column="3" border label-width="150px">
<el-descriptions-item label="项目工程">
<el-descriptions-item label="项目工程">
{{ details.areaName }}
</el-descriptions-item>
<el-descriptions-item label="项目单位">
<el-descriptions-item label="项目单位">
{{ details.gdName }}
</el-descriptions-item>
<el-descriptions-item label="所属变电站">
<el-descriptions-item label="所属变电站">
{{ details.bdName }}
</el-descriptions-item>
<el-descriptions-item label="电压等级">
{{ details.scale }}
</el-descriptions-item>
<el-descriptions-item label="终端厂家">
{{ details.manufacturer }}
</el-descriptions-item>
<el-descriptions-item label="终端名称">
{{ details.devName }}
</el-descriptions-item>
<el-descriptions-item label="网络参数">
{{ details.ip }}
</el-descriptions-item>
<el-descriptions-item label="监测点名称">
<el-descriptions-item label="监测点名称">
{{ details.lineName }}
</el-descriptions-item>
<el-descriptions-item label="监测点序号">
{{ details.id }}
</el-descriptions-item>
<el-descriptions-item label="通讯状态">
{{ details.comFlag }}
</el-descriptions-item>
<el-descriptions-item label="干扰源类型">
{{ details.loadType }}
</el-descriptions-item>
<el-descriptions-item label="监测点对象名称">
<el-descriptions-item label="监测点对象名称">
{{ details.objName }}
</el-descriptions-item>
<el-descriptions-item label="接线方式">
<el-descriptions-item label="监测点序号">
{{ details.id }}
</el-descriptions-item>
<el-descriptions-item label="终端厂家">
{{ details.manufacturer }}
</el-descriptions-item>
<el-descriptions-item label="终端名称">
{{ details.devName }}
</el-descriptions-item>
<el-descriptions-item label="终端型号">
{{ typeArr.find(item => item.id == details.devType)?.name }}
</el-descriptions-item>
<el-descriptions-item label="电压等级">
{{ details.scale }}
</el-descriptions-item>
<el-descriptions-item label="网络参数">
{{ details.ip }}
</el-descriptions-item>
<el-descriptions-item label="通讯状态">
{{ details.comFlag }}
</el-descriptions-item>
<el-descriptions-item label="干扰源类型">
{{ details.loadType }}
</el-descriptions-item>
<el-descriptions-item label="接线方式">
{{ details.ptType }}
</el-descriptions-item>
<el-descriptions-item label="PT变比">
<el-descriptions-item label="统计间隔(分钟)">
{{ details.timeInterval }}
</el-descriptions-item>
<el-descriptions-item label="PT变比">
{{ details.pt }}
</el-descriptions-item>
<el-descriptions-item label="CT变比">
<el-descriptions-item label="CT变比">
{{ details.ct }}
</el-descriptions-item>
<el-descriptions-item label="基准容量(MVA)">
<el-descriptions-item label="基准容量(MVA)">
{{ details.standardCapacity }}
</el-descriptions-item>
<el-descriptions-item label="最小短路容量(MVA)">
{{ details.shortCapacity }}
</el-descriptions-item>
<el-descriptions-item label="供电设备容量(MVA)">
<el-descriptions-item label="供电设备容量(MVA)">
{{ details.devCapacity }}
</el-descriptions-item>
<el-descriptions-item label="用户协议容量(MVA)">
<el-descriptions-item label="最小短路容量(MW)">
{{ details.shortCapacity }}
</el-descriptions-item>
<el-descriptions-item label="用户协议容量(MW)">
{{ details.dealCapacity }}
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
<el-tab-pane label="监测点限值" name="tab2">
<el-descriptions :column="3" border label-width="250px">
<el-descriptions-item label="电压偏差上限值(%)">
<el-descriptions :column="3" border label-width="250px">
<el-descriptions-item label="电压偏差上限值(%)">
{{ limitValue.voltageDev }}
</el-descriptions-item>
<el-descriptions-item label="电压偏差下限值(%)">
<el-descriptions-item label="电压偏差下限值(%)">
{{ limitValue.uvoltageDev }}
</el-descriptions-item>
<el-descriptions-item label="频率偏差限值(Hz)">
<el-descriptions-item label="频率偏差限值(Hz)">
{{ limitValue.freqDev }}
</el-descriptions-item>
<el-descriptions-item label="三相电压不平衡度限值(%)">
<el-descriptions-item label="三相电压不平衡度限值(%)">
{{ limitValue.ubalance }}
</el-descriptions-item>
<el-descriptions-item label="负序电流限值(A)">
<el-descriptions-item label="负序电流限值(A)">
{{ limitValue.ineg }}
</el-descriptions-item>
<el-descriptions-item label="长时闪变限值(%)">
<el-descriptions-item label="长时闪变限值(%)">
{{ limitValue.flicker }}
</el-descriptions-item>
<el-descriptions-item label="电压总畸变率限值(%)">
<el-descriptions-item label="电压总畸变率限值(%)">
{{ limitValue.uaberrance }}
</el-descriptions-item>
<el-descriptions-item label="奇次谐波电压含有率限值(%)">
<el-descriptions-item label="奇次谐波电压含有率限值(%)">
{{ limitValue.oddHarm }}
</el-descriptions-item>
<el-descriptions-item label="偶次谐波电压含有率限值(%)">
<el-descriptions-item label="偶次谐波电压含有率限值(%)">
{{ limitValue.evenHarm }}
</el-descriptions-item>
<el-descriptions-item label="3次谐波电流幅值限值(A)">
<el-descriptions-item label="3次谐波电流幅值限值(A)">
{{ limitValue.iharm3 }}
</el-descriptions-item>
<el-descriptions-item label="4次谐波电流幅值限值(A)">
<el-descriptions-item label="4次谐波电流幅值限值(A)">
{{ limitValue.iharm4 }}
</el-descriptions-item>
<el-descriptions-item label="5次谐波电流幅值限值(A)">
<el-descriptions-item label="5次谐波电流幅值限值(A)">
{{ limitValue.iharm5 }}
</el-descriptions-item>
<el-descriptions-item label="6次谐波电流幅值限值(A)">
<el-descriptions-item label="6次谐波电流幅值限值(A)">
{{ limitValue.iharm6 }}
</el-descriptions-item>
<el-descriptions-item label="7次谐波电流幅值限值(A)">
<el-descriptions-item label="7次谐波电流幅值限值(A)">
{{ limitValue.iharm7 }}
</el-descriptions-item>
<el-descriptions-item label="8次谐波电流幅值限值(A)">
<el-descriptions-item label="8次谐波电流幅值限值(A)">
{{ limitValue.iharm8 }}
</el-descriptions-item>
<el-descriptions-item label="9次谐波电流幅值限值(A)">
<el-descriptions-item label="9次谐波电流幅值限值(A)">
{{ limitValue.iharm9 }}
</el-descriptions-item>
<el-descriptions-item label="10次谐波电流幅值限值(A)">
<el-descriptions-item label="10次谐波电流幅值限值(A)">
{{ limitValue.iharm10 }}
</el-descriptions-item>
<el-descriptions-item label="11次谐波电流幅值限值(A)">
<el-descriptions-item label="11次谐波电流幅值限值(A)">
{{ limitValue.iharm11 }}
</el-descriptions-item>
<el-descriptions-item label="12次谐波电流幅值限值(A)">
<el-descriptions-item label="12次谐波电流幅值限值(A)">
{{ limitValue.iharm12 }}
</el-descriptions-item>
<el-descriptions-item label="13次谐波电流幅值限值(A)">
<el-descriptions-item label="13次谐波电流幅值限值(A)">
{{ limitValue.iharm13 }}
</el-descriptions-item>
<el-descriptions-item label="14次谐波电流幅值限值(A)">
<el-descriptions-item label="14次谐波电流幅值限值(A)">
{{ limitValue.iharm14 }}
</el-descriptions-item>
<el-descriptions-item label="15次谐波电流幅值限值(A)">
<el-descriptions-item label="15次谐波电流幅值限值(A)">
{{ limitValue.iharm15 }}
</el-descriptions-item>
<el-descriptions-item label="16次谐波电流幅值限值(A)">
<el-descriptions-item label="16次谐波电流幅值限值(A)">
{{ limitValue.iharm16 }}
</el-descriptions-item>
<el-descriptions-item label="17次谐波电流幅值限值(A)">
<el-descriptions-item label="17次谐波电流幅值限值(A)">
{{ limitValue.iharm17 }}
</el-descriptions-item>
<el-descriptions-item label="18次谐波电流幅值限值(A)">
<el-descriptions-item label="18次谐波电流幅值限值(A)">
{{ limitValue.iharm18 }}
</el-descriptions-item>
<el-descriptions-item label="19次谐波电流幅值限值(A)">
<el-descriptions-item label="19次谐波电流幅值限值(A)">
{{ limitValue.iharm19 }}
</el-descriptions-item>
<el-descriptions-item label="20次谐波电流幅值限值(A)">
<el-descriptions-item label="20次谐波电流幅值限值(A)">
{{ limitValue.iharm20 }}
</el-descriptions-item>
<el-descriptions-item label="21次谐波电流幅值限值(A)">
<el-descriptions-item label="21次谐波电流幅值限值(A)">
{{ limitValue.iharm21 }}
</el-descriptions-item>
<el-descriptions-item label="22次谐波电流幅值限值(A)">
<el-descriptions-item label="22次谐波电流幅值限值(A)">
{{ limitValue.iharm22 }}
</el-descriptions-item>
<el-descriptions-item label="23次谐波电流幅值限值(A)">
<el-descriptions-item label="23次谐波电流幅值限值(A)">
{{ limitValue.iharm23 }}
</el-descriptions-item>
<el-descriptions-item label="24次谐波电流幅值限值(A)">
<el-descriptions-item label="24次谐波电流幅值限值(A)">
{{ limitValue.iharm24 }}
</el-descriptions-item>
<el-descriptions-item label="25次谐波电流幅值限值(A)">
<el-descriptions-item label="25次谐波电流幅值限值(A)">
{{ limitValue.iharm25 }}
</el-descriptions-item>
<el-descriptions-item label="0.5-1.5次间谐波电压含有率限值(%)">
<el-descriptions-item label="0.5-1.5次间谐波电压含有率限值(%)">
{{ limitValue.inUharm }}
</el-descriptions-item>
<el-descriptions-item label="2.5-15.5次谐波电压含有率限值(%)">
<el-descriptions-item label="2.5-15.5次谐波电压含有率限值(%)">
{{ limitValue.inUharm16 }}
</el-descriptions-item>
</el-descriptions>
@@ -174,11 +184,14 @@
<script lang="ts" setup>
import { ref, inject } from 'vue'
import { getLineOverLimitData, getLineDetailData } from '@/api/device-boot/line'
import { useDictData } from '@/stores/dictData'
const dialogVisible = ref(false)
const title = ref('')
const activeName = ref('tab1')
const details: any = ref([])
const dictData = useDictData()
const limitValue: any = ref([])
const typeArr = dictData.getBasicData('Dev_Type')
const open = (data: any) => {
details.value = []
limitValue.value = []
@@ -187,7 +200,7 @@ const open = (data: any) => {
getLineDetailData(data.id).then(res => {
details.value = res.data
res.data.objName ? (title.value = res.data.objName + '_' + title.value) : ''
})
getLineOverLimitData(data.id).then(res => {
limitValue.value = res.data

View File

@@ -11,25 +11,25 @@
:data="eventList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
>
<!-- <vxe-column type="seq" width="70px" title="序号"></vxe-column> -->
<vxe-column field="time" width="180px" sortable title="发生时刻"></vxe-column>
<vxe-column field="lineName" title="监测点"></vxe-column>
<vxe-column field="powerCompany" title="变电站" width="100px"></vxe-column>
<vxe-column field="powerCompany" title="供电公司" width="100px"></vxe-column>
<vxe-column field="persistTime" width="120px" sortable title="持续时间(s)">
<template #default="{ row }">
{{ Math.floor(row.persistTime * 1000) / 1000 }}
</template>
</vxe-column>
<vxe-column field="time" width="180px" sortable title="暂降发生时刻"></vxe-column>
<vxe-column field="eventValue" width="160px" sortable title="暂降(骤升)幅值(%)">
<template #default="{ row }">
{{ Math.floor(row.eventValue * 10000) / 100 }}
</template>
</vxe-column>
<vxe-column field="persistTime" width="120px" sortable title="持续时间(s)">
<template #default="{ row }">
{{ Math.floor(row.persistTime * 1000) / 1000 }}
</template>
</vxe-column>
<vxe-column field="eventType" width="100px" title="触发类型">
<template #default="{ row }">
{{ event.filter(item => item.id == row.eventType)[0]?.name || '/' }}
</template>
</vxe-column>
<vxe-column field="powerCompany" title="变电站" width="100px"></vxe-column>
<vxe-column field="powerCompany" title="供电公司" width="100px"></vxe-column>
<vxe-column field="lineName" title="监测点"></vxe-column>
</vxe-table>
<div class="table-pagination mt10">
<el-pagination
@@ -99,7 +99,6 @@ const dataSocket = reactive({
// }
const socket = async () => {
const url = localStorage.getItem('WebSocketUrl3') || 'null' //'ws://192.168.2.130:10203/event/'
// const url = 'ws://192.168.1.68:10203/event/'

View File

@@ -8,41 +8,14 @@
name="el-icon-BellFilled"
size="18"
/>
<span class="nav-menu-text" v-if="globalPopUpRef?.eventList.length != 0">
{{ (globalPopUpRef?.eventList.length>99? '99+':globalPopUpRef?.eventList.length) || 0 }}
<span class="nav-menu-text" v-if="globalPopUpRef?.eventList?.length > 0">
{{ globalPopUpRef.eventList.length > 99 ? '99+' : globalPopUpRef.eventList.length }}
</span>
</div>
</el-tooltip>
<!-- <el-tooltip effect="dark" content="截图" placement="bottom">
<div @click="savePng" class="nav-menu-item">
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
name="el-icon-Camera"
size="18"
/>
</div>
</el-tooltip>
<el-tooltip effect="dark" :content="state.isFullScreen ? '缩小' : '放大'" placement="bottom">
<div @click="onFullScreen" class="nav-menu-item" :class="state.isFullScreen ? 'hover' : ''">
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
v-if="state.isFullScreen"
name="fa-solid fa-compress"
size="18"
/>
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
v-else
name="fa-solid fa-expand"
size="18"
/>
</div>
</el-tooltip> -->
<el-dropdown style="height: 100%" @command="handleCommand">
<div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''">
<div class="admin-info" :class="state.currentNavMenu === 'adminInfo' ? 'hover' : ''">
<el-avatar :size="25" fit="fill">
<img src="@/assets/avatar.png" alt="" />
</el-avatar>
@@ -51,105 +24,115 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="adminInfo">个人资料</el-dropdown-item>
<el-dropdown-item command="changePwd">修改密码</el-dropdown-item>
<el-dropdown-item command="layout">退出登录</el-dropdown-item>
<el-dropdown-item command="changePwd" v-if="!IS_LNQR">修改密码</el-dropdown-item>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- <div @click="configStore.setLayout('showDrawer', true)" class="nav-menu-item">
<Icon
:color="configStore.getColorVal('headerBarTabColor')"
class="nav-menu-icon"
name="fa fa-cogs"
size="18"
/>
</div> -->
<Config />
<PopupPwd ref="popupPwd" />
<AdminInfo ref="popupAdminInfo" />
<!-- <TerminalVue /> -->
<!-- 全局暂降事件 -->
<globalPopUp ref="globalPopUpRef" />
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import screenfull from 'screenfull'
import { useConfig } from '@/stores/config'
import { ElMessage } from 'element-plus'
import Config from './config.vue'
import { useAdminInfo } from '@/stores/adminInfo'
import router from '@/router'
import globalPopUp from './globalPopUp.vue'
import { routePush } from '@/utils/router'
import html2canvas from 'html2canvas'
import { useNavTabs } from '@/stores/navTabs'
import { logout } from '@/api/user-boot/user'
import Config from './config.vue'
import PopupPwd from './popup/password.vue'
import AdminInfo from './popup/adminInfo.vue'
import { useNavTabs } from '@/stores/navTabs'
import globalPopUp from './globalPopUp.vue'
const { replace } = useRouter()
// 环境标识
const IS_LNQR = import.meta.env.VITE_NAME === 'LNqr'
const adminInfo = useAdminInfo()
const navTabs = useNavTabs()
const configStore = useConfig()
// 引用
const popupPwd = ref()
const popupAdminInfo = ref()
const state = reactive({
isFullScreen: false,
currentNavMenu: '',
showLayoutDrawer: false,
showAdminInfoPopover: false
})
const globalPopUpRef = ref()
const savePng = () => {
html2canvas(document.body, {
scale: 1,
useCORS: true
}).then(function (canvas) {
var link = document.createElement('a')
link.href = canvas.toDataURL('image/png')
link.download = 'screenshot.png'
link.click()
})
}
const onFullScreen = () => {
if (!screenfull.isEnabled) {
ElMessage.warning('layouts.Full screen is not supported')
return false
// 状态
const state = reactive({
currentNavMenu: ''
})
// 打开暂降事件
const temporaryLandingEvent = () => {
if (globalPopUpRef.value) {
globalPopUpRef.value.open()
}
screenfull.toggle()
screenfull.onchange(() => {
state.isFullScreen = screenfull.isFullscreen
})
}
const handleCommand = async(key: string) => {
console.log(key)
// 下拉菜单命令
const handleCommand = async (key: string) => {
switch (key) {
case 'adminInfo':
popupAdminInfo.value.open()
popupAdminInfo.value?.open()
break
case 'changePwd':
popupPwd.value.open()
popupPwd.value?.open()
break
case 'layout':
await window.location.reload()
setTimeout(() => {
navTabs.closeTabs()
window.localStorage.clear()
adminInfo.reset()
router.push({ name: 'login' })
}, 0)
// navTabs.closeTabs()
// window.localStorage.clear()
// adminInfo.reset()
// router.push({ name: 'login' })
break
default:
case 'logout':
await handleLogout()
break
}
}
const temporaryLandingEvent = () => {
globalPopUpRef.value.open()
// 统一登出逻辑
const handleLogout = async () => {
try {
// 1. 调用后端登出接口
await logout()
// console.log('✅ 后端登出成功')
} catch (err) {
// console.error('❌ 登出接口异常', err)
} finally {
// 2. 清理本地所有数据
clearLocalData()
// 3. LNqr 环境跳 CAS 登出
if (IS_LNQR) {
redirectToCasLogout()
} else {
// 普通环境直接跳登录页
replace('/login')
}
}
}
// 清理本地数据
const clearLocalData = () => {
// 清空 localStorage
window.localStorage.clear()
// 清空用户状态
adminInfo.reset()
// 清空标签页
navTabs.closeTabs()
console.log('✅ 本地数据已全部清空')
}
// CAS 登出跳转
const redirectToCasLogout = () => {
const casLogoutUrl =
'http://privilege-epri.dcloud.ln.dc.sgcc.com.cn/cas/logout?service=http://privilege-epri.dcloud.ln.dc.sgcc.com.cn/cas/login?service=http://PQMonitoring.dcloud.ln.dc.sgcc.com.cn'
window.location.href = casLogoutUrl
// 清 Cookie
document.cookie.split(';').forEach(cookie => {
const name = cookie.split('=')[0].trim()
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
})
}
</script>
@@ -163,7 +146,6 @@ const temporaryLandingEvent = () => {
height: 60px;
display: flex;
align-items: center;
// height: 100%;
margin-left: auto;
background-color: v-bind('configStore.getColorVal("headerBarBackground")');
@@ -219,37 +201,15 @@ const temporaryLandingEvent = () => {
}
}
.dropdown-menu-box :deep(.el-dropdown-menu__item) {
justify-content: center;
}
.admin-info-base {
display: flex;
justify-content: center;
flex-wrap: wrap;
padding-top: 10px;
.admin-info-other {
display: block;
width: 100%;
text-align: center;
padding: 10px 0;
.admin-info-name {
font-size: var(--el-font-size-large);
}
}
}
.admin-info-footer {
padding: 10px 0;
margin: 0 -12px -12px -12px;
display: flex;
justify-content: space-around;
}
.pt2 {
padding-top: 2px;
.nav-menu-text {
position: absolute;
top: 13px;
left: 20px;
font-size: 12px;
background: #ff0000;
color: #fff;
border-radius: 5px;
padding: 0 3px;
}
@keyframes twinkle {
@@ -263,15 +223,4 @@ const temporaryLandingEvent = () => {
transform: scale(1);
}
}
.nav-menu-text {
position: absolute;
top: 13px;
left: 20px;
font-size: 12px;
display: inline-block;
background-color: #ff0000;
color: #fff;
border-radius: 5px;
padding: 0 3px;
}
</style>

View File

@@ -4,8 +4,10 @@ import router from './router'
import pinia from '@/stores/index'
import { registerIcons } from '@/utils/common'
import mitt from 'mitt'
import VxeUI from 'vxe-pc-ui'
import VXETable from 'vxe-table'
import XEUtils from 'xe-utils'
import 'vxe-pc-ui/lib/style.css'
import 'vxe-table/lib/style.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
@@ -61,6 +63,7 @@ const setupAll = async () => {
await setupI18n(app)
app.use(router)
app.use(pinia)
app.use(VxeUI)
app.use(ElementPlus)
app.use(VXETable)

View File

@@ -1,55 +1,22 @@
import type { App } from 'vue'
// 👇使用 form-create 需额外全局引入 element plus 组件
import {
ElAlert,
ElAside,
ElContainer,
ElDivider,
ElHeader,
ElMain,
ElPopconfirm,
ElTable,
ElTableColumn,
ElTabPane,
ElTabs,
ElTransfer
} from 'element-plus'
import FcDesigner from '@form-create/designer'
import formCreate from '@form-create/element-ui'
import install from '@form-create/element-ui/auto-import'
//======================= 自定义组件 =======================
import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile'
import { DictSelect } from '@/components/DictSelect'
import UserSelect from '@/views/system/auth/userList/UserSelect.vue'
import { Editor } from '@/components/Editor'
const components = [
ElAside,
ElPopconfirm,
ElHeader,
ElMain,
ElContainer,
ElDivider,
ElTransfer,
ElAlert,
ElTabs,
ElTable,
ElTableColumn,
ElTabPane,
UploadImg,
UploadImgs,
UploadFile,
DictSelect,
UserSelect,
Editor
]
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
export const setupFormCreate = (app: App<Element>) => {
components.forEach((component) => {
app.component(component.name, component)
})
formCreate.use(install)
app.use(formCreate)
app.use(FcDesigner)
}
import type { App } from 'vue'
import FcDesigner from '@form-create/designer'
import formCreate from '@form-create/element-ui'
import install from '@form-create/element-ui/auto-import'
//======================= 自定义组件 =======================
import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile'
import { DictSelect } from '@/components/DictSelect'
import UserSelect from '@/views/system/auth/userList/UserSelect.vue'
import { Editor } from '@/components/Editor'
// Element Plus 已在 main.ts 通过 app.use(ElementPlus) 全局注册,此处仅注册 form-create 自定义组件
const components = [UploadImg, UploadImgs, UploadFile, DictSelect, UserSelect, Editor]
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
export const setupFormCreate = (app: App<Element>) => {
components.forEach((component) => {
app.component(component.name!, component)
})
formCreate.use(install)
app.use(formCreate)
app.use(FcDesigner)
}

View File

@@ -31,6 +31,8 @@ $border-color: map.merge(
@include set-component-css-var('border-color', $border-color);
--vxe-ui-table-row-current-background-color: var(--el-color-primary-light-8);
--vxe-ui-table-row-hover-current-background-color: var(--el-color-primary-light-8);
--vxe-ui-font-primary-color: var(--el-color-primary);
--vxe-ui-font-primary-lighten-color: var(--el-color-primary);
// --vxe-table-row-current-background-color: var(--el-color-primary-light-7);
// --vxe-table-row-hover-background-color: var(--el-color-primary-light-9);
// --vxe-table-row-hover-current-background-color: var(--el-color-primary-light-7);

View File

@@ -70,7 +70,8 @@
// .vxe-table--render-default .is--disabled.vxe-cell--checkbox .vxe-checkbox--icon{
// color: #fff0;
// }
.vxe-table--tooltip-wrapper {
.vxe-table--tooltip-wrapper,
.vxe-tooltip--wrapper {
z-index: 10000 !important;
}
.is--disabled {
@@ -98,3 +99,8 @@
background-color: #000 !important;
}
}
.vxe-table-custom--panel-list > li {
max-width: 28em;
min-width: 15em;
padding: 0.3em 1em 0.2em 1em;
}

View File

@@ -2,7 +2,7 @@
<div class='default-main'>
<!-- 表头 -->
<!-- date-picker 时间组件 area区域组件-->
<TableHeader date-picker area>
<TableHeader date-picker area showCustomColumn>
<template v-slot:select>
<el-form-item label='关键词:'>
<el-input

View File

@@ -0,0 +1,59 @@
const AXIS_DECIMALS = 2
export function roundAxisValue(val: number, decimals = AXIS_DECIMALS): number {
if (!Number.isFinite(val)) return 0
const factor = 10 ** decimals
return Math.round(val * factor) / factor
}
/** Y 轴刻度:最多 2 位小数,末尾 0 去掉(如 1.00 → 10.10 → 0.1 */
export function formatAxisLabel(value: number): string {
if (!Number.isFinite(value)) return '0'
return String(Number(roundAxisValue(value).toFixed(AXIS_DECIMALS)))
}
/** 瞬间波形 Y 轴范围 */
export function calcShuYAxisRange(dataMin: number, dataMax: number): { min: number; max: number } {
const min = Number(dataMin)
const max = Number(dataMax)
if (!Number.isFinite(min) || !Number.isFinite(max)) {
return { min: 0, max: 1 }
}
let axisMax = max * 1.1
let axisMin = min > 0 ? min - min * 0.1 : min * 1.1
if (axisMax <= axisMin) {
const pad = Math.abs(max) * 0.1 || 0.01
axisMax = max + pad
axisMin = min - pad
}
return {
min: roundAxisValue(axisMin),
max: roundAxisValue(axisMax)
}
}
/** RMS 波形 Y 轴范围 */
export function calcRmsYAxisRange(dataMin: number, dataMax: number): { min: number; max: number } {
const min = Number(dataMin)
const max = Number(dataMax)
if (!Number.isFinite(min) || !Number.isFinite(max)) {
return { min: 0, max: 1 }
}
let axisMax = max * 1.06 * 1.1
let axisMin = min - min * 0.2
if (axisMax <= axisMin) {
const pad = Math.abs(max - min) * 0.1 || Math.abs(max) * 0.1 || 0.01
axisMax = max + pad
axisMin = min - pad
}
return {
min: roundAxisValue(axisMin),
max: roundAxisValue(axisMax)
}
}

View File

@@ -53,82 +53,23 @@ const calculateValue = (o: number, value: number, num: number, isMin: boolean) =
export const yMethod = (arr: any) => {
let num = 0.2
let numList = dataProcessing(arr)
let maxValue = 0
let minValue = 0
let max = 0
if (numList.length === 0) {
return [0, 0]
}
const maxValue = Math.max(...numList)
const minValue = Math.min(...numList)
let min = 0
maxValue = Math.max(...numList)
minValue = Math.min(...numList)
const o = maxValue - minValue == 0 ? maxValue : maxValue - minValue
min = calculateValue(o, minValue, num, true)
let max = 0
max = calculateValue(o, maxValue, num, false)
// if (-100 >= minValue) {
// min = Math.floor((minValue + num * minValue) / 100) * 100
// } else if (-10 >= minValue && minValue > -100) {
// min = Math.floor((minValue + num * minValue) / 10) * 10
// } else if (-1 >= minValue && minValue > -10) {
// min = Math.floor(minValue + num * minValue)
// } else if (0 > minValue && minValue > -1) {
// min = parseFloat((minValue + num * minValue).toFixed(1))
// } else if (minValue == 0) {
// min = 0
// } else if (0 < minValue && minValue < 1) {
// min = parseFloat((minValue - num * minValue).toFixed(1))
// } else if (1 <= minValue && minValue < 10) {
// min = Math.floor(minValue - num * minValue)
// } else if (10 <= minValue && minValue < 100) {
// min = Math.floor((minValue - num * minValue) / 10) * 10
// } else if (100 <= minValue) {
// min = Math.floor((minValue - num * minValue) / 100) * 100
// }
// if (-100 >= maxValue) {
// max = Math.ceil((maxValue - num * maxValue) / 100) * 100
// } else if (-10 >= maxValue && maxValue > -100) {
// max = Math.ceil((maxValue - num * maxValue) / 10) * 10
// } else if (-1 >= maxValue && maxValue > -10) {
// max = Math.ceil(maxValue - num * maxValue)
// } else if (0 > maxValue && maxValue > -1) {
// max = parseFloat((maxValue - num * maxValue).toFixed(1))
// } else if (maxValue == 0) {
// max = 0
// } else if (0 < maxValue && maxValue < 1) {
// max = parseFloat((maxValue + num * maxValue).toFixed(1))
// } else if (1 <= maxValue && maxValue < 10) {
// max = Math.ceil(maxValue + num * maxValue)
// } else if (10 <= maxValue && maxValue < 100) {
// max = Math.ceil((maxValue + num * maxValue) / 10) * 10
// } else if (100 <= maxValue) {
// max = Math.ceil((maxValue + num * maxValue) / 100) * 100
// }
// if (maxValue > 1000 || minValue < -1000) {
// max = Math.ceil(maxValue / 100) * 100
// if (minValue == 0) {
// min = 0
// } else {
// min = Math.floor(minValue / 100) * 100
// }
// } else if (maxValue < 60 && minValue > 40) {
// max = 60
// min = 40
// } else if (maxValue == minValue && maxValue < 10 && minValue > 0) {
// max = Math.ceil(maxValue / 10) * 10
// min = Math.floor(minValue / 10) * 10
// } else if (maxValue == minValue && maxValue != 0 && minValue != 0) {
// max = Math.ceil(maxValue / 10 + 1) * 10
// min = Math.floor(minValue / 10 - 1) * 10
// } else {
// max = Math.ceil(maxValue / 10) * 10
// min = Math.floor(minValue / 10) * 10
// }
// if (maxValue > 0 && maxValue < 1) {
// max = 1
// } else if (max == 0 && minValue > -1 && minValue < 0) {
// min = -1
// }
// 一正一负时各自按绝对值范围计算,不用 max - min 作为跨度
if (maxValue > 0 && minValue < 0) {
min = calculateValue(Math.abs(minValue), minValue, num, true)
max = calculateValue(Math.abs(maxValue), maxValue, num, false)
} else {
const o = maxValue - minValue === 0 ? maxValue : maxValue - minValue
min = calculateValue(o, minValue, num, true)
max = calculateValue(o, maxValue, num, false)
}
return [min, max]
}

View File

@@ -1,4 +1,5 @@
import { downloadFile } from '@/api/system-boot/file'
import { ElMessage } from 'element-plus'
const sanitizeUrl = (url: string): string => {
return url.replace(/\[/g, '(').replace(/\]/g, ')')
@@ -7,7 +8,7 @@ const sanitizeUrl = (url: string): string => {
// 下载文件
export const download = (urls: string) => {
//console.log('下载', urls)
ElMessage.info('下载中...')
downloadFile({ filePath: urls })
.then((res: any) => {
// 1. 确定文件MIME类型优化用更简洁的方式
@@ -26,9 +27,9 @@ export const download = (urls: string) => {
}
return mimeMap[ext] || ''
}
const blob = new Blob([res], { type: getFileType(urls) })
// 2. 提取文件名并保留原生后缀(核心修复点)
const fileName = urls.split('/').at(-1) || '下载文件' // 先提取URL最后一段文件名
@@ -43,8 +44,12 @@ export const download = (urls: string) => {
// 4. 清理资源(优化)
link.remove()
window.URL.revokeObjectURL(url) // 释放blob URL
setTimeout(() => {
ElMessage.success('下载成功')
}, 1000)
})
.catch(err => {
ElMessage.warning('下载失败:', err)
console.error('下载失败:', err)
// 可添加错误提示如Toast
})
@@ -61,7 +66,7 @@ export const previewFile = async (urls: any) => {
//console.log('预览', urls)
let url = ''
await downloadFile({ filePath: decodeURI(urls) })
.then((res: any) => {
.then((res: any) => {
// 1. 确定文件MIME类型优化用更简洁的方式
const getFileType = (url: string) => {
const ext = url.split('.').pop()?.toLowerCase() || ''
@@ -77,7 +82,7 @@ export const previewFile = async (urls: any) => {
jpg: 'image/jpg'
}
return mimeMap[ext] || ''
}
}
const blob = new Blob([res], { type: getFileType(decodeURI(urls)) })
// 3. 创建下载链接
url = window.URL.createObjectURL(blob)

View File

@@ -1,10 +1,11 @@
import type { AxiosRequestConfig, Method } from 'axios'
import axios from 'axios'
import { ElLoading, ElNotification, type LoadingOptions } from 'element-plus'
import { refreshToken } from '@/api/user-boot/user'
import { lnRefreshToken, lnRefreshTokenTo, refreshToken } from '@/api/user-boot/user'
import router from '@/router/index'
import { useAdminInfo } from '@/stores/adminInfo'
import { debounce } from 'lodash-es'
const IS_LN_VERSION = import.meta.env.VITE_NAME === 'LNqr'
let loginExpireTimer: any = null
window.requests = []
window.tokenRefreshing = false
@@ -110,7 +111,8 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
if (
config.url == '/user-boot/user/generateSm2Key' ||
config.url == '/pqs-auth/oauth/token' ||
config.url == '/LNapi/pqs-auth/oauth/lnLogin'
config.url == '/pqs-auth/oauth/lnLogin' ||
config.url == '/pqs-auth/oauth/lnRefreshToken'
) {
config.headers.Authorization = 'Basic bmpjbjpuamNucHFz'
}
@@ -146,24 +148,46 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
if (!window.tokenRefreshing) {
window.tokenRefreshing = true
return refreshToken()
.then(res => {
adminInfo.setToken(res.data.access_token, 'auth')
adminInfo.setToken(res.data.refresh_token, 'refresh')
window.requests.forEach(cb => cb(res.data.access_token))
window.requests = []
if (IS_LN_VERSION) {
//辽宁版本
return lnRefreshTokenTo()
.then(res => {
adminInfo.setToken(res.data.access_token, 'auth')
adminInfo.setToken(res.data.refresh_token, 'refresh')
window.requests.forEach(cb => cb(res.data.access_token))
window.requests = []
return Axios(response.config)
})
.catch(err => {
window.location.reload()
adminInfo.removeToken()
router.push({ name: 'login' })
return Promise.reject(err)
})
.finally(() => {
window.tokenRefreshing = false
})
return Axios(response.config)
})
.catch(err => {
// refresh_token + CAS 都过期 → 重新走CAS登录
//window.location.href = '/api/pqs-auth/oauth/lnCheck'
return Promise.reject(err)
})
.finally(() => {
window.tokenRefreshing = false
})
} else {
//通用版本
return refreshToken()
.then(res => {
adminInfo.setToken(res.data.access_token, 'auth')
adminInfo.setToken(res.data.refresh_token, 'refresh')
window.requests.forEach(cb => cb(res.data.access_token))
window.requests = []
return Axios(response.config)
})
.catch(err => {
window.location.reload()
adminInfo.removeToken()
router.push({ name: 'login' })
return Promise.reject(err)
})
.finally(() => {
window.tokenRefreshing = false
})
}
} else {
return new Promise(resolve => {
// 用函数形式将 resolve 存入,等待刷新后再执行
@@ -189,6 +213,17 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
loginExpireTimer = null // 执行后清空定时器
}, 100) // 可根据实际情况调整延迟时间
return Promise.reject(response.data)
} else if (response.data.code == 'A0121') {
//统一认证过期
//const casLogoutUrl = 'http://privilege-epri.dcloud.ln.dc.sgcc.com.cn/cas/login?service=http://PQMonitoring.dcloud.ln.dc.sgcc.com.cn'
// 1. 清空本地失效token、用户信息
adminInfo.removeToken()
adminInfo.reset()
// 2. 跳转统一鉴权中转接口
const authCheckUrl = '/api/pqs-auth/oauth/lnCheck'
window.location.href = authCheckUrl
// 中断请求
return Promise.reject(response.data)
} else {
if (options.showCodeMessage) {
ElNotification({

View File

@@ -10,7 +10,6 @@ import { compact, isEmpty, reverse } from 'lodash-es'
import { getRouteMenu, dictDataCache } from '@/api/auth'
import { isAdminApp } from '@/utils/common'
import { adminBaseRoutePath } from '@/router/static'
const route:any = useRouter()
/**
* 导航失败有错误消息的路由push
* @param to — 导航位置,同 router.push
@@ -319,8 +318,9 @@ export const getMenu = () => {
}
handlerMenu(res.data)
handleAdminRoute(res.data)
if (route?.params.to) {
const lastRoute = JSON.parse(route.params.to as string)
const currentRoute = router.currentRoute.value
if (currentRoute?.params.to) {
const lastRoute = JSON.parse(currentRoute.params.to as string)
if (lastRoute.path != adminBaseRoutePath) {
let query = !isEmpty(lastRoute.query) ? lastRoute.query : {}
routePush({ path: lastRoute.path, query: query })

View File

@@ -48,6 +48,7 @@ export default class TableStore {
filename: null,
loading: true,
exportLoading: false,
customColumnInHeader: false,
column: [],
loadCallback: null,
exportProcessingData: null,

50
src/utils/waveCache.ts Normal file
View File

@@ -0,0 +1,50 @@
import { getWaveRawList } from './waveRawStore'
const MAX_CACHE_SIZE = 6
const cache = new Map<string, unknown>()
function dataFingerprint(data: unknown[][] | undefined): string {
if (!data?.length) return '0'
const first = data[0]
const last = data[data.length - 1]
return `${data.length}:${first?.[0]}:${last?.[0]}`
}
export function buildWaveCacheKey(
type: 'shu' | 'rms',
wp: Record<string, any> | undefined,
value: number,
isOpen: boolean,
boxoList: Record<string, any>
): string {
if (!wp) return ''
const rawList = wp.waveStoreKey
? getWaveRawList(type, wp.waveStoreKey)
: ((type === 'shu' ? wp.listWaveData : wp.listRmsData) as unknown[][] | undefined)
const waveFp = dataFingerprint(rawList)
const boxoFp = boxoList?.startTime ?? boxoList?.lineName ?? boxoList?.equipmentName ?? ''
return `${type}|${wp.time}|${wp.waveType}|${wp.iphasic}|${value}|${isOpen}|${waveFp}|${boxoFp}`
}
export function getWaveCache<T>(key: string): T | null {
if (!key || !cache.has(key)) return null
const value = cache.get(key) as T
cache.delete(key)
cache.set(key, value)
return value
}
export function setWaveCache(key: string, value: unknown): void {
if (!key) return
if (cache.has(key)) cache.delete(key)
cache.set(key, value)
while (cache.size > MAX_CACHE_SIZE) {
const oldest = cache.keys().next().value
if (oldest !== undefined) cache.delete(oldest)
}
}
export function clearWaveCache(): void {
cache.clear()
}

View File

@@ -0,0 +1,59 @@
const EMPTY_SERIES: number[][] = []
function ds(data: number[][] | undefined): number[][] {
if (!data?.length) return EMPTY_SERIES
return data
}
/** Worker 完整计算后,剔除当前未使用的值类型,降低内存占用 */
export function compactShuWaveResult(data: any, value: number) {
const waveDatas = data.waveDatas.map((wd: any) => {
if (value === 1) {
return {
...wd,
shunshiF: {
shunshiFA: ds(wd.shunshiF.shunshiFA),
shunshiFB: ds(wd.shunshiF.shunshiFB),
shunshiFC: ds(wd.shunshiF.shunshiFC)
},
shunshiS: { shunshiSA: EMPTY_SERIES, shunshiSB: EMPTY_SERIES, shunshiSC: EMPTY_SERIES }
}
}
return {
...wd,
shunshiF: { shunshiFA: EMPTY_SERIES, shunshiFB: EMPTY_SERIES, shunshiFC: EMPTY_SERIES },
shunshiS: {
shunshiSA: ds(wd.shunshiS.shunshiSA),
shunshiSB: ds(wd.shunshiS.shunshiSB),
shunshiSC: ds(wd.shunshiS.shunshiSC)
}
}
})
return { ...data, waveDatas }
}
export function compactRmsWaveResult(data: any, value: number) {
const waveDatas = data.waveDatas.map((wd: any) => {
if (value === 1) {
return {
...wd,
RMSFWave: {
rmsFA: ds(wd.RMSFWave.rmsFA),
rmsFB: ds(wd.RMSFWave.rmsFB),
rmsFC: ds(wd.RMSFWave.rmsFC)
},
RMSSWave: { rmsSA: EMPTY_SERIES, rmsSB: EMPTY_SERIES, rmsSC: EMPTY_SERIES }
}
}
return {
...wd,
RMSFWave: { rmsFA: EMPTY_SERIES, rmsFB: EMPTY_SERIES, rmsFC: EMPTY_SERIES },
RMSSWave: {
rmsSA: ds(wd.RMSSWave.rmsSA),
rmsSB: ds(wd.RMSSWave.rmsSB),
rmsSC: ds(wd.RMSSWave.rmsSC)
}
}
})
return { ...data, waveDatas }
}

75
src/utils/waveRawStore.ts Normal file
View File

@@ -0,0 +1,75 @@
/** 原始波形数据存放于 Vue 响应式体系之外,避免大数组触发深度代理与重复持有 */
interface WaveRawEntry {
listWaveData?: unknown[][]
listRmsData?: unknown[][]
waveTimeRange?: { min: number; max: number }
}
const rawStore = new Map<string, WaveRawEntry>()
function calcTimeRange(data: unknown[][] | undefined): { min: number; max: number } | undefined {
if (!data?.length) return undefined
const first = data[0] as number[]
const last = data[data.length - 1] as number[]
return { min: first[0], max: last[0] + 1 }
}
export function buildWaveStoreKey(boxoList: Record<string, any>): string {
const id = boxoList?.eventId ?? boxoList?.id ?? ''
return `${id}|${boxoList?.startTime ?? ''}`
}
export function registerWaveRaw(storeKey: string, wp: Record<string, any>): void {
if (!storeKey || !wp) return
const entry: WaveRawEntry = rawStore.get(storeKey) ?? {}
if (wp.listWaveData?.length) {
entry.listWaveData = wp.listWaveData
entry.waveTimeRange = calcTimeRange(wp.listWaveData) ?? entry.waveTimeRange
}
if (wp.listRmsData?.length) {
entry.listRmsData = wp.listRmsData
entry.waveTimeRange = calcTimeRange(wp.listRmsData) ?? entry.waveTimeRange
}
rawStore.set(storeKey, entry)
}
export function getWaveRawList(type: 'shu' | 'rms', storeKey: string | undefined): unknown[][] | undefined {
if (!storeKey) return undefined
const entry = rawStore.get(storeKey)
return type === 'shu' ? entry?.listWaveData : entry?.listRmsData
}
export function getWaveTimeRange(storeKey: string | undefined): { min: number; max: number } | undefined {
if (!storeKey) return undefined
return rawStore.get(storeKey)?.waveTimeRange
}
export function releaseWaveRaw(storeKey: string | undefined, type: 'shu' | 'rms' | 'all' = 'all'): void {
if (!storeKey) return
if (type === 'all') {
rawStore.delete(storeKey)
return
}
const entry = rawStore.get(storeKey)
if (!entry) return
if (type === 'shu') delete entry.listWaveData
if (type === 'rms') delete entry.listRmsData
if (!entry.listWaveData && !entry.listRmsData) {
rawStore.delete(storeKey)
} else {
rawStore.set(storeKey, entry)
}
}
/** 构建不含大数组的 wp 对象,供子组件 props 使用 */
export function buildSlimWp(wp: Record<string, any>, storeKey: string): Record<string, any> {
const { listWaveData: _w, listRmsData: _r, ...rest } = wp
return {
...rest,
waveStoreKey: storeKey,
waveTimeRange: getWaveTimeRange(storeKey) ?? calcTimeRange(_w) ?? calcTimeRange(_r)
}
}

View File

@@ -0,0 +1,98 @@
import { toRaw } from 'vue'
import { getWaveRawList } from './waveRawStore'
type WorkerMessageHandler = (data: any) => void
let shuWorker: Worker | null = null
let rmsWorker: Worker | null = null
/** 递归剥离 Vue 响应式代理,得到可 structuredClone 的纯对象 */
export function toPlainDeep<T>(value: T): T {
const raw = toRaw(value as object) as T
if (Array.isArray(raw)) {
return raw.map(item => toPlainDeep(item)) as T
}
if (raw !== null && typeof raw === 'object') {
const out: Record<string, unknown> = {}
for (const [key, val] of Object.entries(raw)) {
out[key] = toPlainDeep(val)
}
return out as T
}
return raw
}
const BOXO_LIST_KEYS = [
'systemType',
'powerStationName',
'measurementPointName',
'startTime',
'featureAmplitude',
'duration',
'engineeringName',
'equipmentName',
'evtParamVVaDepth',
'evtParamTm',
'lineName',
'persistTime',
'subName'
] as const
export function buildWorkerPayload(
type: 'shu' | 'rms',
wp: Record<string, any>,
boxoList: Record<string, any>,
extras: { requestId: number; value: number; isOpen: boolean; iphasic?: number }
) {
const plainWp = toPlainDeep(wp)
const wpPayload: Record<string, unknown> = {
pt: plainWp.pt,
ct: plainWp.ct,
waveTitle: plainWp.waveTitle,
iphasic: plainWp.iphasic,
time: plainWp.time,
waveType: plainWp.waveType,
yzd: plainWp.yzd
}
const storeKey = plainWp.waveStoreKey as string | undefined
if (type === 'shu') {
wpPayload.listWaveData = getWaveRawList('shu', storeKey) ?? plainWp.listWaveData
} else {
wpPayload.listRmsData = getWaveRawList('rms', storeKey) ?? plainWp.listRmsData
}
const plainBoxo: Record<string, unknown> = {}
const rawBoxo = toPlainDeep(boxoList)
for (const key of BOXO_LIST_KEYS) {
if (rawBoxo[key] !== undefined) {
plainBoxo[key] = rawBoxo[key]
}
}
return {
requestId: extras.requestId,
value: extras.value,
isOpen: extras.isOpen,
iphasic: extras.iphasic,
wp: wpPayload,
boxoList: plainBoxo
}
}
export function getShuWorker(onMessage: WorkerMessageHandler): Worker {
if (!shuWorker) {
shuWorker = new Worker(new URL('../components/echarts/shuWorker.js', import.meta.url))
}
shuWorker.onmessage = e => onMessage(e.data)
shuWorker.onerror = error => console.error('Shu worker error:', error)
return shuWorker
}
export function getRmsWorker(onMessage: WorkerMessageHandler): Worker {
if (!rmsWorker) {
rmsWorker = new Worker(new URL('../components/echarts/rmsWorker.js', import.meta.url))
}
rmsWorker.onmessage = e => onMessage(e.data)
rmsWorker.onerror = error => console.error('Rms worker error:', error)
return rmsWorker
}

View File

@@ -58,7 +58,7 @@ export default class SocketService {
}
// 处理连接成功事件
private handleOpen(): void {
ElMessage.success('webSocket连接服务端成功了')
// ElMessage.success('webSocket连接服务端成功了')
console.log('连接服务端成功了')
this.connected = true
this.connectRetryCount = 0
@@ -110,6 +110,7 @@ export default class SocketService {
}
} else {
this.callBackMapping['message']!({ Flag: false })
if(event.data == 'connect')return
ElMessage.error(event.data)
}
}

View File

@@ -1,423 +1,423 @@
<template>
<div class="default-main">
<TableHeader datePicker theCurrentTime area ref="header">
<!-- <template v-slot:select>
</template> -->
</TableHeader>
<div v-loading="tableStore.table.loading">
<el-row :gutter="10" class="pd10">
<el-col :span="11" style="position: relative">
<el-card>
<el-radio-group
v-model="tableStore.table.params.type"
class="group"
@change="tableStore.index()"
>
<el-radio-button label="风电场" value="1" />
<el-radio-button label="光伏电站" value="2" />
</el-radio-group>
<MyEchartMap
ref="EchartMap"
:options="echartMapList"
class="map"
@eliminate="eliminate"
@getRegionByRegion="getRegionByRegion"
@clickMap="clickMap"
/>
</el-card>
</el-col>
<el-col :span="13">
<el-card>
<template #header>
<div class="card-header">
<span>暂降列表</span>
</div>
</template>
<!-- <h3 class="mb10">暂降列表</h3> -->
<div class="tall1">
<vxe-table
height="auto"
auto-resize
:data="distributionData"
v-loading="loading"
v-bind="defaultAttribute"
>
<vxe-column
field="newStationName"
title="新能源站名称"
min-width="150px"
show-overflow-tooltip
></vxe-column>
<vxe-column field="startTime" title="暂降发生时刻" min-width="150px"></vxe-column>
<vxe-column
field="featureAmplitude"
title="暂降(骤升)幅值(%)"
sortable
min-width="150px"
></vxe-column>
<vxe-column
field="advanceReason"
title="暂降原因"
sortable
:formatter="formFilter"
min-width="100px"
></vxe-column>
<vxe-column field="severity" title="严重度" min-width="100px" sortable></vxe-column>
<vxe-column title="操作" width="80px">
<template #default="{ row }">
<el-button
type="primary"
v-if="row.wavePath != null"
size="small"
link
@click="boxi(row)"
>
查看波形
</el-button>
<el-button v-else disabled size="small" link>暂无波形</el-button>
</template>
</vxe-column>
</vxe-table>
</div>
</el-card>
<el-card class="mt10">
<template #header>
<div class="card-header">
<span>暂降波形</span>
</div>
</template>
<div class="tall" v-loading="loading1">
<!-- <h3 class="mb10">暂降波形</h3> -->
<div v-if="wp != null">
<rmsboxi :value="1" :height="height" :boxoList="boxoList" :wp="wp" />
</div>
<el-empty v-else description="暂无数据" class="custom-empty" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup lang="ts">
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import MyEchartMap from '@/components/echarts/MyEchartMap.vue'
import TableStore from '@/utils/tableStore'
import { voltageRideThroughEventQueryPage } from '@/api/event-boot/highAndLowPressure'
import { getMonitorEventAnalyseWave, getTransientDetailById } from '@/api/event-boot/transient'
import rmsboxi from '@/components/echarts/rmsboxi.vue'
import { ref, onMounted, provide } from 'vue'
import { mainHeight } from '@/utils/layout'
import { FormItem } from 'vxe-table'
defineOptions({
name: 'newEnergy/highAndLowPressure'
})
const EchartMap = ref()
const dictData = useDictData()
const reason = dictData.getBasicData('Event_Reason')
const triggerType = dictData.getBasicData('Event_Statis')
const echartMapList: any = ref({})
const header = ref()
const distributionData: any = ref([])
const loading = ref(false)
const loading1 = ref(false)
const boxoList = ref(null)
const wp = ref(null)
const tableStore = new TableStore({
url: '/event-boot/voltageRideThrough/view',
method: 'POST',
column: [],
beforeSearchFun: () => {
tableStore.table.params.areaId = tableStore.table.params.deptIndex
},
loadCallback: () => {
// 处理地图数据
map(tableStore.table.data)
// tabulation(tableStore.table.data)
EchartMap.value.GetEchar(header.value.areaRef.areaName)
if (tableStore.table.data.length > 0) {
tabulation({ data: tableStore.table.data[0], seriesName: '高压' })
}
}
})
const height = mainHeight(200, 2).height
provide('tableStore', tableStore)
tableStore.table.params.type = '1'
// 地图点击事件
const getRegionByRegion = (list: any) => {
tableStore.table.params.deptIndex = list.id
tableStore.onTableAction('search', {})
}
// 消除点
const eliminate = (name: string) => {
echartMapList.value.options.series = []
EchartMap.value.GetEchar(name)
}
// 地图数处理
const map = (res: any) => {
echartMapList.value = {
title: {
text: '高/低电压穿越'
},
tooltip: {
trigger: 'item',
formatter: function (params) {
if (params.seriesType == 'bar3D') {
return [params.seriesName, params.name + '' + params.value[2] + (params.value[3] || '')].join(
'<br />'
)
}
}
},
legend: {
selectedMode: false
},
geo3D: {
show: true,
// name: '浙江',
itemStyle: {
color: getComputedStyle(document.documentElement).getPropertyValue('--el-color-primary-light-3'),
borderWidth: 1,
borderColor: '#fff'
},
emphasis: {
label: { show: true, fontSize: 16 },
itemStyle: {
color: getComputedStyle(document.documentElement).getPropertyValue('--el-color-primary-light-7')
}
},
viewControl: {
alpha: 60,
distance: 120,
panMouseButton: 'right', //平移操作使用的鼠标按键
rotateMouseButton: 'left' //旋转操作使用的鼠标按键
},
label: {
show: true,
color: '#000',
fontSize: 14,
distance: 0,
textStyle: {
color: '#000',
backgroundColor: '#000'
}
},
data: [
{ name: '低压', field: 'lowPressure', unit: '次' },
{ name: '高压', field: 'highPressure', unit: '次' }
]
},
options: {
series: [
{
name: '低压',
type: 'bar3D',
coordinateSystem: 'geo3D',
shading: 'lambert',
label: {
show: true,
position: 'top',
formatter: params => {
return params.value[2]
}
},
data:
res.length == 0
? []
: res.map((item: any) => {
return {
...item,
value: [item.lng, item.lat, item.lowPressure || 0]
}
}),
// data:[],
barSize: 1,
minHeight: 1,
itemStyle: {
color: '#77DA63'
},
emphasis: {
label: { show: true }
}
},
{
name: '高压',
type: 'bar3D',
coordinateSystem: 'geo3D',
shading: 'lambert',
label: {
show: true,
// position: 'top',
formatter: params => {
return params.value[2]
}
},
data:
res.length == 0
? []
: res.map((item: any) => {
return {
...item,
value: [item.lng - 0.1, item.lat, item.highPressure || 0]
}
}),
// [
// {
// adcode: 330400,
// name: '大连',
// wcs: 10,
// mbs: 40,
// wcl: 100,
// value: [121.67391, 38.947468, 40, '']
// }
// ],
barSize: 1,
minHeight: 1,
itemStyle: {
color: '#FFBF00'
},
emphasis: {
label: { show: true }
}
}
]
}
}
}
// 点击地图
const clickMap = (e: any) => {
tabulation(e)
}
// 表格数据处理
const tabulation = (e: any) => {
console.log()
loading.value = true
voltageRideThroughEventQueryPage({
...tableStore.table.params,
areaId: e.data.id,
frequencyType:
e.seriesName == '高压'
? triggerType.filter(item => item.code == 'Voltage_Rise')[0].id
: triggerType.filter(item => item.code == 'Voltage_Dip')[0].id
})
.then(res => {
distributionData.value = res.data
for (let i = 0; i < res.data.length; i++) {
if (res.data[i].wavePath != null) {
boxi(res.data[i])
break
}
}
// res.data.forEach((item: any) => {
// if (item.wavePath != null) {
// boxi(item)
// return
// }
// })
// 。wavePath
loading.value = false
})
.catch(() => {
loading.value = false
})
}
const formFilter = (row: any) => {
if (row.column.property == 'advanceReason') {
let title = ''
reason.forEach(item => {
if (item.id == row.row.advanceReason) {
title = item.name
} else if (row.row.advanceReason == null || row.row.advanceReason == '') {
title = '/'
}
})
return title
} else {
return row.row[row.column.property]
}
}
// 查看波形数据
const boxi = async (row: any) => {
loading1.value = true
wp.value = null
await getTransientDetailById({
eventId: row.eventId,
sysType: 0,
smallType: 0
}).then(res => {
boxoList.value = res.data
})
await getMonitorEventAnalyseWave({
id: row.eventId,
systemType: 0,
type: 0
}).then(res => {
if (res != undefined) {
wp.value = res.data
}
loading1.value = false
})
}
onMounted(() => {
tableStore.index()
})
const layout = mainHeight(83) as any
const layout1 = mainHeight(93) as any
</script>
<style lang="scss" scoped>
.map {
height: calc(v-bind('layout1.height') - 30px);
}
.tall {
height: calc((v-bind('layout1.height') - 100px) / 2);
}
.tall1 {
height: calc((v-bind('layout1.height') - 100px) / 2 - 50px);
}
.group {
position: absolute;
top: 15px;
right: 20px;
z-index: 10;
}
:deep(.el-card__body) {
padding: 10px;
}
/* 自定义 el-empty 的样式 */
:deep(.custom-empty) {
display: flex;
justify-content: center;
align-items: center;
height: 100%; /* 调整高度 */
padding: 20px; /* 调整内边距 */
.el-empty__image {
display: none; /* 隐藏默认图片 */
}
.el-empty__description {
font-size: 14px; /* 调整字体大小 */
color: var(--vxe-font-color);
}
}
</style>
<template>
<div class="default-main">
<TableHeader datePicker theCurrentTime area ref="header">
<!-- <template v-slot:select>
</template> -->
</TableHeader>
<div v-loading="tableStore.table.loading">
<el-row :gutter="10" class="pd10">
<el-col :span="11" style="position: relative">
<el-card>
<el-radio-group
v-model="tableStore.table.params.type"
class="group"
@change="tableStore.index()"
>
<el-radio-button label="风电场" value="1" />
<el-radio-button label="光伏电站" value="2" />
</el-radio-group>
<MyEchartMap
ref="EchartMap"
:options="echartMapList"
class="map"
@eliminate="eliminate"
@getRegionByRegion="getRegionByRegion"
@clickMap="clickMap"
/>
</el-card>
</el-col>
<el-col :span="13">
<el-card>
<template #header>
<div class="card-header">
<span>暂降列表</span>
</div>
</template>
<!-- <h3 class="mb10">暂降列表</h3> -->
<div class="tall1">
<vxe-table
height="auto"
auto-resize
:data="distributionData"
v-loading="loading"
v-bind="defaultAttribute"
>
<vxe-column
field="newStationName"
title="新能源站名称"
min-width="150px"
show-overflow-tooltip
></vxe-column>
<vxe-column field="startTime" title="暂降发生时刻" min-width="180px" sortable></vxe-column>
<vxe-column
field="featureAmplitude"
title="暂降(骤升)幅值(%)"
sortable
min-width="160px"
></vxe-column>
<vxe-column
field="advanceReason"
title="暂降原因"
sortable
:formatter="formFilter"
min-width="100px"
></vxe-column>
<vxe-column field="severity" title="严重度" min-width="100px" sortable></vxe-column>
<vxe-column title="操作" width="80px">
<template #default="{ row }">
<el-button
type="primary"
v-if="row.wavePath != null"
size="small"
link
@click="boxi(row)"
>
查看波形
</el-button>
<el-button v-else disabled size="small" link>暂无波形</el-button>
</template>
</vxe-column>
</vxe-table>
</div>
</el-card>
<el-card class="mt10">
<template #header>
<div class="card-header">
<span>暂降波形</span>
</div>
</template>
<div class="tall" v-loading="loading1">
<!-- <h3 class="mb10">暂降波形</h3> -->
<div v-if="wp != null">
<rmsboxi :value="1" :height="height" :boxoList="boxoList" :wp="wp" />
</div>
<el-empty v-else description="暂无数据" class="custom-empty" />
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup lang="ts">
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import MyEchartMap from '@/components/echarts/MyEchartMap.vue'
import TableStore from '@/utils/tableStore'
import { voltageRideThroughEventQueryPage } from '@/api/event-boot/highAndLowPressure'
import { getMonitorEventAnalyseWave, getTransientDetailById } from '@/api/event-boot/transient'
import rmsboxi from '@/components/echarts/rmsboxi.vue'
import { ref, onMounted, provide } from 'vue'
import { mainHeight } from '@/utils/layout'
import { FormItem } from 'vxe-table'
defineOptions({
name: 'newEnergy/highAndLowPressure'
})
const EchartMap = ref()
const dictData = useDictData()
const reason = dictData.getBasicData('Event_Reason')
const triggerType = dictData.getBasicData('Event_Statis')
const echartMapList: any = ref({})
const header = ref()
const distributionData: any = ref([])
const loading = ref(false)
const loading1 = ref(false)
const boxoList = ref(null)
const wp = ref(null)
const tableStore = new TableStore({
url: '/event-boot/voltageRideThrough/view',
method: 'POST',
column: [],
beforeSearchFun: () => {
tableStore.table.params.areaId = tableStore.table.params.deptIndex
},
loadCallback: () => {
// 处理地图数据
map(tableStore.table.data)
// tabulation(tableStore.table.data)
EchartMap.value.GetEchar(header.value.areaRef.areaName)
if (tableStore.table.data.length > 0) {
tabulation({ data: tableStore.table.data[0], seriesName: '高压' })
}
}
})
const height = mainHeight(200, 2).height
provide('tableStore', tableStore)
tableStore.table.params.type = '1'
// 地图点击事件
const getRegionByRegion = (list: any) => {
tableStore.table.params.deptIndex = list.id
tableStore.onTableAction('search', {})
}
// 消除点
const eliminate = (name: string) => {
echartMapList.value.options.series = []
EchartMap.value.GetEchar(name)
}
// 地图数处理
const map = (res: any) => {
echartMapList.value = {
title: {
text: '高/低电压穿越'
},
tooltip: {
trigger: 'item',
formatter: function (params) {
if (params.seriesType == 'bar3D') {
return [params.seriesName, params.name + '' + params.value[2] + (params.value[3] || '')].join(
'<br />'
)
}
}
},
legend: {
selectedMode: false
},
geo3D: {
show: true,
// name: '浙江',
itemStyle: {
color: getComputedStyle(document.documentElement).getPropertyValue('--el-color-primary-light-3'),
borderWidth: 1,
borderColor: '#fff'
},
emphasis: {
label: { show: true, fontSize: 16 },
itemStyle: {
color: getComputedStyle(document.documentElement).getPropertyValue('--el-color-primary-light-7')
}
},
viewControl: {
alpha: 60,
distance: 120,
panMouseButton: 'right', //平移操作使用的鼠标按键
rotateMouseButton: 'left' //旋转操作使用的鼠标按键
},
label: {
show: true,
color: '#000',
fontSize: 14,
distance: 0,
textStyle: {
color: '#000',
backgroundColor: '#000'
}
},
data: [
{ name: '低压', field: 'lowPressure', unit: '次' },
{ name: '高压', field: 'highPressure', unit: '次' }
]
},
options: {
series: [
{
name: '低压',
type: 'bar3D',
coordinateSystem: 'geo3D',
shading: 'lambert',
label: {
show: true,
position: 'top',
formatter: params => {
return params.value[2]
}
},
data:
res.length == 0
? []
: res.map((item: any) => {
return {
...item,
value: [item.lng, item.lat, item.lowPressure || 0]
}
}),
// data:[],
barSize: 1,
minHeight: 1,
itemStyle: {
color: '#77DA63'
},
emphasis: {
label: { show: true }
}
},
{
name: '高压',
type: 'bar3D',
coordinateSystem: 'geo3D',
shading: 'lambert',
label: {
show: true,
// position: 'top',
formatter: params => {
return params.value[2]
}
},
data:
res.length == 0
? []
: res.map((item: any) => {
return {
...item,
value: [item.lng - 0.1, item.lat, item.highPressure || 0]
}
}),
// [
// {
// adcode: 330400,
// name: '大连',
// wcs: 10,
// mbs: 40,
// wcl: 100,
// value: [121.67391, 38.947468, 40, '']
// }
// ],
barSize: 1,
minHeight: 1,
itemStyle: {
color: '#FFBF00'
},
emphasis: {
label: { show: true }
}
}
]
}
}
}
// 点击地图
const clickMap = (e: any) => {
tabulation(e)
}
// 表格数据处理
const tabulation = (e: any) => {
console.log()
loading.value = true
voltageRideThroughEventQueryPage({
...tableStore.table.params,
areaId: e.data.id,
frequencyType:
e.seriesName == '高压'
? triggerType.filter(item => item.code == 'Voltage_Rise')[0].id
: triggerType.filter(item => item.code == 'Voltage_Dip')[0].id
})
.then(res => {
distributionData.value = res.data
for (let i = 0; i < res.data.length; i++) {
if (res.data[i].wavePath != null) {
boxi(res.data[i])
break
}
}
// res.data.forEach((item: any) => {
// if (item.wavePath != null) {
// boxi(item)
// return
// }
// })
// 。wavePath
loading.value = false
})
.catch(() => {
loading.value = false
})
}
const formFilter = (row: any) => {
if (row.column.property == 'advanceReason') {
let title = ''
reason.forEach(item => {
if (item.id == row.row.advanceReason) {
title = item.name
} else if (row.row.advanceReason == null || row.row.advanceReason == '') {
title = '/'
}
})
return title
} else {
return row.row[row.column.property]
}
}
// 查看波形数据
const boxi = async (row: any) => {
loading1.value = true
wp.value = null
await getTransientDetailById({
eventId: row.eventId,
sysType: 0,
smallType: 0
}).then(res => {
boxoList.value = res.data
})
await getMonitorEventAnalyseWave({
id: row.eventId,
systemType: 0,
type: 0
}).then(res => {
if (res != undefined) {
wp.value = res.data
}
loading1.value = false
})
}
onMounted(() => {
tableStore.index()
})
const layout = mainHeight(83) as any
const layout1 = mainHeight(93) as any
</script>
<style lang="scss" scoped>
.map {
height: calc(v-bind('layout1.height') - 30px);
}
.tall {
height: calc((v-bind('layout1.height') - 100px) / 2);
}
.tall1 {
height: calc((v-bind('layout1.height') - 100px) / 2 - 50px);
}
.group {
position: absolute;
top: 15px;
right: 20px;
z-index: 10;
}
:deep(.el-card__body) {
padding: 10px;
}
/* 自定义 el-empty 的样式 */
:deep(.custom-empty) {
display: flex;
justify-content: center;
align-items: center;
height: 100%; /* 调整高度 */
padding: 20px; /* 调整内边距 */
.el-empty__image {
display: none; /* 隐藏默认图片 */
}
.el-empty__description {
font-size: 14px; /* 调整字体大小 */
color: var(--vxe-font-color);
}
}
</style>

View File

@@ -1,119 +1,119 @@
<template>
<div class="dictiontary-list-detail child-router">
<TableHeader :showSearch="false">
<template #select>
<el-form-item label="">
<span class="text-large font-600 mr-3">{{ props.timePopUpBox.time }}</span>
</el-form-item>
<el-form-item>
<el-radio-group v-model="radio2" @change="tableStore.index()">
<el-radio-button label="基础数据" value="1" />
<el-radio-button label="谐波电压" value="2" />
<el-radio-button label="谐波电流" value="3" />
<el-radio-button label="间谐波电压" value="4" />
</el-radio-group>
</el-form-item>
</template>
<template #operation>
<el-button icon="el-icon-Back" @click="$emit('close')">返回</el-button>
</template>
</TableHeader>
<Table ref="tableRef" isGroup />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { ElMessage } from 'element-plus'
import { dictDataDelete } from '@/api/system-boot/dicData'
defineOptions({
name: 'setting/dictionary/list/detail'
})
const radio2 = ref('1')
interface Props {
timePopUpBox: anyObj
}
const props = withDefaults(defineProps<Props>(), {
timePopUpBox: () => {
return {}
}
})
const popupEditRef = ref()
const tableStore = new TableStore({
url: '/harmonic-boot/powerStatistics/getTargetByTime',
method: 'POST',
showPage: false,
column: [
{
title: '指标',
field: 'anotherName',
minWidth: '120px',
formatter: row => {
row.column.title =
radio2.value == '1' ? '指标' : (row.column.title = radio2.value == '4' ? '间谐波次数' : '谐波次数')
return row.cellValue
}
},
{ title: '单位', field: 'unit' },
{
title: '最小值',
field: 'min',
children: [
{
title: 'A',
field: 'minphaseA',
formatter: row => formatter(row)
},
{ title: 'B', field: 'minphaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'minphaseC', formatter: row => formatter(row) }
]
},
{
title: '最大值',
field: 'max',
children: [
{ title: 'A', field: 'maxphaseA', formatter: row => formatter(row) },
{ title: 'B', field: 'maxphaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'maxphaseC', formatter: row => formatter(row) }
]
},
{
title: '平均值',
field: 'avg',
children: [
{ title: 'A', field: 'avgphaseA', formatter: row => formatter(row) },
{ title: 'B', field: 'avgphaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'avgphaseC', formatter: row => formatter(row) }
]
},
{
title: 'cp95值',
field: 'cp95',
children: [
{ title: 'A', field: 'cp95PhaseA', formatter: row => formatter(row) },
{ title: 'B', field: 'cp95PhaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'cp95PhaseC', formatter: row => formatter(row) }
]
}
],
beforeSearchFun: () => {
tableStore.table.params.field = props.timePopUpBox.field
tableStore.table.params.lineId = props.timePopUpBox.lineId
tableStore.table.params.searchBeginTime = props.timePopUpBox.time
tableStore.table.params.searchEndTime = props.timePopUpBox.time
tableStore.table.params.statisticalId = radio2.value
},
loadCallback: () => {}
})
provide('tableStore', tableStore)
const formatter = (row: any) => {
return row.cellValue || '/'
}
onMounted(() => {
tableStore.index()
})
</script>
<template>
<div class="dictiontary-list-detail child-router">
<TableHeader :showSearch="false" showCustomColumn>
<template #select>
<el-form-item label="">
<span class="text-large font-600 mr-3">{{ props.timePopUpBox.time }}</span>
</el-form-item>
<el-form-item>
<el-radio-group v-model="radio2" @change="tableStore.index()">
<el-radio-button label="基础数据" value="1" />
<el-radio-button label="谐波电压" value="2" />
<el-radio-button label="谐波电流" value="3" />
<el-radio-button label="间谐波电压" value="4" />
</el-radio-group>
</el-form-item>
</template>
<template #operation>
<el-button icon="el-icon-Back" @click="$emit('close')">返回</el-button>
</template>
</TableHeader>
<Table ref="tableRef" isGroup />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { ElMessage } from 'element-plus'
import { dictDataDelete } from '@/api/system-boot/dicData'
defineOptions({
name: 'setting/dictionary/list/detail'
})
const radio2 = ref('1')
interface Props {
timePopUpBox: anyObj
}
const props = withDefaults(defineProps<Props>(), {
timePopUpBox: () => {
return {}
}
})
const popupEditRef = ref()
const tableStore = new TableStore({
url: '/harmonic-boot/powerStatistics/getTargetByTime',
method: 'POST',
showPage: false,
column: [
{
title: '指标',
field: 'anotherName',
minWidth: '120px',
formatter: row => {
row.column.title =
radio2.value == '1' ? '指标' : (row.column.title = radio2.value == '4' ? '间谐波次数' : '谐波次数')
return row.cellValue
}
},
{ title: '单位', field: 'unit' },
{
title: '最小值',
field: 'min',
children: [
{
title: 'A',
field: 'minphaseA',
formatter: row => formatter(row)
},
{ title: 'B', field: 'minphaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'minphaseC', formatter: row => formatter(row) }
]
},
{
title: '最大值',
field: 'max',
children: [
{ title: 'A', field: 'maxphaseA', formatter: row => formatter(row) },
{ title: 'B', field: 'maxphaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'maxphaseC', formatter: row => formatter(row) }
]
},
{
title: '平均值',
field: 'avg',
children: [
{ title: 'A', field: 'avgphaseA', formatter: row => formatter(row) },
{ title: 'B', field: 'avgphaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'avgphaseC', formatter: row => formatter(row) }
]
},
{
title: 'cp95值',
field: 'cp95',
children: [
{ title: 'A', field: 'cp95PhaseA', formatter: row => formatter(row) },
{ title: 'B', field: 'cp95PhaseB', formatter: row => formatter(row) },
{ title: 'C', field: 'cp95PhaseC', formatter: row => formatter(row) }
]
}
],
beforeSearchFun: () => {
tableStore.table.params.field = props.timePopUpBox.field
tableStore.table.params.lineId = props.timePopUpBox.lineId
tableStore.table.params.searchBeginTime = props.timePopUpBox.time
tableStore.table.params.searchEndTime = props.timePopUpBox.time
tableStore.table.params.statisticalId = radio2.value
},
loadCallback: () => {}
})
provide('tableStore', tableStore)
const formatter = (row: any) => {
return row.cellValue || '/'
}
onMounted(() => {
tableStore.index()
})
</script>

View File

@@ -779,6 +779,7 @@ const analyseList = (e: string) => {
provide('tableStore', tableStore)
onMounted(() => {
TableHeaderRef.value.setTheDate(4)
const dom = document.getElementById('navigation-splitpanes')
if (dom) {
size.value = Math.round((180 / dom.offsetHeight) * 120)

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader ref="TableHeaderRef">
<TableHeader ref="TableHeaderRef" showCustomColumn>
<template #select>
<el-form-item label="新能源场站名称">
<el-input v-model="tableStore.table.params.name" clearable placeholder="输入关键字筛选" />

View File

@@ -1,7 +1,7 @@
<template>
<div class="default-main">
<div v-show="flg">
<TableHeader datePicker showExport ref="TableHeaderRef">
<TableHeader datePicker showExport ref="TableHeaderRef" showCustomColumn>
<template v-slot:select>
<el-form :inline="true" label-width="90px" class="">
<el-form-item label="用户名:">

View File

@@ -1,7 +1,7 @@
<template>
<div class="default-main">
<div v-show="addedShow">
<TableHeader datePicker showExport ref="TableHeaderRef">
<TableHeader datePicker showExport ref="TableHeaderRef" showCustomColumn>
<template #select>
<el-form-item label="评估类型">
<el-select

View File

@@ -1,7 +1,7 @@
<template>
<div class="default-main">
<div>
<TableHeader :showTimeAll="false" showExport ref="TableHeaderRef">
<TableHeader :showTimeAll="false" showExport ref="TableHeaderRef" showCustomColumn>
<template #select>
<el-form-item label="用户类型">
<el-select

View File

@@ -1,7 +1,7 @@
<template>
<!-- 新增 -->
<el-dialog draggable title="详情" v-model="dialogVisible" width="1200px">
<TableHeader datePicker showExport :showReset="false">
<TableHeader datePicker showExport :showReset="false" >
<template v-slot:select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" placeholder="请输入筛选数据" clearable maxlength="32" show-word-limit/>

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader datePicker showExport>
<TableHeader datePicker showExport showCustomColumn>
<template v-slot:select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" placeholder="请输入筛选数据" clearable maxlength="32" show-word-limit/>

View File

@@ -462,7 +462,7 @@ const currentChangeEvent = () => {
hasDevices.value = hasAnyDevices
dataSource.value = res.data.processDeviceList.filter(item => {
item.name = item.processNo + ''
item.name = item.processNo + '号进程'
return true // 保持原有的过滤逻辑
})
} else {

View File

@@ -1107,7 +1107,7 @@
</el-form-item>
<el-form-item
class="form-item"
label="测量点性质:"
label="电网标志:"
:prop="
'deviceParam.' +
[deviceIndex] +
@@ -1119,14 +1119,14 @@
"
:rules="{
required: true,
message: '请选择测量点性质',
message: '请选择电网标志',
trigger: 'blur'
}"
>
<el-select
filterable
v-model="lineItem.powerFlag"
placeholder="请选择测量点性质"
placeholder="请选择电网标志"
:disabled="pageStatus == 1"
@change="lineItem.objId = ''"
>
@@ -1140,14 +1140,15 @@
</el-form-item>
<el-form-item
class="form-item"
label="用户名称:"
label="监测对象名称:"
v-if="lineItem.powerFlag == 1"
>
<el-select
filterable
v-model="lineItem.objId"
placeholder="请选择用户名称"
placeholder="请选择对象名称"
:disabled="pageStatus == 1"
@change="(val) => handleSelect(lineItem, val)"
clearable
>
<el-option
@@ -1355,7 +1356,7 @@
lIndex +
'.bigObjType'
"
:rules="{ required: true, message: '请选择对象大类', trigger: 'blur' }"
>
<el-select
filterable
@@ -1384,7 +1385,7 @@
lIndex +
'.smallObjType'
"
:rules="{ required: true, message: '请选择对象小类', trigger: 'blur' }"
>
<el-select
filterable
@@ -2202,6 +2203,22 @@ const category = () => {
bigList.value = convertToTree(res.data)
})
}
//用户对象chage事件
const handleSelect = (item, selectedId) => {
// 清空选择
if (!selectedId) {
item.projectName = ''
return
}
// 过滤当前下拉数据源
const options = newStationIdArr.value.find(i => i.id === selectedId)
if (options) {
item.objName = options.projectName
}
}
// 组装用户大小类
const convertToTree = (data: any) => {
const nodeMap: any = {}

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader :showReset="false" showExport>
<TableHeader :showReset="false" showExport showCustomColumn>
<template #select>
<el-form-item label="筛选数据">
<el-input

View File

@@ -0,0 +1,185 @@
<template>
<el-dialog
:title="dialogMode === 'detail' ? '定检详情' : '上传周期检测报告'"
:width="dialogMode === 'detail' ? '1000px' : '600px'"
v-model="dialogShow"
v-if="dialogShow"
>
<!-- 详情区域 -->
<div v-if="dialogMode === 'detail'">
</div>
<!-- 上传报告表单区域 -->
<div v-if="dialogMode === 'upload'">
<el-form ref="uploadFormRef" :model="uploadForm" :rules="uploadRules" label-width="120px">
<el-form-item label="定检日期" prop="thisCheckTime">
<el-date-picker
v-model="uploadForm.thisCheckTime"
type="date"
placeholder="请选择定检日期"
@change="calcNextCheckTime"
style="width:100%"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="下次定检日期" prop="nextCheckTime">
<el-date-picker
v-model="uploadForm.nextCheckTime"
type="date"
placeholder="请选择下次定检日期"
style="width:100%"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item class="uploadFile" for="-" label="设备检测报告:">
<el-upload
v-model:file-list="uploadForm.checkUrl"
ref="uploadRef"
action=""
:accept="acceptType"
:limit="1"
:on-change="(file) => choose(file, 'checkUrl')"
:auto-upload="false"
:on-remove="removeFile('checkUrl')"
>
<template #trigger>
<el-button type="primary">上传文件</el-button>
</template>
</el-upload>
</el-form-item>
</el-form>
</div>
<template #footer>
<el-button v-if="dialogMode === 'upload'" @click="dialogShow = false">取消</el-button>
<el-button
v-if="dialogMode === 'upload'"
type="primary"
:loading="submitLoading"
@click="submitUploadReport"
>
提交
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, nextTick } from 'vue'
import type { UploadFile } from 'element-plus'
import { ElMessage } from 'element-plus'
import { uploadFile } from '@/api/system-boot/file'
import { userCheckUpload } from '@/api/device-boot/device'
const emit = defineEmits(['onSubmit'])
const acceptType = '.pdf,.docx'
const detailId = ref('')
const dialogShow = ref(false)
const dialogMode = ref<'detail' | 'upload'>('detail')
const submitLoading = ref(false)
const uploadFormRef = ref()
const uploadForm = reactive({
thisCheckTime: '',
nextCheckTime: '',
checkUrl: [] as any[],
devId: '',
objId: ''
})
const checkUrlFile = ref('')
const calcNextCheckTime = (dateVal: string) => {
if (!dateVal) {
uploadForm.nextCheckTime = ''
return
}
const date = new Date(dateVal)
date.setFullYear(date.getFullYear() + 3)
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, '0')
const d = String(date.getDate()).padStart(2, '0')
uploadForm.nextCheckTime = `${y}-${m}-${d}`
}
const uploadRules = reactive({
thisCheckTime: [{ required: true, message: '请选择定检日期', trigger: 'change' }],
checkUrl: [{ required: true, message: '请上传设备检测报告', trigger: 'change' }]
})
const submitUploadReport = async () => {
await uploadFormRef.value?.validate(async (valid: boolean) => {
if (!valid) return
submitLoading.value = true
try {
const params = {
devId: uploadForm.devId,
objId: uploadForm.objId,
thisCheckTime: uploadForm.thisCheckTime,
nextCheckTime: uploadForm.nextCheckTime,
checkUrl: checkUrlFile.value
}
const res = await userCheckUpload(params)
if (res.code === 'A0000') {
ElMessage.success('提交成功')
dialogShow.value = false
emit('onSubmit')
}
} catch (err: any) {
ElMessage.error(err.msg || '提交失败')
} finally {
submitLoading.value = false
}
})
}
const getFileName = (filePath: string) => {
if (!filePath) return ''
const parts = filePath.split('/')
return parts[parts.length - 1] || filePath.replace(/^.*?\/(.*?)\//, '')
}
const openUpload = (row: any) => {
uploadForm.devId = row.devId
uploadForm.objId = row.objId
uploadForm.thisCheckTime = row.thisCheckTime || ''
uploadForm.nextCheckTime = row.nextCheckTime || ''
if (row.checkUrl) {
checkUrlFile.value = row.checkUrl
uploadForm.checkUrl = [{ name: getFileName(row.checkUrl), url: row.checkUrl }]
} else {
checkUrlFile.value = ''
uploadForm.checkUrl = []
}
dialogMode.value = 'upload'
dialogShow.value = true
nextTick(() => {
uploadFormRef.value?.clearValidate()
})
}
const openDetail = (id: string) => {
detailId.value = id
dialogMode.value = 'detail'
dialogShow.value = true
}
const removeFile = (val: string) => {
return (_file: UploadFile, fileList: UploadFile[]) => {
if (val === 'checkUrl') {
checkUrlFile.value = ''
}
uploadForm[val] = fileList
}
}
const choose = (e: any, field: string) => {
uploadFile(e.raw, '/supervision/').then(res => {
if (field === 'checkUrl') {
checkUrlFile.value = res.data.name
}
})
}
defineExpose({ openUpload, openDetail })
</script>

View File

@@ -0,0 +1,205 @@
<template>
<div class="default-main">
<TableHeader ref="TableHeaderRef" showExport>
<template #select>
<el-form-item label="地市">
<el-select v-model="tableStore.table.params.city" clearable placeholder="请选择地市">
<el-option
v-for="item in areaOptionList"
:key="item.id"
:label="item.name"
:value="item.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="监测对象">
<el-input
style="width: 200px"
placeholder="请输入监测对象名称"
v-model="tableStore.table.params.searchValue"
clearable
maxlength="32"
show-word-limit
></el-input>
</el-form-item>
</template>
<template #operation>
<!-- <el-button icon="el-icon-Plus" type="primary" @click="addFormModel">新增定检记录</el-button>-->
</template>
</TableHeader>
<Table ref="tableRef" @cell-click="cellClickEvent" />
<checkForm ref="checkFormRef" @onSubmit="tableStore.index()" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, watch } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import checkForm from './components/form.vue'
import { useDictData } from '@/stores/dictData'
import { useAdminInfo } from '@/stores/adminInfo'
import { download } from '@/utils/fileDownLoad'
defineOptions({
name: 'terminal/terminaCkeck'
})
// 公共依赖
const TableHeaderRef = ref()
const checkFormRef = ref()
const dictData = useDictData()
const adminInfo = useAdminInfo()
// 地区字典(和你原有地市下拉同源)
const areaOptionList = dictData.getBasicData('jibei_area')
// TableStore 表格配置(仅修改字段映射,其余不动)
const tableStore = new TableStore({
url: '/device-boot/alarm/terminalCheckPage',
method: 'POST',
column: [
// 多选框
{
width: '60',
type: 'checkbox'
},
// 序号(分页自增,和你现有逻辑一致)
{
title: '序号',
width: 70,
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'areaName', title: '所在地市', minWidth: 100 },
{ field: 'bdName', title: '变电站', minWidth: 150 },
{ field: 'objName', title: '监测对象', minWidth: 180 },
{ field: 'devName', title: '终端名称', minWidth: 180 },
{ field: 'loginDate', title: '投运时间', minWidth: 120 },
{ field: 'thisCheckTime', title: '本次定检时间', minWidth: 120 },
{ field: 'nextCheckTime', title: '下次定检时间', minWidth: 120 },
{
title: '是否逾期',
field: 'checkStatus',
align: 'center',
width: 80,
render: 'customRender',
customRender: props => {
const val = props.renderValue
let text = ''
let color = ''
if (val === 1) {
text = '是'
color = '#f56c6c'
} else if (val === 0) {
text = '否'
color = '#67c23a'
}
return h('span', { style: { color, fontWeight: 'bold' } }, text)
}
},
{
field: 'overdueDay',
title: '逾期天数',
minWidth: 80,
render: 'customRender',
customRender: props => {
const val = props.renderValue
let text = ''
let color = ''
if (val > 0) {
text = val
color = '#f56c6c'
} else if (val > -30) {
text = Math.abs(val).toString() + '天后将逾期'
color = '#e6a23c'
} else {
text = '/'
}
return h('span', { style: { color, fontWeight: 'bold' } }, text)
}
},
{
field: 'checkUrl',
title: '周期检测报告',
minWidth: 160,
render: 'customRender',
customRender: props => {
const val = props.renderValue?.replace(/^.*?\/(.*?)\//, '') || ''
return val ? h('span', { style: { color: 'var(--el-color-primary)', cursor: 'pointer' } }, val) : '/'
}
},
// 操作列固定右侧
{
title: '操作',
fixed: 'right',
minWidth: 160,
render: 'buttons',
buttons: [
// {
// name: 'detail',
// title: '详情',
// type: 'primary',
// icon: 'el-icon-EditPen',
// render: 'basicButton',
// click: row => {
// checkFormRef.value.openDetail(row.devId)
// }
// },
{
name: 'edit',
title: '上传周期检测报告',
type: 'primary',
icon: 'el-icon-Open',
render: 'basicButton',
click: row => {
checkFormRef.value.openUpload(row)
}
}
]
}
],
beforeSearchFun: () => {
// 携带当前登录人部门ID
tableStore.table.params.orgId = adminInfo.$state.deptId
}
})
// 初始化查询参数
tableStore.table.params.city = ''
tableStore.table.params.substation = ''
tableStore.table.params.terminalName = ''
tableStore.table.params.orgId = adminInfo.$state.deptId
// 路由跳转带ID回显编辑复用你原有路由传参逻辑
const props = defineProps({
id: { type: String, default: 'null' }
})
watch(
() => props.id,
async newVal => {
if (newVal === 'null') return
const fullId = newVal.split('@')[0]
const routeTime = Number(newVal.split('@')[1])
if (isNaN(routeTime) || Date.now() - routeTime > import.meta.env.VITE_ROUTE_TIME_OUT) return
const res = await getPqCheckById(fullId)
if (res.code === 'A0000') {
}
},
{ immediate: true }
)
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field == 'checkUrl') {
row.checkUrl ? download(row.checkUrl) : ''
}
}
provide('tableStore', tableStore)
onMounted(() => {
tableStore.index()
})
</script>
<style scoped></style>

View File

@@ -41,7 +41,7 @@
</el-col>
<el-col :span="19">
<div class="mt10">
<TableHeader showExport :showSearch="false" ref="TableHeaderRef">
<TableHeader showExport :showSearch="false" ref="TableHeaderRef" showCustomColumn>
<template v-slot:select>
<span style="font-size: 14px; line-height: 40px; color: red;">
所选变电站:
@@ -504,14 +504,16 @@ const maintenance = (val: any) => {
id: val.tfIndex,
subId: val.subIndex
}).then(res => {
bind.value = res.data.bind
treeMenuLeftData.value = res.data.upNode
treeMenuRightData.value = res.data.downNode
setDisabled(treeMenuRightData.value)
setDisabled(treeMenuLeftData.value)
isLoading3.value = false
if (treeMenuLeftData.value && treeMenuLeftData.value.length > 0) {
treeMenuLeftData.value[0].children?.forEach(item => {
/* treeMenuLeftData.value[0].children?.forEach(item => {
item.children?.forEach(item1 => {
item1.children?.forEach(item2 => {
item2.children?.forEach(item3 => {
@@ -523,21 +525,25 @@ const maintenance = (val: any) => {
})
})
})
})
})*/
menuTreeLeft.value?.setCheckedKeys([bind.value[0]])
maintenanceData.upNode = bind.value[0]
bindLevel.value = 5
}
if (treeMenuRightData.value && treeMenuRightData.value.length > 0) {
treeMenuRightData.value[0].children?.forEach(item => {
/* treeMenuRightData.value[0].children?.forEach(item => {
item.children?.forEach(item1 => {
item1.children?.forEach(item2 => {
item2.children?.forEach(item3 => {
if (item3.level != null) {
menuTreeRight.value?.setCheckedKeys([item3.id])
maintenanceData.downNode = item3.id
}
})
})
})
})
})*/
menuTreeRight.value?.setCheckedKeys([bind.value[1]])
maintenanceData.downNode = bind.value[1]
}
nodemaintenanceInformation.value = true
})

View File

@@ -212,7 +212,7 @@
<el-form-item for="-" v-if="form.userType != '0' && form.userType != '1'" label="PCC点">
<el-input v-model.trim="form.pccPoint" autocomplete="off" placeholder="请输入PCC点" />
</el-form-item>
<el-form-item
<!-- <el-form-item
for="-"
v-if="form.userType == '2' || form.userType == '3' || form.userType == '4' || form.userType == '5'"
label="基准短路容量:"
@@ -273,7 +273,7 @@
>
<template #append>MVA</template>
</el-input>
</el-form-item>
</el-form-item>-->
<el-form-item for="-" v-if="form.userType == '6'" label="行业:">
<el-select v-model="form.industry" placeholder="请选择行业">
<el-option v-for="(item, index) in industryList" :label="item.name" :value="item.id" :key="index" />
@@ -368,6 +368,48 @@
show-word-limit
/>
</el-form-item>
<el-form-item
class="uploadFile"
for="-"
label="设备检测报告:"
prop="checkUrl"
>
<el-upload
v-model:file-list="form.checkUrl"
ref="uploadRef"
action=""
:accept="acceptType"
:limit="1"
:on-change="(file) => choose(file, 'checkUrl')"
:auto-upload="false"
:on-remove="removeFile('checkUrl')"
>
<template #trigger>
<el-button type="primary" >上传文件</el-button>
</template>
</el-upload>
</el-form-item>
<el-form-item
class="uploadFile"
for="-"
label="电能质量评估报告"
prop="evaluation"
>
<el-upload
v-model:file-list="form.assessUrl"
ref="uploadRef"
action=""
:accept="acceptType"
:limit="1"
:on-change="(file) => choose(file, 'assessUrl')"
:auto-upload="false"
:on-remove="removeFile('assessUrl')"
>
<template #trigger>
<el-button type="primary">上传文件</el-button>
</template>
</el-upload>
</el-form-item>
<el-form-item for="-" label="预测评估结论" style="width: 100%">
<el-input
type="textarea"
@@ -390,7 +432,7 @@
<script lang="ts" setup>
import { ref, onMounted, reactive, defineExpose, defineProps, defineEmits, watch, onUnmounted } from 'vue'
// import { upload, insertTerminal, updateTerminal } from '@/api/process-boot/terminal'
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
import type { UploadFile, UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
import { genFileId, ElMessage } from 'element-plus'
import { useDictData } from '@/stores/dictData'
import type { ComponentSize, FormInstance, FormRules } from 'element-plus'
@@ -435,10 +477,11 @@ const props = defineProps({
}
})
const emits = defineEmits(['onSubmit'])
const IS_LN = import.meta.env.VITE_NAME == 'LN' || import.meta.env.VITE_NAME == 'LNqr'
const dictData = useDictData()
const dialogFormVisible = ref(false)
// .doc,.docx,.xlsx,.xls,.pdf
const acceptType = ''
const acceptType = '.pdf,.docx'
const form: any = ref({})
const loading = ref(false)
const control: any = ref(true)
@@ -532,6 +575,10 @@ const defaultProps = {
const nonlinearDeviceTypeList: any = ref('')
//获取登陆用户姓名和部门
const adminInfo = useAdminInfo()
// 电能质量检测报告
const checkUrlFile = ref('')
// 电能质量评估报告
const evaluationFile = ref('')
const resetForm = () => {
form.value = {
reporter: '', //填报人 1
@@ -558,6 +605,8 @@ const resetForm = () => {
pccPoint: '', //PCC点 1
evaluationType: evaluationTypeList[0].id, //评估类型 1
evaluationChekDept: '', //预测评估审单位 1
checkUrl: [], //电能质量检测报告
assessUrl: [], //电能质量评估报告
minShortCircuitCapacity: '', //系统最小短路容量MVA 1
userAgreementCapacity: '', //用户用电协议容量MVA 1
needGovernance: 0, //是否需要治理 1
@@ -581,6 +630,8 @@ const resetForm = () => {
delete form.value.loadLevel
delete form.value.powerSupplyInfo
}
checkUrlFile.value = ''
evaluationFile.value = ''
}
//初始化数据
resetForm()
@@ -694,7 +745,7 @@ const rules = ref({
reporter: [{ required: true, message: '请输入填报人', trigger: 'blur' }],
reportDate: [{ required: true, message: '请选择填报日期', trigger: 'change' }],
orgId: [{ required: true, message: '请选择填报部门', trigger: 'change' }],
stationId: [{ required: true, message: '请选择所属电站', trigger: 'change' }],
//stationId: [{ required: true, message: '请选择所属电站', trigger: 'change' }],
expectedProductionDate: [{ required: true, message: '请选择工程预期投产日期', trigger: 'change' }],
userType: [{ required: true, message: '清选择用户性质', trigger: 'change' }],
city: [{ required: true, message: '请选择所在地市', trigger: 'change' }],
@@ -900,6 +951,10 @@ const open = async (row: any) => {
} else {
resendId.value = ''
form.value.userType = userTypeList.value[0].value
checkUrlFile.value = ''
evaluationFile.value = ''
form.value.checkUrl = []
form.value.evaluation = []
}
await getSubstationSelect().then(res => {
stationList.value = res.data
@@ -917,18 +972,36 @@ async function handleResponse(data: any) {
// userType logic is commented out; if needed, implement accordingly
if (data.userReportProjectPO) {
form.value = { ...data, ...data.userReportProjectPO }
fileRaw(data, 'userReportProjectPO')
} else if (data.userReportSubstationPO) {
form.value = { ...data, ...data.userReportSubstationPO }
fileRaw(data, 'userReportSubstationPO')
} else if (data.userReportSensitivePO) {
form.value = {
...data,
...data.userReportSensitivePO,
energyQualityIndex: data.userReportSensitivePO.energyQualityIndex.split(', ')
}
fileRaw(data, 'userReportSensitivePO')
}
form.value.orgId = adminInfo.$state.deptName
}
// 处理上传文件回显
const fileRaw = (row: any, key: string) => {
const fileFields = ['checkUrl', 'assessUrl']
for (const field of fileFields) {
const filePath = row[key]?.[field] ?? row[field]
if (filePath != null && filePath.length > 0) {
form.value[field] = [{ name: filePath.split('/')[2] }]
} else {
form.value[field] = []
}
}
checkUrlFile.value = row[key]?.checkUrl ?? row.checkUrl ?? ''
evaluationFile.value = row[key]?.assessUrl ?? row.assessUrl ?? ''
}
const close = () => {
//重置表单内容
//取消表单校验状态
@@ -947,14 +1020,25 @@ const handleExceed: UploadProps['onExceed'] = files => {
file.uid = genFileId()
uploadRef.value!.handleStart(file)
}
let uploadName = ref('')
//上传报告字段名称
const uploadFileName = (val: any) => {
uploadName.value = val
}
//移除文件上传
const removeFile = (file: any, uploadFiles: any) => {
console.log(file, uploadFiles)
const removeFile = (val: string) => {
return (_file: UploadFile, fileList: UploadFile[]) => {
if (val === 'checkUrl') {
checkUrlFile.value = ''
} else if (val === 'assessUrl') {
evaluationFile.value = ''
}
form.value[val] = fileList
}
}
const choose = (e: any, field: string) => {
uploadFile(e.raw, '/supervision/').then(res => {
if (field === 'checkUrl') {
checkUrlFile.value = res.data.name
} else if (field === 'assessUrl') {
evaluationFile.value = res.data.name
}
})
}
// 关联终端
@@ -981,12 +1065,16 @@ const confirmForm = async (flag: boolean) => {
subForm.value.reporter = adminInfo.$state.id
subForm.value.orgId = adminInfo.$state.deptId
let confirmFormData = JSON.parse(JSON.stringify(subForm.value))
confirmFormData.checkUrl = checkUrlFile.value
confirmFormData.assessUrl = evaluationFile.value
//1.判断是否上传
if (subForm.value.userType == '0' || subForm.value.userType == '1') {
{
//其他报告非必填
confirmFormData.userReportProjectPO = {
...confirmFormData.userReportProjectPO
...confirmFormData.userReportProjectPO,
checkUrl: checkUrlFile.value,
assessUrl: evaluationFile.value
}
}
}
@@ -999,7 +1087,9 @@ const confirmForm = async (flag: boolean) => {
) {
{
confirmFormData.userReportSubstationPO = {
...confirmFormData.userReportSubstationPO
...confirmFormData.userReportSubstationPO,
checkUrl: checkUrlFile.value,
assessUrl: evaluationFile.value
}
}
}
@@ -1008,7 +1098,9 @@ const confirmForm = async (flag: boolean) => {
{
confirmFormData.userReportSensitivePO = {
...confirmFormData.userReportSensitivePO,
energyQualityIndex: confirmFormData.userReportSensitivePO.energyQualityIndex.join(', ')
energyQualityIndex: confirmFormData.userReportSensitivePO.energyQualityIndex.join(', '),
checkUrl: checkUrlFile.value,
assessUrl: evaluationFile.value
}
}
}
@@ -1060,8 +1152,6 @@ const filterUsers = (arr: any) => {
userTypeList.value = userTypeList.value.filter(item => !arr.includes(item.value))
}
const setcontroFlag = () => {
controFlag.value = true
}
defineExpose({ open, filterUsers, setcontroFlag })

View File

@@ -212,12 +212,21 @@
})?.name
}}
</el-descriptions-item>
<el-descriptions-item label="敏感电能质量指标" v-if="detailData.userType == 6">
{{
energyQualityIndexList.find(item => {
return item.id == proviteData.energyQualityIndex
})?.name
}}
<el-descriptions-item :span="2" label="电能质量设备检测报告">
<el-icon class="elView" v-if="detailData?.checkUrl">
<View @click="openFile(detailData?.checkUrl)" />
</el-icon>
<span class="aLoad" @click="download(detailData.checkUrl)" target="_blank">
{{ detailData.checkUrl?.replace(/^.*?\/(.*?)\//, '') || '' }}
</span>
</el-descriptions-item>
<el-descriptions-item :span="2" label="电能质量评估报告">
<el-icon class="elView" v-if="detailData?.assessUrl">
<View @click="openFile(detailData?.assessUrl)" />
</el-icon>
<span class="aLoad" @click="download(detailData.assessUrl)" target="_blank">
{{ detailData.assessUrl?.replace(/^.*?\/(.*?)\//, '') || '' }}
</span>
</el-descriptions-item>
</el-descriptions>
</div>
@@ -233,7 +242,7 @@ import { getDictTreeById } from '@/api/system-boot/dictTree'
import { useDictData } from '@/stores/dictData'
import { getFileNameAndFilePath } from '@/api/system-boot/file'
import { Link, View } from '@element-plus/icons-vue'
import PreviewFile from '@/components/PreviewFile/index.vue'
import { download } from '@/utils/fileDownLoad'
// import { addOrUpdateFile, getFileById } from '@/api/supervision-boot/interfere/index'
defineOptions({ name: 'BpmUserReportDetail' })

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader ref="TableHeaderRef">
<TableHeader ref="TableHeaderRef" showExport showCustomColumn>
<template #select>
<el-form-item label="项目名称">
<el-input
@@ -32,7 +32,7 @@
<el-button icon="el-icon-Upload" type="primary" @click="importUserData">批量导入</el-button> -->
</template>
</TableHeader>
<Table ref="tableRef" />
<Table ref="tableRef" @cell-click="cellClickEvent" />
<el-dialog title="详情" width="1000px" v-model="dialogShow" v-if="dialogShow">
<DetailInfo :id="userId" :openType="'sourcesOfInterference'"></DetailInfo>
@@ -45,7 +45,7 @@
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, provide, nextTick, watch } from 'vue'
import { ref, onMounted, reactive, provide, nextTick, watch, onUnmounted } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
@@ -58,6 +58,7 @@ import { downloadSensitiveReportTemplate } from '@/api/supervision-boot/userRepo
import DetailInfo from './components/detail.vue'
import { cancelFormData, getUserReportById } from '@/api/supervision-boot/interfere/index'
import { deleteUserReport } from '@/api/device-boot/sensitiveLoadMange'
import { download } from '@/utils/fileDownLoad'
const addForms = ref()
const dictData = useDictData()
const sensitiveUserPopup = ref()
@@ -88,13 +89,13 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'city', title: '所在地市', minWidth: 80 },
{ field: 'substation', title: '厂站名称', minWidth: 100 },
{ field: 'projectName', title: '项目名称', minWidth: 170 },
{ field: 'city', title: '所在地市', minWidth: 100 },
/* { field: 'substation', title: '厂站名称', minWidth: 100 },*/
{ field: 'projectName', title: '项目名称', minWidth: 150 },
{
field: 'userType',
title: '用户性质',
minWidth: 150,
minWidth: 180,
formatter: (obj: any) => {
const userType = obj.row.userType
return getUserTypeName(userType)
@@ -102,19 +103,42 @@ const tableStore = new TableStore({
},
// { field: 'responsibleDepartment', title: '归口管理部门', minWidth: 130 },
{ field: 'ratePower', title: '装机容量(MW)', minWidth: 130 },
{ field: 'ratePower', title: '装机容量(MW)', minWidth: 80 },
{ field: 'stationId', title: '所属电站', minWidth: 150 },
{
field: 'checkUrl',
title: '电能质量设备检测报告',
minWidth: 160,
render: 'customRender',
customRender: props => {
const val = props.renderValue?.replace(/^.*?\/(.*?)\//, '') || ''
return val ? h('span', { style: { color: 'var(--el-color-primary)', cursor: 'pointer' } }, val) : '/'
}
},
{
field: 'assessUrl',
title: '电能质量评估报告',
minWidth: 160,
render: 'customRender',
customRender: props => {
const val = props.renderValue?.replace(/^.*?\/(.*?)\//, '') || ''
return val ? h('span', { style: { color: 'var(--el-color-primary)', cursor: 'pointer' } }, val) : '/'
}
},
/* {
field: 'createBy',
title: '创建人',
minWidth: 80,
formatter: (row: any) => {
return dictData.state.userList.filter(item => item.id == row.cellValue)[0]?.name
}
},
},*/
{
title: '操作',fixed: 'right',
title: '操作',
fixed: 'right',
minWidth: 150,
render: 'buttons',
buttons: [
{
@@ -137,7 +161,6 @@ const tableStore = new TableStore({
disabled: row => {
return !(row.status == 0)
},
click: row => {
addForms.value.filterUsers([6])
@@ -147,9 +170,7 @@ const tableStore = new TableStore({
row: row
})
}
},
}
]
}
],
@@ -208,27 +229,13 @@ const deleteEven = () => {
})
}
}
/**取消流程操作*/
const cancelLeave = async (row: any) => {
// 二次确认
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputType: 'textarea',
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
inputErrorMessage: '取消原因不能为空'
})
// 发起取消
let data = {
id: row.id,
processInstanceId: row.processInstanceId,
dataType: 1,
reason: value
// 点击行
const cellClickEvent = ({ row, column }: any) => {
if (column.field == 'checkUrl') {
row.checkUrl ? download(row.checkUrl) : ''
} else if (column.field == 'assessUrl') {
row.assessUrl ? download(row.assessUrl) : ''
}
await cancelFormData(data)
ElMessage.success('取消成功')
// 加载数据
tableStore.index()
}
// 新增

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader date-picker>
<TableHeader date-picker showCustomColumn>
<template v-slot:select>
<!-- <el-form-item label="用户名">
<el-select v-model="value" class="m-2" placeholder="Select" size="large">
@@ -33,14 +33,22 @@ const tableStore = new TableStore({
url: '/system-boot/audit/getAuditLog',
method: 'POST',
column: [
{ title: '操作时间', field: 'time', align: 'center', width: 200 },
{ title: '操作人员', field: 'userName', align: 'center', width: 120 },
{ title: '操作类型', field: 'operate', align: 'center', width: 220 },
{ title: '事件描述', field: 'describe', align: 'center', showOverflow: true,minWidth: 200 },
{ title: '事件类型', field: 'type', align: 'center', width: 160 },
{ title: '操作结果', field: 'type', align: 'center', width: 100 },
{ title: '操作IP', field: 'ip', align: 'center', width: 120 },
{ title: '事件等级', field: 'level', align: 'center', width: 100 }
{
title: '序号',
field: 'index',
minWidth: 80,
formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '操作时间', field: 'time', align: 'center', minWidth: 200 },
{ title: '操作人员', field: 'userName', align: 'center', minWidth: 120 },
{ title: '操作类型', field: 'operate', align: 'center', minWidth: 220 },
{ title: '事件描述', field: 'describe', align: 'center', minWidth: 200 },
{ title: '事件类型', field: 'type', align: 'center', minWidth: 160 },
{ title: '操作结果', field: 'type', align: 'center', minWidth: 100 },
{ title: '操作IP', field: 'ip', align: 'center', minWidth: 120 },
{ title: '事件等级', field: 'level', align: 'center', minWidth: 100 }
]
})
tableStore.table.params.loginName = ''

View File

@@ -2,32 +2,39 @@
<div class="default-main" :style="height">
<!-- 标准库 -->
<splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes">
<splitpanes
style="height: 100%"
class="default-theme"
id="navigation-splitpanes"
@resize="onPaneResize"
@resized="onPaneResized"
>
<pane :size="size">
<standardTree ref="treeRef"
:default-expanded-keys="monitoringPoint.state.lineId ? [monitoringPoint.state.lineId] : []"
:current-node-key="monitoringPoint.state.lineId" @node-click="handleNodeClick"
@init="handleNodeClick"></standardTree>
</pane>
<pane style="background: #fff" :style="height">
<div class="pd10" style="display: flex; justify-content: end">
<pane style="background: #fff" class="standard-pane">
<div class="pd10 standard-pane__toolbar" style="display: flex; justify-content: end">
<el-button icon="el-icon-Plus" type="primary" @click="addUser" v-if="information">新增</el-button>
<el-button icon="el-icon-Edit" type="primary" @click="editUser" v-if="information">修改</el-button>
<el-button icon="el-icon-Delete" type="primary" @click="deleteEven"
v-if="information">删除</el-button>
<el-button icon="el-icon-Download" type="primary" @click="download" v-if="flag">下载</el-button>
</div>
<el-empty v-if="url.length == 0" description="暂无数据" class="custom-empty"
:style="`height: calc(${height.height} - 60px);`" />
<div :style="`height: calc(${height.height} - 60px);overflow: auto;`" v-else>
<vue-office-docx v-if="urlKey.includes('.doc') || urlKey.includes('.docx')" :src="url" />
<vue-office-excel v-if="urlKey.includes('.xls') || urlKey.includes('.xlsx')" :src="url"
:options="excelOptions" />
<!-- <vue-office-pdf v-if="url.includes('.pdf')" :src="url"/> -->
<iframe v-if="urlKey.includes('.pdf')" :src="url" style="width: 100%; height: 99%"></iframe>
<img v-if="
urlKey.includes('.png') || urlKey.includes('.jpg') || urlKey.includes('.gif') || urlKey.includes('.bmp')
" :src="url" />
<el-empty v-if="url.length == 0" description="暂无数据" class="custom-empty standard-pane__content" />
<div v-else class="standard-pane__content">
<div v-show="!isPaneResizing" class="standard-preview">
<vue-office-docx v-if="urlKey.includes('.doc') || urlKey.includes('.docx')" :src="url" />
<vue-office-excel v-if="urlKey.includes('.xls') || urlKey.includes('.xlsx')" :src="url"
:options="excelOptions" />
<!-- <vue-office-pdf v-if="url.includes('.pdf')" :src="url"/> -->
<iframe v-if="urlKey.includes('.pdf')" :src="url" style="width: 100%; height: 100%"></iframe>
<img v-if="
urlKey.includes('.png') || urlKey.includes('.jpg') || urlKey.includes('.gif') || urlKey.includes('.bmp')
" :src="url" />
</div>
</div>
</pane>
</splitpanes>
@@ -70,7 +77,16 @@ const addTreeRef = ref()
const url = ref('')
const dotList: any = ref({})
const flag: any = ref(false)
const isPaneResizing = ref(false)
const information = adminInfo.roleCode.includes('information_info')
const onPaneResize = () => {
isPaneResizing.value = true
}
const onPaneResized = () => {
requestAnimationFrame(() => {
isPaneResizing.value = false
})
}
onMounted(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom) {
@@ -79,7 +95,7 @@ onMounted(() => {
})
const urlKey=ref('')
const VITE_FLAG = import.meta.env.VITE_NAME == 'jibei'
const VITE_FLAG = false//import.meta.env.VITE_NAME == 'jibei'
const handleNodeClick = (data: any, node: any) => {
// console.log("🚀 ~ handleNodeClick ~ data:", data)
dotList.value = data
@@ -166,6 +182,36 @@ const download = () => {
background: #eaeef1;
}
:deep(.splitpanes__pane) {
overflow: hidden;
}
.standard-pane {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.standard-pane__toolbar {
flex-shrink: 0;
}
.standard-pane__content {
flex: 1;
min-height: 0;
overflow: auto;
}
.standard-preview {
height: 99%;
min-height: 99%;
}
:deep(.splitpanes--dragging) .standard-preview {
visibility: hidden;
}
.grid-content {
text-align: center;
}

View File

@@ -1,10 +1,16 @@
<template>
<div class="default-main">
<div v-show="view">
<TableHeader datePicker area showExport>
<TableHeader datePicker area showExport showCustomColumn>
<template #select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" placeholder="输入关键字筛选" clearable maxlength="32" show-word-limit/>
<el-input
v-model="tableStore.table.params.searchValue"
placeholder="输入关键字筛选"
clearable
maxlength="32"
show-word-limit
/>
</el-form-item>
<el-form-item label="统计类型">
<el-select
@@ -127,7 +133,7 @@ const tableStore = new TableStore({
{ field: 'lineName', title: '监测点名称', minWidth: '180' },
{ field: 'gdName', title: '供电公司', minWidth: '120' },
{ field: 'subName', title: '变电站', minWidth: '150' },
{ field: 'ip', title: '网络参数' ,width:'120px' },
{ field: 'ip', title: '网络参数', width: '120px' },
{ field: 'scale', title: '电压等级', minWidth: '110' },
{
field: 'advanceType',
@@ -137,7 +143,7 @@ const tableStore = new TableStore({
return row.cellValue || '其他'
}
},
{
{
field: 'eventType',
title: '触发类型',
minWidth: '120',
@@ -153,7 +159,7 @@ const tableStore = new TableStore({
return row.cellValue || '其他'
}
},
// {
// field: 'severity', title: '严重度', minWidth: "80", formatter: (row: any) => {
// return row.cellValue.toFixed(2)
@@ -180,7 +186,8 @@ const tableStore = new TableStore({
{ field: 'duration', title: '持续时间(s)', minWidth: '100' },
{
title: '操作',fixed: 'right',
title: '操作',
fixed: 'right',
width: '120',
render: 'buttons',
buttons: [
@@ -278,6 +285,7 @@ const download = () => {
document.body.appendChild(link)
link.click() //执行下载
document.body.removeChild(link) //释放标签
ElMessage.success('波形下载成功')
})
}
</script>

View File

@@ -1,7 +1,8 @@
<template>
<div class="default-main online">
<div class="online_header">
<TableHeader date-picker ref="tableHeaderRef">
<TableHeader date-picker ref="tableHeaderRef" showCustomColumn>
<template #select>
<el-form-item label="统计类型">
<el-select v-model="formData.statisticalType" placeholder="请选择统计类型" value-key="id"
@@ -54,8 +55,17 @@
:value="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="通讯状态">
<el-select v-model="tableStore.table.params.comFlagStatus" clearable placeholder="请选择通讯状态"
style="width: 100%">
<el-option label="正常" value="1" />
<el-option label="中断" value="0" />
</el-select>
</el-form-item>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入关键字"></el-input>
<el-input v-model="tableStore.table.params.searchValue" clearable
placeholder="请输入关键字"></el-input>
</el-form-item>
</template>
<template #operation>
@@ -77,9 +87,10 @@
</div>
<el-dialog v-model="timePopUp" draggable title="补招" width="500">
补招时间:
<el-date-picker v-model="timeData" type="datetimerange" format="YYYY-MM-DD HH:mm:00"
value-format="YYYY-MM-DD HH:mm:00" range-separator="" date-format="YYYY-MM-DD" time-format="HH:mm:00"
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 400px" :disabledDate="disabledDate" />
<el-date-picker v-model="timeData" type="datetimerange" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" range-separator="" date-format="YYYY-MM-DD" time-format="HH:mm:ss"
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 400px" :disabledDate="disabledDate"
:default-time="defaultTimeRange" />
<template #footer>
<div class="dialog-footer">
<el-button @click="timePopUp = false">取消</el-button>
@@ -139,6 +150,10 @@ const classificationData = dictData.getBasicData('Statistical_Type', ['Report_Ty
const treeData = ref([])
const idArr = ref([])
const timeData = ref([])
const defaultTimeRange: [Date, Date] = [
new Date(2000, 0, 1, 0, 0, 0),
new Date(2000, 0, 1, 23, 59, 59)
]
const logList: any = ref([])
const activeName = ref(0)
const getTreeData = async () => {
@@ -198,7 +213,7 @@ const tableStore = new TableStore({
{
title: '网络参数',
field: 'ip',
align: 'center',width:'120px',
align: 'center', width: '120px',
formatter: function (row) {
return row.cellValue ? row.cellValue : '/'
}
@@ -288,7 +303,7 @@ const tableStore = new TableStore({
// tableStore.table.params.searchEndTime = tableHeaderRef.value.datePickerRef.timeValue[1]
},
loadCallback: () => {
tableStore.table.data = tree2List(filterTreeByKeyword( tableStore.table.data,tableStore.table.params.searchValue), Math.random() * 1000)
tableStore.table.data = tree2List(filterTreeByKeyword(tableStore.table.data, tableStore.table.params.searchValue), Math.random() * 1000)
tableStore.table.column[0].title = formData.value.statisticalType.name
chartsRef.value && chartsRef.value.getTableStoreParams(tableStore.table.params)
@@ -313,6 +328,7 @@ tableStore.table.params.statisticalType = []
tableStore.table.params.scale = []
tableStore.table.params.manufacturer = []
tableStore.table.params.loadType = []
tableStore.table.params.comFlagStatus = ''
provide('tableStore', tableStore)
const tree2List = (list: any, id?: string) => {
//存储结果的数组
@@ -353,65 +369,65 @@ const tree2List = (list: any, id?: string) => {
* @returns {Array} 筛选后的嵌套树形数据,保持原层级
*/
function filterTreeByKeyword(treeData, keyword) {
// 关键词为空,直接返回原树(深拷贝,避免修改原数据)
if (!keyword || keyword.trim() === '') {
return JSON.parse(JSON.stringify(treeData));
}
const targetKey = keyword.trim();
// 存储需要保留的节点ID匹配节点+所有上级+所有下级)
const keepIdSet = new Set();
// 关键词为空,直接返回原树(深拷贝,避免修改原数据)
if (!keyword || keyword.trim() === '') {
return JSON.parse(JSON.stringify(treeData));
}
const targetKey = keyword.trim();
// 存储需要保留的节点ID匹配节点+所有上级+所有下级)
const keepIdSet = new Set();
// 第一步递归遍历树形标记所有需要保留的节点ID
const markKeepNodes = (node, parentNodes = []) => {
// 1. 若当前节点名称包含关键词,标记自身+所有上级+所有下级
const isMatch = node.name && node.name.includes(targetKey);
if (isMatch) {
// 标记自身
keepIdSet.add(node.id);
// 标记所有上级父节点
parentNodes.forEach(pNode => keepIdSet.add(pNode.id));
// 标记所有下级子节点(递归)
const markChildren = (childNode) => {
keepIdSet.add(childNode.id);
if (childNode.children && childNode.children.length) {
childNode.children.forEach(markChildren);
// 第一步递归遍历树形标记所有需要保留的节点ID
const markKeepNodes = (node, parentNodes = []) => {
// 1. 若当前节点名称包含关键词,标记自身+所有上级+所有下级
const isMatch = node.name && node.name.includes(targetKey);
if (isMatch) {
// 标记自身
keepIdSet.add(node.id);
// 标记所有上级父节点
parentNodes.forEach(pNode => keepIdSet.add(pNode.id));
// 标记所有下级子节点(递归)
const markChildren = (childNode) => {
keepIdSet.add(childNode.id);
if (childNode.children && childNode.children.length) {
childNode.children.forEach(markChildren);
}
};
if (node.children && node.children.length) {
node.children.forEach(markChildren);
}
}
};
if (node.children && node.children.length) {
node.children.forEach(markChildren);
}
}
// 2. 递归遍历子节点传递当前节点的上级链parentNodes + 当前节点)
if (node.children && node.children.length) {
node.children.forEach(child => markKeepNodes(child, [...parentNodes, node]));
}
};
// 2. 递归遍历子节点传递当前节点的上级链parentNodes + 当前节点)
if (node.children && node.children.length) {
node.children.forEach(child => markKeepNodes(child, [...parentNodes, node]));
}
};
// 遍历根节点,开始标记
treeData.forEach(rootNode => markKeepNodes(rootNode));
// 遍历根节点,开始标记
treeData.forEach(rootNode => markKeepNodes(rootNode));
// 第二步:递归重构树形,只保留标记过的节点,保持层级
const rebuildTree = (node) => {
// 若当前节点无需保留直接返回null
if (!keepIdSet.has(node.id)) {
return null;
}
// 深拷贝当前节点,避免修改原数据
const newNode = { ...node };
// 递归处理子节点,过滤掉无需保留的,只保留有效子节点
if (newNode.children && newNode.children.length) {
const newChildren = newNode.children.map(child => rebuildTree(child)).filter(Boolean);
newNode.children = newChildren;
} else {
newNode.children = [];
}
return newNode;
};
// 第二步:递归重构树形,只保留标记过的节点,保持层级
const rebuildTree = (node) => {
// 若当前节点无需保留直接返回null
if (!keepIdSet.has(node.id)) {
return null;
}
// 深拷贝当前节点,避免修改原数据
const newNode = { ...node };
// 递归处理子节点,过滤掉无需保留的,只保留有效子节点
if (newNode.children && newNode.children.length) {
const newChildren = newNode.children.map(child => rebuildTree(child)).filter(Boolean);
newNode.children = newChildren;
} else {
newNode.children = [];
}
return newNode;
};
// 重构根节点过滤掉null的根节点
const filteredTree = treeData.map(rootNode => rebuildTree(rootNode)).filter(Boolean);
return filteredTree;
// 重构根节点过滤掉null的根节点
const filteredTree = treeData.map(rootNode => rebuildTree(rootNode)).filter(Boolean);
return filteredTree;
}
@@ -429,7 +445,7 @@ const makeUp = () => {
.getCheckboxRecords()
.filter((item: any) => item.level == '6') || []
if (list.length == 0) {
return ElMessage({
@@ -468,24 +484,29 @@ const makeUpSubmit = () => {
reCallStartTime: timeData.value[0]
}
socket(form)
timePopUp.value = false
logPopUp.value = true
setTimeout(() => {
socket(form)
timePopUp.value = false
logPopUp.value = true
}, 500)
}
const socket = async (form: any) => {
const url = (localStorage.getItem('WebSocketUrl2') || 'null')//'ws://192.168.1.67:10405/api/recell/')
const url = (localStorage.getItem('WebSocketUrl2') || 'null')//'ws://192.168.1.68:10405/api/recell/')
logList.value = []
await dataSocket.socketServe.connect(`${url}${adminInfo.id}`)
await dataSocket.socketServe.send(form)
setTimeout(() => {
dataSocket.socketServe.send(form)
}, 500)
logList.value.push({
type: '',
time: formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss'),
time: formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss'),
name: '开始补召,请稍等...',
})
await dataSocket.socketServe.registerCallBack('message', (res: any) => {
if (res.code == undefined) return
logList.value.push({
type: res.code == 500 ? 'error' : '',
time: formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss'),
time: formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss'),
name: res.message
})

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader date-picker area showExport>
<TableHeader date-picker area showExport showCustomColumn>
<template #select>
<el-form-item label="统计类型" v-if="false">
<el-select

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader date-picker area showExport>
<TableHeader date-picker area showExport showCustomColumn>
<template #select>
<el-form-item label="统计类型:" v-if="false">
<el-select

View File

@@ -1,7 +1,7 @@
<template>
<div class="default-main">
<TableHeader date-picker area showExport>
<TableHeader date-picker area showExport showCustomColumn>
<template #select>
<!-- <el-form-item label="统计类型:">
<el-select v-model="tableStore.table.params.statisticalType" placeholder="请选择统计类型" value-key="id">

View File

@@ -5,7 +5,7 @@
用采数据列表
<back-component />
</div>
<TableHeader :showReset="false" ref="TableHeaderRef">
<TableHeader :showReset="false" ref="TableHeaderRef" showCustomColumn>
<template #select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入关键字" maxlength="32" show-word-limit/>
@@ -63,6 +63,10 @@ const tableStore = new TableStore({
method: 'POST',
publicHeight: 52,
column: [
{ title: '序号', field: 'index', width: 80, formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '表名', field: 'name' },
{ title: '起始时间', field: 'startTime' },
{ title: '截止时间', field: 'endTime' },

View File

@@ -1,6 +1,6 @@
<template>
<el-dialog v-model="dialogVisible" draggable title="完整性不足详情" width="1000">
<TableHeader :showReset="false" ref="TableHeaderRef">
<TableHeader :showReset="false" ref="TableHeaderRef" showCustomColumn>
<template #select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入关键字" />
@@ -27,6 +27,10 @@ const tableStore = new TableStore({
method: 'POST',
publicHeight: 547,
column: [
{ title: '序号', field: 'index', width: 80, formatter: (row: any) => {
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ title: '数据名', field: 'name' },
{ title: '用户名', field: 'userName' },
{ title: '测量点局号', field: 'lineNo' },

View File

@@ -1,7 +1,7 @@
<template>
<div class="default-main">
<!-- 模版 -->
<TableHeader datePicker showExport :showReset="false" ref="TableHeaderRef">
<TableHeader datePicker showExport :showReset="false" ref="TableHeaderRef" showCustomColumn>
<template #select>
<!-- <el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入关键字" />

View File

@@ -1,7 +1,7 @@
<template>
<div class="default-main">
<TableHeader datePicker area showExport>
<TableHeader datePicker area showExport showCustomColumn>
<template #select>
<!-- <el-form-item label="统计类型:">
<el-select v-model="tableStore.table.params.statisticalType" placeholder="请选择统计类型" value-key="id">
@@ -67,13 +67,13 @@ const tableStore = new TableStore({
column: [
{ field: 'index', title: '序号', width: '80', formatter: (row: any) => { return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1 } },
{ field: 'provinceCompany', title: '地级', minWidth: "150px", },
{ field: 'cityCompany', title: '供电公司', minWidth: "150px", },
{ field: 'provinceCompany', title: '地级', minWidth: "150px", },
/* { field: 'cityCompany', title: '供电公司', minWidth: "150px", },*/
{ field: 'subName', title: '变电站', minWidth: "150px", },
{ field: 'lineScale', title: '电压等级', minWidth: "120px", },
{ field: 'lineName', title: '监测点名称', minWidth: "150px", },
{ field: 'loadType', title: '干扰源类型', minWidth: "150px", },
{ field: 'lineObjectName', title: '监测点对象名称', minWidth: "180px", formatter: (row: any) => { return row.cellValue == null ? '/' : row.cellValue } },
{ field: 'lineName', title: '监测点名称', minWidth: "150px", },
{ field: 'lineScale', title: '电压等级', minWidth: "120px", },
{ field: 'loadType', title: '干扰源类型', minWidth: "150px", },
{ field: 'overDay', title: '超标天数', minWidth: "80px", },
{ field: 'freqOverDay', title: '频率偏差', minWidth: "100px", },
{ field: 'volDevOverDay', title: '电压偏差', minWidth: "100px", },

View File

@@ -1,6 +1,6 @@
<template>
<div>
<TableHeader datePicker area ref="TableHeaderRef" showExport>
<TableHeader datePicker area ref="TableHeaderRef" showExport showCustomColumn>
<template #select>
<el-form-item label="统计类型">
<el-select

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader datePicker area showExport>
<TableHeader datePicker area showExport showCustomColumn>
<template #select>
<el-form-item label="统计类型">
<el-select

View File

@@ -12,6 +12,7 @@
</el-form-item>
</template>
<template v-slot:operation>
<el-button type="primary" icon="el-icon-Search" @click="Search">查询</el-button>
<el-button type="primary" icon="el-icon-Download" @click="exportTemplate">导出</el-button>
</template>
</TableHeader>
@@ -51,6 +52,7 @@
:interval="datePickerRef?.interval"
:w="item.w"
:h="item.h"
:searchKey="searchKey"
/>
<div v-else class="pd10">组件加载失败...</div>
</div>
@@ -114,6 +116,7 @@ const layout: any = ref([
// path: '/src/views/pqs/runManage/assessment/components/uese/index.vue'
// },
])
const searchKey = ref(0)
const layoutCopy: any = ref([])
const flag = ref(true)
// 组件映射
@@ -293,6 +296,10 @@ const fetchLayoutData = async () => {
// 可以添加错误提示逻辑
}
}
const Search = () => {
searchKey.value += 1
}
// 导出
const exportTemplate = () => {
console.log(123)

View File

@@ -159,18 +159,19 @@
</template>
</vxe-column>
<vxe-column field="cit" title="所在地市" minWidth="110px"></vxe-column>
<vxe-column field="company" title="供电公司" minWidth="110px"></vxe-column>
<vxe-column v-if="!IS_LN" field="company" title="供电公司" minWidth="110px"></vxe-column>
<vxe-column field="subStation" title="变电站" minWidth="110px"></vxe-column>
<vxe-column field="manufacturer" title="终端厂家" minWidth="110px"></vxe-column>
<vxe-column field="deviceName" title="终端名称" minWidth="130px"></vxe-column>
<vxe-column field="ip" title="终端IP" :formatter="formatter" width="120px"></vxe-column>
<vxe-column v-if="VITE_FLAG" field="objName" title="监测对象" minWidth="130px"></vxe-column>
<vxe-column
field="lineName"
title="监测点名称"
:formatter="formatter"
minWidth="130px"
></vxe-column>
<vxe-column v-if="VITE_FLAG" field="objName" title="监测对象" minWidth="130px"></vxe-column>
<vxe-column field="runFlag" title="运行状态" width="90px">
<template #default="{ row }">
<el-tag
@@ -211,14 +212,14 @@
<script setup lang="ts">
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { onMounted, provide, ref, watch } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { getMonitorVerifyDay } from '@/api/device-boot/dataVerify'
const IS_LN = import.meta.env.VITE_NAME == 'LN' || import.meta.env.VITE_NAME == 'LNqr'
defineOptions({
name: 'harmonic-boot/harmonic/getIntegrityData'
})
@@ -338,21 +339,19 @@ const echart = () => {
z: 0,
zlevel: 0,
itemStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 1,
color: '#FF9100' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 1,
color: '#FF9100' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
}
},
@@ -370,34 +369,30 @@ const echart = () => {
z: 0,
zlevel: 0,
itemStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: '#57bc6e' // 0% 处的颜色
},
{
offset: 1,
color: '#57bc6e' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: '#57bc6e' // 0% 处的颜色
},
{
offset: 1,
color: '#57bc6e' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
}
},
{
type: 'pictorialBar',
itemStyle: {
normal: {
color: '#fff'
}
color: '#fff'
},
symbolRepeat: 50,
// symbolMargin: 300,
@@ -418,11 +413,9 @@ const echart = () => {
barWidth: 18,
itemStyle: {
normal: {
color: 'transparent',
barBorderColor: 'rgb(148,217,249)',
barBorderWidth: 1
}
color: 'transparent',
borderColor: 'rgb(148,217,249)',
borderWidth: 1
},
z: 2
}

View File

@@ -1,5 +1,5 @@
<template>
<TableHeader area datePicker ref="TableHeaderRef">
<TableHeader area datePicker ref="TableHeaderRef" showCustomColumn>
<template #select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.filterName" @keyup="searchEvent" placeholder="输入关键字筛选" />

View File

@@ -157,12 +157,12 @@
</template>
</vxe-column>
<vxe-column field="cit" title="所在地市" width="110px"></vxe-column>
<vxe-column field="company" title="供电公司" minWidth="110px"></vxe-column>
<vxe-column field="subStation" title="变电站" minWidth="110px"></vxe-column>
<vxe-column field="manufacturer" title="终端厂家" minWidth="110px"></vxe-column>
<vxe-column field="deviceName" title="终端名称" minWidth="130px"></vxe-column>
<vxe-column v-if="!IS_LN" field="company" title="供电公司" minWidth="100px"></vxe-column>
<vxe-column field="subStation" title="变电站" minWidth="100px"></vxe-column>
<vxe-column field="manufacturer" title="终端厂家" minWidth="90px"></vxe-column>
<vxe-column field="deviceName" title="终端名称" minWidth="100px"></vxe-column>
<vxe-column field="ip" title="终端IP" :formatter="formatter" width="120px"></vxe-column>
<vxe-column field="timeID" title="最新数据时间" :formatter="formatter" width="160px"></vxe-column>
<vxe-column field="runFlag" title="运行状态" width="90px">
<template #default="{ row }">
<el-tag
@@ -182,6 +182,19 @@
</el-tag>
</template>
</vxe-column>
<vxe-column field="comFlag" title="通讯状态" width="90px">
<template #default="{ row }">
<el-tag
:type="
row.comFlag == '正常'
? 'success'
: 'danger'
"
>
{{ row.comFlag }}
</el-tag>
</template>
</vxe-column>
<vxe-column field="onlineRate" title="在线率(%)" width="90px"></vxe-column>
</vxe-table>
</div>
@@ -201,16 +214,14 @@
</template>
<script setup lang="ts">
// import '@/assets/font/iconfont.css'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { onMounted, provide, ref } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { getMonitorVerifyDay } from '@/api/device-boot/dataVerify'
const IS_LN = import.meta.env.VITE_NAME == 'LN' || import.meta.env.VITE_NAME == 'LNqr'
defineOptions({
name: 'harmonic-boot/harmonic/getIntegrityData'
})
@@ -335,21 +346,19 @@ const echart = () => {
z: 0,
zlevel: 0,
itemStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 1,
color: '#FF9100' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 1,
color: '#FF9100' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
}
},
@@ -361,34 +370,30 @@ const echart = () => {
z: 0,
zlevel: 0,
itemStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: '#57bc6e' // 0% 处的颜色
},
{
offset: 1,
color: '#57bc6e' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: '#57bc6e' // 0% 处的颜色
},
{
offset: 1,
color: '#57bc6e' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
}
},
{
type: 'pictorialBar',
itemStyle: {
normal: {
color: '#fff'
}
color: '#fff'
},
symbolRepeat: 50,
// symbolMargin: 300,
@@ -409,11 +414,9 @@ const echart = () => {
barWidth: 18,
itemStyle: {
normal: {
color: 'transparent',
barBorderColor: 'rgb(148,217,249)',
barBorderWidth: 1
}
color: 'transparent',
borderColor: 'rgb(148,217,249)',
borderWidth: 1
},
z: 2
}

View File

@@ -1,6 +1,6 @@
<template>
<div>
<TableHeader datePicker ref="TableHeaderRef">
<TableHeader datePicker ref="TableHeaderRef" >
<template v-slot:select>
<el-form-item label="超标指标">
<el-select

View File

@@ -29,7 +29,7 @@
<el-tab-pane label="稳态指标合格率" name="2" lazy v-if="!isReload">
<Wentaizhibiaohegelv v-if="activeName == '2'" />
</el-tab-pane>
<el-tab-pane label="稳态数据分析" name="3" lazy v-if="!isReload">
<el-tab-pane label="稳态数据分析" name="3" lazy v-if="!isReload && !IS_LNQR">
<Wentaishujufenxi v-if="activeName == '3'" />
</el-tab-pane>
<el-tab-pane label="谐波频谱" name="4" lazy v-if="!isReload">
@@ -38,10 +38,10 @@
<el-tab-pane label="告警数据统计" name="5" lazy v-if="!isReload && VITE_FLAG">
<Gaojingshujutongji v-if="activeName == '5'" />
</el-tab-pane>
<el-tab-pane label="监测点运行状态" name="6" lazy v-if="!isReload">
<el-tab-pane label="监测点运行状态" name="6" lazy v-if="!isReload && !VITE_FLAG1 && !IS_LNQR">
<Yunxingzhuangtai v-if="activeName == '6'" />
</el-tab-pane>
<el-tab-pane label="实时数据" name="7" lazy v-if="!isReload && !VITE_FLAG1">
<el-tab-pane label="实时数据" name="7" lazy v-if="!isReload && !VITE_FLAG1 && !IS_LNQR">
<Shishishuju v-if="activeName == '7'" />
</el-tab-pane>
<el-tab-pane label="统计报表" name="8" lazy v-if="!isReload && VITE_FLAG">
@@ -77,6 +77,8 @@ import { useRoute } from 'vue-router'
import StatisticalReport from './statisticalReport/index.vue'
const VITE_FLAG = import.meta.env.VITE_NAME == 'jibei'
const VITE_FLAG1 = import.meta.env.VITE_NAME == 'hainan'
// 环境标识
const IS_LNQR = import.meta.env.VITE_NAME === 'LNqr'
import router from '@/router'
import { useMonitoringPoint } from '@/stores/monitoringPoint'
import { id } from 'element-plus/es/locale'

View File

@@ -1,7 +1,7 @@
<!--业务用户管理界面-->
<template>
<div class="default-main">
<TableHeader datePicker area showExport>
<TableHeader datePicker area showExport showCustomColumn>
<template v-slot:select>
<el-form-item label="筛选数据">
<el-input

View File

@@ -11,11 +11,12 @@
<pane style="background: #fff" :style="height">
<TableHeader ref="TableHeaderRef" datePicker :show-search="false">
<template #operation>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent" :loading="loading">
<el-button icon="el-icon-Download" type="primary" @click="openDialog" :loading="loading">
生成
</el-button>
</template>
</TableHeader>
<div class="box">
<div id="luckysheet">
<img
@@ -25,13 +26,46 @@
/>
</div>
</div>
<!-- 电压等级选择弹窗 -->
<el-dialog
v-model="dialogVisible"
title="选择电压等级"
width="420px"
:close-on-click-modal="false"
>
<el-form label-width="100px" style="padding:10px 0;">
<el-form-item label="电压等级">
<el-select
v-model="voltageLevel"
multiple
collapse-tags
clearable
value-key="id"
placeholder="请选择电压等级"
style="width: 260px;"
>
<el-option
v-for="item in voltageleveloption"
:key="item.id"
:label="item.name"
:value="item"
></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmExport" :loading="loading">确定并生成</el-button>
</template>
</el-dialog>
</pane>
</splitpanes>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue'
import { onMounted, ref, provide, watch } from 'vue'
import 'splitpanes/dist/splitpanes.css'
import { Splitpanes, Pane } from 'splitpanes'
import TableStore from '@/utils/tableStore'
@@ -40,8 +74,7 @@ import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { areaHarmonicReport } from '@/api/process-boot/reportForms'
import { genFileId, ElMessage, ElNotification } from 'element-plus'
import type { UploadProps, UploadUserFile } from 'element-plus'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'
defineOptions({
@@ -53,7 +86,11 @@ const loading = ref(false)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
const Template: any = ref({})
const voltageleveloption = dictData.getBasicData('Dev_Voltage_Stand') || []
const voltageLevel: any = ref([])
// 弹窗状态
const dialogVisible = ref(false)
const tableStore = new TableStore({
url: '',
@@ -64,9 +101,15 @@ const tableStore = new TableStore({
})
provide('tableStore', tableStore)
// 弹窗打开时默认全选所有电压等级
watch(dialogVisible, (val) => {
if (val && voltageleveloption.length) {
voltageLevel.value = [...voltageleveloption]
}
})
onMounted(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom) {
size.value = Math.round((180 / dom.offsetHeight) * 120)
}
@@ -76,45 +119,62 @@ const handleNodeClick = (data: any, node: any) => {
dotList.value = data
}
// 生成
const exportEvent = () => {
// 打开弹窗
const openDialog = () => {
if (!dotList.value?.id) {
ElMessage.warning('请先在左侧选择站点/区域')
return
}
dialogVisible.value = true
}
// 确认生成
const confirmExport = async () => {
loading.value = true
ElMessage('生成报告中...')
areaHarmonicReport({
deptId: dotList.value.id,
areaReportFlag: 1,
startTime: TableHeaderRef.value.datePickerRef.timeValue[0],
endTime: TableHeaderRef.value.datePickerRef.timeValue[1]
})
.then((res: any) => {
if (res == undefined) {
loading.value = false
return
}
let blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8'
})
// createObjectURL(blob); //创建下载的链接
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a') // 创建a标签
link.href = url
link.download = dotList.value.name + '区域稳态报告' + dayjs().format('YYYYMMDD') // 设置下载的文件名
document.body.appendChild(link)
link.click() //执行下载
document.body.removeChild(link)
loading.value = false
try {
// 选中数据转为id数组空数组代表导出全部
const voltageIds = voltageLevel.value.map((item: any) => item.id)
const res: any = await areaHarmonicReport({
deptId: dotList.value.id,
areaReportFlag: 1,
startTime: TableHeaderRef.value.datePickerRef.timeValue[0],
endTime: TableHeaderRef.value.datePickerRef.timeValue[1],
voltageIds
})
.catch(() => {
if (!res) {
loading.value = false
return
}
const blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8'
})
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = dotList.value.name + '区域稳态报告' + dayjs().format('YYYYMMDD')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
ElMessage.success('生成成功')
dialogVisible.value = false
} catch (e) {
ElMessage.error('生成失败,请重试')
} finally {
loading.value = false
}
}
</script>
<style lang="scss">
.splitpanes.default-theme .splitpanes__pane {
background: #eaeef1;
}
.box {
padding: 10px;
}

View File

@@ -10,8 +10,14 @@
></PointTree>
</pane>
<pane style="background: #fff" :style="height">
<TableHeader ref="TableHeaderRef" datePicker @selectChange="selectChange">
<TableHeader ref="TableHeaderRef" datePicker :isStatisticData="isStatisticData" @selectChange="selectChange">
<template v-slot:select>
<el-form-item >
<el-radio-group v-model="isStatisticData" @change="onStatisticDataChange">
<el-radio-button value="0">统计数据</el-radio-button>
<el-radio-button value="1">分钟数据</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="模板策略">
<el-select v-model="Template" @change="changetype" placeholder="请选择模版" value-key="id">
<el-option
@@ -22,22 +28,10 @@
></el-option>
</el-select>
</el-form-item>
<el-form-item label="报表类型">
<!-- <el-form-item label="报表类型">
<el-input readonly type="text" value="分析报表"></el-input>
<!-- <el-select-->
<!-- :disabled="true"-->
<!-- v-model="reportForm"-->
<!-- :popper-append-to-body="false"-->
<!-- placeholder="请选择报表类型"-->
<!-- >-->
<!-- <el-option-->
<!-- v-for="item in reportFormList"-->
<!-- :key="item.value"-->
<!-- :label="item.label"-->
<!-- :value="item.value"-->
<!-- ></el-option>-->
<!-- </el-select>-->
</el-form-item>
</el-form-item> -->
</template>
<template #operation>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent">导出excel</el-button>
@@ -85,6 +79,7 @@ const dotList: any = ref({})
const Template: any = ref({})
const reportForm: any = ref('')
const name = ref('')
const isStatisticData = ref('0')
const templatePolicy: any = ref([])
const reportFormList: any = ref([
{
@@ -108,6 +103,11 @@ const tableStore = new TableStore({
tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id
name.value = dotList.value.name
if (Number(isStatisticData.value) === 1) {
tableStore.table.params.isStatisticData = 1
} else {
delete tableStore.table.params.isStatisticData
}
},
loadCallback: () => {
tableStore.table.data.forEach((item: any) => {
@@ -160,6 +160,12 @@ getTemplateByDept({ id: dictData.state.area[0].id })
const changetype = (val: any) => {
reportForm.value = val.reportForm
}
const onStatisticDataChange = () => {
TableHeaderRef.value?.setTheDate(3)
if (dotList.value?.id) {
}
}
const selectChange = () => {
//console.log('🚀 ~ selectChange ~ tableStore.table.data.lnegth :', tableStore.table.data.length)
if (tableStore.table.data.length != 0) {

View File

@@ -1,48 +1,49 @@
<template>
<div class="default-main">
<TableHeader area ref="TableHeaderRef" showExport>
<TableHeader area ref="TableHeaderRef" showExport showCustomColumn>
<template #select>
<el-form-item label="运行状态">
<el-select filterable multiple collapse-tags v-model="tableStore.table.params.runFlag" clearable
placeholder="请选择运行状态">
placeholder="请选择运行状态">
<el-option v-for="item in runFlagList" :key="item.id" :label="item.name"
:value="item.id"></el-option>
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="通讯状态">
<el-select v-model="tableStore.table.params.comFlag" filterable multiple collapse-tags clearable
placeholder="请选择通讯状态">
placeholder="请选择通讯状态">
<el-option v-for="item in communicationstatus" :key="item.value" :label="item.label"
:value="item.value"></el-option>
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="电压等级">
<el-select v-model="tableStore.table.params.scale" filterable multiple collapse-tags clearable
placeholder="请选择电压等级" value-key="id">
placeholder="请选择电压等级" value-key="id">
<el-option v-for="item in voltageleveloption" :key="item.id" :label="item.name"
:value="item"></el-option>
:value="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="终端厂家">
<el-select v-model="tableStore.table.params.manufacturer" filterable multiple collapse-tags
clearable placeholder="请选择终端厂家" value-key="id">
clearable placeholder="请选择终端厂家" value-key="id">
<el-option v-for="item in terminaloption" :key="item.id" :label="item.name"
:value="item"></el-option>
:value="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="干扰源类型">
<el-select v-model="tableStore.table.params.loadType" filterable multiple collapse-tags clearable
placeholder="请选择干扰源类型" value-key="id">
placeholder="请选择干扰源类型" value-key="id">
<el-option v-for="item in interfereoption" :key="item.id" :label="item.name"
:value="item"></el-option>
:value="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="数据筛选">
<el-input style="width: 240px" placeholder="电站名称,终端编号,监测点名称、电压等级、终端厂家、干扰源类型"
v-model="tableStore.table.params.searchValue" clearable maxlength="32"
show-word-limit></el-input>
<el-input style="width: 240px"
placeholder="电站名称,终端编号,监测点名称、电压等级、终端厂家、干扰源类型"
v-model="tableStore.table.params.searchValue" clearable maxlength="32"
show-word-limit></el-input>
</el-form-item>
</template>
@@ -51,6 +52,9 @@
</template>
</TableHeader>
<Table ref="tableRef" />
<!-- 限值查看弹框 -->
<LimitDialog ref="limitDialogRef" />
</div>
</template>
<script setup lang="ts">
@@ -58,14 +62,16 @@ import { ref, onMounted, provide, nextTick, watch } from 'vue'
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import LimitDialog from './monitorLimit.vue'
import { useDictData } from '@/stores/dictData'
const dictData = useDictData()
const TableHeaderRef = ref()
const areaOptionList = dictData.getBasicData('jibei_area')
const IS_LN = import.meta.env.VITE_NAME == 'LN' || import.meta.env.VITE_NAME == 'LNqr'
const interfereoption = dictData.getBasicData('Interference_Source')
const terminaloption = dictData.getBasicData('Dev_Manufacturers')
const voltageleveloption = dictData.getBasicData('Dev_Voltage_Stand')
const limitDialogRef = ref()
const tableStore = new TableStore({
url: '/device-boot/runManage/getLineLedgerComm',
method: 'POST',
@@ -79,13 +85,22 @@ const tableStore = new TableStore({
}
},
{
field: 'areaName',
title: '省公司',
minWidth: 100
},
{
visible: !IS_LN,
field: 'areaName',
title: '省公司',
minWidth: 100
},
{ field: 'gdName', title: '市公司', minWidth: 150 },
{ visible: IS_LN,
field: 'gdName', title: '市公司', minWidth: 150, formatter: (row: any) => {
if (IS_LN) {
return row.cellValue.replace('国网', '').replace('供电公司', '')
} else {
return row.cellValue
}
}
},
{
field: 'bdName',
title: '所属变电站',
@@ -100,7 +115,7 @@ const tableStore = new TableStore({
}
},
{ field: 'lineName', title: '监测点名称', minWidth: 130 },
{ field: 'scale', title: '监测点电压等级', minWidth: 120 },
{ field: 'scale', title: '监测点电压等级', minWidth: 80 },
{ field: 'loadType', title: '干扰源类型', minWidth: 120 },
@@ -139,52 +154,53 @@ const tableStore = new TableStore({
{
field: 'shortCapacity',
title: '最小短路容量(MVA)',
minWidth: 150
minWidth: 100
},
{
field: 'devCapacity',
title: '供电设备容量(MVA )',
minWidth: 160
minWidth: 100
},
{
field: 'dealCapacity',
title: '用户协议容量(MVA)',
minWidth: 150
minWidth: 100
},
{ field: 'id', title: '监测点序号', minWidth: 90 },
{ field: 'id', title: '监测点序号', minWidth: 70 },
{ field: 'devName', title: '监测终端编号 ', minWidth: 140 },
{ field: 'ptType', title: '监测终端接线方式', minWidth: 140 },
{
field: 'voltageDev',
title: '电压偏差上限(%)',
minWidth: 140
minWidth: 80
},
{
field: 'uvoltageDev',
title: '电压偏差下限(%)',
minWidth: 140
}
minWidth: 80
},
/* {
title: '操作',fixed: 'right',
minWidth: 150,
{
title: '操作',
minWidth: 80,
fixed: 'right',
render: 'buttons',
buttons: [
{
name: 'productSetting',
title: '流程详情',
title: '查看限值',
type: 'primary',
icon: 'el-icon-EditPen',
render: 'basicButton',
click: row => {
// 打开限值查看弹框
limitDialogRef.value?.open(row)
}
}
]
}*/
}
],
beforeSearchFun: () => {
@@ -209,7 +225,7 @@ tableStore.table.params.loadType = []
const runFlagList = [
{ id: 0, name: '投运' },
{ id: 1, name: '检修' },
{ id: 2, name: '停运' },
{ id: 2, name: '停运' }
]
@@ -224,5 +240,4 @@ onMounted(() => {
})
</script>

View File

@@ -0,0 +1,163 @@
<template>
<el-dialog
v-model="dialogVisible"
title="限值查看"
width="800px"
:close-on-click-modal="false"
destroy-on-close
>
<!-- 基本信息 -->
<div class="basic-info" style="margin-bottom: 20px; padding: 16px; background: #f5f7fa; border-radius: 4px;">
<el-row :gutter="20">
<el-col :span="8">
<div><strong>监测点名称</strong>{{ currentRow?.lineName || '-' }}</div>
</el-col>
<el-col :span="8">
<div><strong>监测对象名称</strong>{{ currentRow?.objName || '-' }}</div>
</el-col>
<el-col :span="8">
<div><strong>所属变电站</strong>{{ currentRow?.bdName || '-' }}</div>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="8">
<div><strong>电压等级</strong>{{ currentRow?.scale || '-' }}</div>
</el-col>
<el-col :span="8">
<div><strong>干扰源类型</strong>{{ currentRow?.loadType || '-' }}</div>
</el-col>
<el-col :span="8">
<div><strong>终端厂家</strong>{{ currentRow?.manufacturer || '-' }}</div>
</el-col>
</el-row>
</div>
<el-tabs v-model="activeTab">
<el-tab-pane label="电能质量限值" name="powerQuality">
<el-table :data="limitData" border style="width: 100%" max-height="400">
<el-table-column prop="name" label="参数名称" width="300" fixed />
<el-table-column prop="value" label="限值" width="300">
<template #default="{ row }">
<span>{{ row.value !== null && row.value !== undefined ? row.value + '%' : '-' }}</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="谐波电压限值" name="harmonicVoltage">
<el-table :data="harmonicVoltageData" border style="width: 100%" max-height="400">
<el-table-column prop="harmonic" label="谐波次数" width="300" />
<el-table-column prop="limit" label="限值(%)" width="300">
<template #default="{ row }">
<span>{{ row.limit || '-' }}</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="谐波电流限值" name="harmonicCurrent">
<el-table :data="harmonicCurrentData" border style="width: 100%" max-height="400">
<el-table-column prop="harmonic" label="谐波次数" width="300" />
<el-table-column prop="limit" label="限值(%)" width="300">
<template #default="{ row }">
<span>{{ row.limit || '-' }}</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const dialogVisible = ref(false)
const activeTab = ref('powerQuality')
const currentRow = ref<any>(null)
// 电能质量限值数据
const limitData = ref([
{ name: '频率偏差', value: 0.2 },
{ name: '电压偏差上限', value: null },
{ name: '电压偏差下限', value: null },
{ name: '电压不平衡度', value: 2 },
{ name: '闪变', value: 1 },
{ name: '电压波变', value: 3 },
{ name: '间谐波电压', value: 0.16 },
{ name: '负序电流', value: null },
])
// 谐波电压数据2-25次
const harmonicVoltageData = ref(
Array.from({ length: 24 }, (_, i) => {
const harmonicNumber = i + 2
let limit = 0
if (harmonicNumber <= 3) limit = 2.4
else if (harmonicNumber <= 5) limit = 1.2
else if (harmonicNumber <= 7) limit = 0.8
else if (harmonicNumber <= 9) limit = 0.6
else if (harmonicNumber <= 11) limit = 0.5
else if (harmonicNumber <= 13) limit = 0.4
else if (harmonicNumber <= 15) limit = 0.3
else if (harmonicNumber <= 17) limit = 0.2
else if (harmonicNumber <= 19) limit = 0.18
else limit = 0.15
return {
harmonic: `${harmonicNumber}`,
limit: limit
}
})
)
// 谐波电流数据2-25次
const harmonicCurrentData = ref(
Array.from({ length: 24 }, (_, i) => {
const harmonicNumber = i + 2
const limits = {
2: 16, 3: 13, 4: 8.1, 5: 13, 6: 5.4, 7: 9.3, 8: 4.1, 9: 4.3,
10: 3.3, 11: 5.9, 12: 2.7, 13: 5, 14: 2.3, 15: 2.6, 16: 2, 17: 3.8,
18: 1.8, 19: 3.4, 20: 1.6, 21: 1.9, 22: 1.5, 23: 2.8, 24: 1.4, 25: 2.6
}
return {
harmonic: `${harmonicNumber}`,
limit: limits[harmonicNumber as keyof typeof limits] || 0
}
})
)
// 打开弹框
const open = (row: any) => {
currentRow.value = row
// 更新电能质量限值数据
limitData.value.forEach(item => {
if (item.name === '电压偏差上限') {
item.value = row.voltageDev !== undefined ? row.voltageDev : null
} else if (item.name === '电压偏差下限') {
item.value = row.uvoltageDev !== undefined ? row.uvoltageDev : null
} else if (item.name === '负序电流') {
item.value = row.ineg !== undefined ? row.ineg : null
}
})
dialogVisible.value = true
}
defineExpose({
open
})
</script>
<style scoped lang="scss">
.dialog-footer {
display: flex;
justify-content: flex-end;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main">
<TableHeader area ref="TableHeaderRef" showExport>
<TableHeader area ref="TableHeaderRef" showExport showCustomColumn>
<template #select>
<!-- <el-form-item label="统计类型:">
<el-select v-model="tableStore.table.params.statisticalType" placeholder="请选择统计类型" value-key="id">
@@ -128,6 +128,7 @@ const classificationData = dictData.getBasicData('Statistical_Type', ['Report_Ty
const voltageleveloption = dictData.getBasicData('Dev_Voltage_Stand')
const terminaloption = dictData.getBasicData('Dev_Manufacturers')
const interfereoption = dictData.getBasicData('Interference_Source')
const IS_LN = import.meta.env.VITE_NAME == 'LN' || import.meta.env.VITE_NAME == 'LNqr'
const communicationstatus = [
{ value: 0, label: '中断' },
@@ -150,8 +151,21 @@ const tableStore = new TableStore({
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
}
},
{ field: 'areaName', title: '区域', minWidth: 90 },
{ field: 'gdName', title: '市公司', minWidth: 110 },
{
visible: !IS_LN,
field: 'areaName',
title: '省公司',
minWidth: 100
},
{ visible: IS_LN,
field: 'gdName', title: '市公司', minWidth: 120, formatter: (row: any) => {
if (IS_LN) {
return row.cellValue.replace('国网', '').replace('供电公司', '')
} else {
return row.cellValue
}
}
},
{
field: 'bdName',
title: '变电站',

View File

@@ -90,6 +90,7 @@
</div>
<div
class="card-Box"
style="max-height: 100px;"
:style="`height:calc((${boxHeight.height} - 50px )/ 3);`"
v-for="val in item.children"
>
@@ -302,13 +303,14 @@ const countList: any = ref([
{
title: '网公司',
children: [
{ title: '非电网侧监测点', num: 0, onLineNum: 0, integrityRate: 0 },
{
title: '电网侧监测点',
num: 0,
onLineNum: 0,
integrityRate: 0
},
{ title: '非电网侧监测点', num: 0, onLineNum: 0, integrityRate: 0 }
]
},
{
@@ -395,8 +397,8 @@ const info = async (row: any) => {
})
// 监测点指标统计
getGridDiagramStatistics(form).then(res => {
countList.value[0].children[0] = { title: '电网侧监测点', ...res.data.data[0] }
countList.value[0].children[1] = { title: '电网侧监测点', ...res.data.data[1] }
countList.value[0].children[0] = { title: '电网侧监测点', ...res.data.data[1] }
countList.value[0].children[1] = { title: '电网侧监测点', ...res.data.data[0] }
countList.value[1].children[0] = { title: '电网侧监测点', ...res.data.gwData[0] }
})
// 监测运行统计

File diff suppressed because it is too large Load Diff

View File

@@ -85,11 +85,7 @@ const active: any = ref(1)
const evaluationData: any = ref([])
const dictData = useDictData()
const Voltage: any = dictData.getBasicData('Dev_Voltage_Stand').filter(item => {
if (item.code == '35kV' || item.code == '500kV' || item.code == '220kV' || item.code == '110kV') {
return item
}
})
const Voltage: any = dictData.getBasicData('Panoramic_voltage')
const chartRef = ref<HTMLDivElement>()
const url: any = [

View File

@@ -3,28 +3,38 @@
<DatePicker ref="datePickerRef" style="display: none" theCurrentTime />
<el-page-header :icon="ArrowLeft" @back="emit('back')">
<template #content>
<span style="font-size: 16px">{{ dropList.lineName }}详情 </span>
<span style="font-weight: 500">最新数据时间</span>
<span style="color: var(--color-primary-default)">{{ dropList.updateTime }}</span>
<span style="">{{ dropList.gdName }}_{{ dropList.bdName }}_{{ dropList.lineName }}</span>
<span style="font-weight: 500" class="ml20">最新数据时间</span>
<span style="color: var(--el-color-primary)">{{ dropList.updateTime }}</span>
<span style="font-weight: 500" class="ml20">统计日期</span>
<span style="color: var(--el-color-primary)">
{{ getTimeOfTheMonth('3')[0] + '至' + getTimeOfTheMonth('3')[1] }}
</span>
</template>
</el-page-header>
<el-row :gutter="20" class="mt10" :style="`height:${rowHeight}`">
<el-col :span="8">
<h3 class="mb10 iconBox">
<h3 class="mb10 iconBox " >
<span></span>
台账信息
</h3>
<el-descriptions title="" border :column="2">
<el-descriptions-item label="监测点名称" width="140px">
<el-descriptions title="" label-width="100px" border :column="2" size="small">
<el-descriptions-item label="变电站名称">
{{ dropList.bdName }}
</el-descriptions-item>
<el-descriptions-item label="监测点名称">
{{ dropList.lineName }}
</el-descriptions-item>
<el-descriptions-item label="监测对象名称">
{{ dropList.objName }}
</el-descriptions-item>
<el-descriptions-item label="所属厂家">{{ dropList.manufacturer }}</el-descriptions-item>
<el-descriptions-item label="电压等级">{{ dropList.scale }}</el-descriptions-item>
<el-descriptions-item label="投运日期">{{ dropList.loginTime }}</el-descriptions-item>
<el-descriptions-item label="PT">{{ dropList.pt }}</el-descriptions-item>
<el-descriptions-item label="CT">{{ dropList.ct }}</el-descriptions-item>
<!-- <el-descriptions-item label="PT">{{ dropList.pt }}</el-descriptions-item>
<el-descriptions-item label="CT">{{ dropList.ct }}</el-descriptions-item> -->
<el-descriptions-item label="干扰源类型">{{ dropList.loadType }}</el-descriptions-item>
<el-descriptions-item label="通讯状态" width="140px">
<el-descriptions-item label="通讯状态">
<el-tag size="small" :type="dropList.comFlag == '正常' ? 'success' : 'danger'" effect="dark">
{{ dropList.comFlag }}
</el-tag>
@@ -32,22 +42,33 @@
</el-descriptions>
</el-col>
<el-col :span="8">
<h3 class="mb10 iconBox">
<h3
class="mb10 iconBox "
>
<span></span>
告警明细 ()
告警天数
</h3>
<el-descriptions title="" border :column="2" size="small">
<el-descriptions-item width="140px" label="告警原因">
<span style="font-weight: 550"
:style="TargetData.info == 0 ? 'color: #0e8780;' : TargetData.info == 3 ? 'color: #000' : 'color: #ff0000;'">
<span
style="font-weight: 550"
:style="
TargetData.info == 0
? 'color: #0e8780;'
: TargetData.info == 3
? 'color: #000'
: 'color: #ff0000;'
"
>
{{
TargetData.info == 1
? '超标告警'
: TargetData.info == 2
? '完整性告警'
: TargetData.info == 0
? '无告警'
: '暂无数据'
? '完整性告警'
: TargetData.info == 0
? '无告警'
: '暂无数据'
}}
</span>
</el-descriptions-item>
@@ -72,72 +93,27 @@
数据质量
</h3>
<div style="display: flex">
<MyEChart :style="`height: calc(${rowHeight} - 31px)`" :options="ComCharts" @click="Integrity" />
<MyEChart :style="`height: calc(${rowHeight} - 31px)`" :options="onLineCharts"
@click="OnlineRate" />
<MyEChart :style="`height: calc(${rowHeight} - 31px)`" :options="ComCharts" />
<MyEChart
:style="`height: calc(${rowHeight} - 31px)`"
:options="onLineCharts"
/>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mt10" :style="`height:${rowHeight}`">
<el-col :span="8">
<h3 class="mb10 iconBox">
<span></span>
监测点总体评价结论
</h3>
<div class="evaluationData">
<el-row style="width: 96%" class="row pb5">
<el-col :span="12" style="display: flex">
<span>{{ dropList.lineName }}</span>
</el-col>
<el-col :span="12" style="display: flex">
<div>
综合评估得分:
<span class="conclusion" :class="dropList.assessData == '特质'
? 'background1'
: dropList.assessData == '较差'
? 'background2'
: dropList.assessData == '极差'
? 'background3'
: ''
">
{{ dropList.assessData }}
</span>
</div>
</el-col>
</el-row>
<el-row style="width: 96%" v-for="(item, i) in evaluationData" class="row pb4 pt3">
<el-col :span="14" style="display: flex; align-items: center">
<img :src="url[i]" />
<span style="line-height: 20px">{{ item.targetName }}</span>
</el-col>
<el-col :span="10" style="display: flex; align-items: center">
<div style="width: 100%">
评估得分
<span class="conclusion" :class="item.avg == '特质'
? 'background1'
: item.avg == '较差'
? 'background2'
: item.avg == '极差'
? 'background3'
: ''
">
{{ item.avg }}
</span>
</div>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="8">
<h3 class="mb10 iconBox">
<h3 class="mb10 iconBox ">
<span></span>
稳态指标合格率
</h3>
<div :style="`height: calc(${rowHeight} - 31px)`" ref="chartRef"></div>
<div :style="`height: calc(${rowHeight} - 31px); `" ref="chartRef"></div>
</el-col>
<el-col :span="8">
<h3 class="mb10 iconBox">
<h3 class="mb10 iconBox" >
<span></span>
暂态事件统计
</h3>
@@ -167,6 +143,7 @@ import DatePicker from '@/components/form/datePicker/index.vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import echarts from '@/components/echarts/echarts'
import { useRouter } from 'vue-router'
import { getTimeOfTheMonth } from '@/utils/formatTime'
const router = useRouter()
import { getLineDetailData } from '@/api/advance-boot/bearingCapacity'
import {
@@ -175,14 +152,14 @@ import {
lineQualifiedDetail,
getLineAssess,
getEventDetailByLineId,
getGridDiagramTargetData
getGridDiagramTargetData,
} from '@/api/device-boot/panorama'
import { formatter } from 'element-plus'
const loading=ref(false)
const emit = defineEmits(['back'])
const dropList: any = ref({})
const TargetData: any = ref({})
const evaluationData: any = ref([])
const incidentCharts: any = ref({})
const ComCharts: any = ref({})
const onLineCharts: any = ref({})
@@ -362,42 +339,8 @@ const open = async (id: string) => {
}
}
})
loading.value = true
// 监测点总体评价结论
getLineAssess({
lineId: id,
startTime: datePickerRef.value.timeValue[0],
endTime: datePickerRef.value.timeValue[1]
}).then((res: any) => {
dropList.value.assessData = res.data.assessLevel
evaluationData.value = [
{
targetName: '频率偏差(Hz)',
avg: res.data.freqAssessLevel,
sd: res.data.freqQualifyData
},
{
targetName: '电压偏差(%)',
avg: res.data.vdevAssessLevel,
sd: res.data.vdevQualifyData
},
{
targetName: '电压总谐波畸变率(%)',
avg: res.data.harmAssessLevel,
sd: res.data.harmQualifyData
},
{
targetName: '三相电压不平衡度(%)',
avg: res.data.unbalanceAssessLevel,
sd: res.data.unbalanceQualifyData
},
{
targetName: '闪变',
avg: res.data.flickerAssessLevel,
sd: res.data.flickerQualifyData
}
]
})
// 稳态指标合格率
lineQualifiedDetail({
@@ -522,7 +465,7 @@ const open = async (id: string) => {
if (res.data[k] != '/') {
judgment = false
}
flag += (res.data[k] == '/' ? 0 : res.data[k])
flag += res.data[k] == '/' ? 0 : res.data[k]
}
}
@@ -633,7 +576,7 @@ const echart = (row: any) => {
},
indicator: indicator
},
color: color,
series: []
}
@@ -642,32 +585,25 @@ const echart = (row: any) => {
name: item.time,
type: 'radar',
symbol: 'none',
areaStyle: {
normal: {
color: '#2a9fe069'
}
opacity: 0.6, // 调整透明度,避免重叠遮挡
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: `${color[i]}50` },
{ offset: 1, color: `${color[i]}50` } // 渐变透明
])
},
itemStyle: {
color: '#2a9fe0'
lineStyle: {
color: color[i],
width: 2
},
data: [item.ratioList]
})
})
// console.log("🚀 ~ echart ~ option:", option)
chart.setOption(option)
}
// 跳转
const Integrity = () => {
router.push({
name: 'harmonic-boot/harmonic/getIntegrityData'
})
}
const OnlineRate = () => {
router.push({
name: 'harmonic-boot/area/OnlineRate'
})
}
onMounted(() => {
// open('4c87b7dff2281254fc55c25a4da31506')
@@ -708,7 +644,7 @@ defineExpose({ open })
.iconBox {
display: flex;
align-items: center;
span {
display: inline-block;
width: 3px;
@@ -737,7 +673,16 @@ defineExpose({ open })
}
.background3 {
background-color: #A52a2a;
background-color: #a52a2a;
color: #fff;
}
:deep(.el-table--small .el-table__cell) {
padding: 2px 0 !important;
}
.text-style {
cursor: pointer;
text-decoration: underline;
text-decoration-color: var(--el-color-primary);
text-underline-offset: 4px;
}
</style>

View File

@@ -0,0 +1,743 @@
<template>
<div class="default-main lineInfo" :style="height">
<DatePicker ref="datePickerRef" style="display: none" theCurrentTime />
<el-page-header :icon="ArrowLeft" @back="emit('back')">
<template #content>
<span style="font-size: 16px">{{ dropList.objName ? dropList.objName + '_' : '' }}{{ dropList.lineName }}详情 </span>
<span style="font-weight: 500">最新数据时间</span>
<span style="color: var(--color-primary-default)">{{ dropList.updateTime }}</span>
</template>
</el-page-header>
<el-row :gutter="20" class="mt10" :style="`height:${rowHeight}`">
<el-col :span="8">
<h3 class="mb10 iconBox">
<span></span>
台账信息
</h3>
<el-descriptions title="" border :column="2">
<el-descriptions-item label="监测点名称" width="140px">
{{ dropList.lineName }}
</el-descriptions-item>
<el-descriptions-item label="所属厂家">{{ dropList.manufacturer }}</el-descriptions-item>
<el-descriptions-item label="电压等级">{{ dropList.scale }}</el-descriptions-item>
<el-descriptions-item label="投运日期">{{ dropList.loginTime }}</el-descriptions-item>
<el-descriptions-item label="PT">{{ dropList.pt }}</el-descriptions-item>
<el-descriptions-item label="CT">{{ dropList.ct }}</el-descriptions-item>
<el-descriptions-item label="干扰源类型">{{ dropList.loadType }}</el-descriptions-item>
<el-descriptions-item label="通讯状态" width="140px">
<el-tag size="small" :type="dropList.comFlag == '正常' ? 'success' : 'danger'" effect="dark">
{{ dropList.comFlag }}
</el-tag>
</el-descriptions-item>
</el-descriptions>
</el-col>
<el-col :span="8">
<h3 class="mb10 iconBox">
<span></span>
告警明细 ()
</h3>
<el-descriptions title="" border :column="2" size="small">
<el-descriptions-item width="140px" label="告警原因">
<span style="font-weight: 550"
:style="TargetData.info == 0 ? 'color: #0e8780;' : TargetData.info == 3 ? 'color: #000' : 'color: #ff0000;'">
{{
TargetData.info == 1
? '超标告警'
: TargetData.info == 2
? '完整性告警'
: TargetData.info == 0
? '无告警'
: '暂无数据'
}}
</span>
</el-descriptions-item>
<el-descriptions-item label="频率偏差">{{ TargetData.freqDevOvertime }}</el-descriptions-item>
<el-descriptions-item label="电压偏差">{{ TargetData.voltageDevOvertime }}</el-descriptions-item>
<el-descriptions-item label="谐波电流">{{ TargetData.iharmOvertime }}</el-descriptions-item>
<el-descriptions-item label="负序电流">{{ TargetData.inegOvertime }}</el-descriptions-item>
<el-descriptions-item label="间谐波电压">{{ TargetData.inuharmOvertime }}</el-descriptions-item>
<el-descriptions-item label="电压总谐波畸变率">
{{ TargetData.uaberranceOvertime }}
</el-descriptions-item>
<el-descriptions-item label="闪变">{{ TargetData.flickerOvertime }}</el-descriptions-item>
<el-descriptions-item label="谐波电压">{{ TargetData.uharmOvertime }}</el-descriptions-item>
<el-descriptions-item width="140px" label="三相电压不平衡度">
{{ TargetData.ubalanceOvertime }}
</el-descriptions-item>
</el-descriptions>
</el-col>
<el-col :span="8">
<h3 class="mb10 iconBox">
<span></span>
数据质量
</h3>
<div style="display: flex">
<MyEChart :style="`height: calc(${rowHeight} - 31px)`" :options="ComCharts" @click="Integrity" />
<MyEChart :style="`height: calc(${rowHeight} - 31px)`" :options="onLineCharts"
@click="OnlineRate" />
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="mt10" :style="`height:${rowHeight}`">
<el-col :span="8">
<h3 class="mb10 iconBox">
<span></span>
监测点总体评价结论
</h3>
<div class="evaluationData">
<el-row style="width: 96%" class="row pb5">
<el-col :span="12" style="display: flex">
<span>{{ dropList.lineName }}</span>
</el-col>
<el-col :span="12" style="display: flex">
<div>
综合评估得分:
<span class="conclusion" :class="dropList.assessData == '特质'
? 'background1'
: dropList.assessData == '较差'
? 'background2'
: dropList.assessData == '极差'
? 'background3'
: ''
">
{{ dropList.assessData }}
</span>
</div>
</el-col>
</el-row>
<el-row style="width: 96%" v-for="(item, i) in evaluationData" class="row pb4 pt3">
<el-col :span="14" style="display: flex; align-items: center">
<img :src="url[i]" />
<span style="line-height: 20px">{{ item.targetName }}</span>
</el-col>
<el-col :span="10" style="display: flex; align-items: center">
<div style="width: 100%">
评估得分
<span class="conclusion" :class="item.avg == '特质'
? 'background1'
: item.avg == '较差'
? 'background2'
: item.avg == '极差'
? 'background3'
: ''
">
{{ item.avg }}
</span>
</div>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="8">
<h3 class="mb10 iconBox">
<span></span>
稳态指标合格率
</h3>
<div :style="`height: calc(${rowHeight} - 31px)`" ref="chartRef"></div>
</el-col>
<el-col :span="8">
<h3 class="mb10 iconBox">
<span></span>
暂态事件统计
</h3>
<MyEChart :style="`height: calc(${rowHeight} - 31px)`" :options="incidentCharts" />
</el-col>
</el-row>
<el-row :gutter="20" class="mt10" :style="`height:${rowHeight}`">
<el-col :span="24">
<h3 class="mb10 iconBox">
<span></span>
历史趋势图
</h3>
<history :lineId="lineId" ref="historyRef" />
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, provide } from 'vue'
import { ArrowLeft } from '@element-plus/icons-vue'
import { mainHeight } from '@/utils/layout'
import history from './history.vue'
import { color } from '@/components/echarts/color'
import DatePicker from '@/components/form/datePicker/index.vue'
import MyEChart from '@/components/echarts/MyEchart.vue'
import echarts from '@/components/echarts/echarts'
import { useRouter } from 'vue-router'
const router = useRouter()
import { getLineDetailData } from '@/api/advance-boot/bearingCapacity'
import {
getTotalIntegrityByLineIds,
getTotalOnlineRates,
lineQualifiedDetail,
getLineAssess,
getEventDetailByLineId,
getGridDiagramTargetData
} from '@/api/device-boot/panorama'
import { formatter } from 'element-plus'
const emit = defineEmits(['back'])
const dropList: any = ref({})
const TargetData: any = ref({})
const evaluationData: any = ref([])
const incidentCharts: any = ref({})
const ComCharts: any = ref({})
const onLineCharts: any = ref({})
const datePickerRef = ref()
const chartRef = ref<HTMLDivElement>()
const historyRef = ref()
const IntegrityNum = ref(0)
const url: any = [
new URL(`@/assets/img/PLPC.png`, import.meta.url),
new URL(`@/assets/img/DYPC.png`, import.meta.url),
new URL(`@/assets/img/JBL.png`, import.meta.url),
new URL(`@/assets/img/SXDY.png`, import.meta.url),
new URL(`@/assets/img/SB.png`, import.meta.url)
]
const lineId: any = ref('')
const height = mainHeight(20)
const rowHeight = mainHeight(190, 3).height
// 查询
const open = async (id: string) => {
let form = {
comFlag: 1,
endTime: datePickerRef.value.timeValue[1],
lineIds: [id],
ids: [id],
searchValue: '',
startTime: datePickerRef.value.timeValue[0]
}
lineId.value = id
const { data } = await getLineDetailData({ id: id })
dropList.value = data
// 运行状态
// 完整性
getTotalIntegrityByLineIds(form).then((res: any) => {
let num = (res.data || 0) / 100
IntegrityNum.value = res.data
ComCharts.value = {
title: {
text: '完整性',
bottom: '10%',
left: 'center',
textStyle: {
fontSize: 16
}
},
xAxis: {
show: false
},
yAxis: {
show: false
},
options: {
dataZoom: null,
series: [
{
//第二个球的填充
type: 'liquidFill',
radius: '65%',
center: ['50%', '40%'], //中心点
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#2aa1e3'
}
],
globalCoord: false
}
],
data: [num, num], // data个数代表波浪数
backgroundStyle: {
borderWidth: 1,
color: '#f7f8fc'
},
label: {
normal: {
formatter: () => {
return (num * 100).toFixed(2) + '%'
},
textStyle: {
fontSize: 20,
color: '#000'
}
}
},
outline: {
// show: false
borderDistance: 0,
itemStyle: {
borderWidth: 2,
borderColor: '#fff'
}
}
}
]
}
}
// 稳态指标合格率
})
// 在线率
getTotalOnlineRates({ ...form, id: dropList.value.devId, ids: [dropList.value.devId] }).then((res: any) => {
let num = (res.data || 0) / 100
onLineCharts.value = {
title: {
text: '在线率',
bottom: '10%',
left: 'center',
textStyle: {
fontSize: 16
}
},
xAxis: {
show: false
},
yAxis: {
show: false
},
options: {
dataZoom: null,
series: [
{
//第二个球的填充
type: 'liquidFill',
radius: '65%',
center: ['50%', '40%'], //中心点
color: [
{
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 1,
color: '#67c23a'
}
],
globalCoord: false
}
],
data: [num, num], // data个数代表波浪数
backgroundStyle: {
color: '#f7f8fc'
},
label: {
normal: {
formatter: () => {
return (num * 100).toFixed(2) + '%'
},
textStyle: {
fontSize: 20,
color: '#000'
}
}
},
outline: {
// show: false
borderDistance: 0,
itemStyle: {
borderWidth: 2,
borderColor: '#fff'
}
}
}
]
}
}
})
// 监测点总体评价结论
getLineAssess({
lineId: id,
startTime: datePickerRef.value.timeValue[0],
endTime: datePickerRef.value.timeValue[1]
}).then((res: any) => {
dropList.value.assessData = res.data.assessLevel
evaluationData.value = [
{
targetName: '频率偏差(Hz)',
avg: res.data.freqAssessLevel,
sd: res.data.freqQualifyData
},
{
targetName: '电压偏差(%)',
avg: res.data.vdevAssessLevel,
sd: res.data.vdevQualifyData
},
{
targetName: '电压总谐波畸变率(%)',
avg: res.data.harmAssessLevel,
sd: res.data.harmQualifyData
},
{
targetName: '三相电压不平衡度(%)',
avg: res.data.unbalanceAssessLevel,
sd: res.data.unbalanceQualifyData
},
{
targetName: '闪变',
avg: res.data.flickerAssessLevel,
sd: res.data.flickerQualifyData
}
]
})
// 稳态指标合格率
lineQualifiedDetail({
lineId: id,
startTime: datePickerRef.value.timeValue[0],
endTime: datePickerRef.value.timeValue[1]
}).then(res => {
echart([
{
time: datePickerRef.value.timeValue[0].substring(0, 7),
targetList: [
'频率偏差(Hz)',
'电压偏差(%)',
'电压总谐波畸变率(%)',
'三相电压不平衡度(%)',
'闪变',
'谐波电压(%)',
'谐波电流(%)',
'间谐波电压(%)',
'负序电流(A)'
],
ratioList: [
res.data.freqDev == 3.14159 ? null : res.data.freqDev,
res.data.vdev == 3.14159 ? null : res.data.vdev,
res.data.vthd == 3.14159 ? null : res.data.vthd,
res.data.ubalance == 3.14159 ? null : res.data.ubalance,
res.data.plt == 3.14159 ? null : res.data.plt,
res.data.uharm == 3.14159 ? null : res.data.uharm,
res.data.iharm == 3.14159 ? null : res.data.iharm,
res.data.inuHarm == 3.14159 ? null : res.data.inuHarm,
res.data.ineg == 3.14159 ? null : res.data.ineg
]
}
])
})
// 暂态事件统计
getEventDetailByLineId({
id: id,
startTime: datePickerRef.value.timeValue[0],
endTime: datePickerRef.value.timeValue[1],
type: datePickerRef.value.interval
}).then((res: any) => {
let data = [
{
name: '电压暂升',
value: res.data?.swellTimes || 0
},
{
name: '电压暂降',
value: res.data?.sagTimes || 0
},
{
name: '电压中断',
value: res.data?.interruptTimes || 0
}
]
incidentCharts.value = {
title: {
text: '总数:' + (data[0].value + data[1].value + data[2].value),
left: '40%',
top: '45%',
textStyle: {
// fontWeight: 600,
fontSize: 12
}
// subtext: '总数',
// subtextStyle: {
// fontWeight: 550,
// fontSize: 14
// }
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
top: 'center',
right: '10%',
formatter: function (e: any) {
return e + ' ' + data.filter(item => item.name == e)[0].value + '条'
}
},
xAxis: {
show: false
},
yAxis: {
show: false
},
options: {
dataZoom: null,
series: [
{
type: 'pie',
center: ['45%', '50%'],
radius: ['60%', '80%'],
label: {
show: false,
position: 'outside',
textStyle: {
//数值样式
}
},
data: data
}
]
}
}
})
// 告警明细
getGridDiagramTargetData({ ...form, id: id, type: 3 }).then(res => {
TargetData.value = res.data
let num = 0
let flag = 0
let judgment = true
for (let k in res.data) {
if (k != 'lineId') {
if (res.data[k] != '/') {
judgment = false
}
flag += (res.data[k] == '/' ? 0 : res.data[k])
}
}
if (IntegrityNum.value == 0) {
num = 2 //完整性告警
} else {
if (flag > 0) {
num = 1 //超标告警
} else {
num = 0 //无告警
}
if (judgment) {
num = 3
}
}
TargetData.value.info = num
})
}
const echart = (row: any) => {
let chart = echarts.init(chartRef.value as HTMLDivElement)
let dataname = [
'频率偏差(Hz)',
'电压偏差(%)',
'电压总谐波畸变率(%)',
'三相电压不平衡度(%)',
'闪变',
'谐波电压(%)',
'谐波电流(%)',
'间谐波电压(%)',
'负序电流(A)'
]
let datamax = [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
let indicator = []
for (let i = 0; i < dataname.length; i++) {
indicator.push({
name: dataname[i],
max: datamax[i]
})
}
let option: any = {
tooltip: {
trigger: 'item',
axisPointer: {
type: 'shadow',
label: {
color: '#fff',
fontSize: 16
}
},
textStyle: {
color: '#fff',
fontStyle: 'normal',
opacity: 0.35,
fontSize: 14
},
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0
},
legend: {
data: row.map((item: any) => item.time),
type: 'scroll',
orient: 'vertical',
icon: 'roundRect',
right: '20',
itemGap: 10,
itemWidth: 16,
itemHeight: 16,
textStyle: {
fontSize: '15',
color: '#656565'
}
},
radar: {
center: ['50%', '50%'],
radius: '65%',
startAngle: 90,
splitNumber: 5,
splitArea: {
areaStyle: {
color: ['#FFFFFF', '#F5F9FF'].reverse()
}
},
axisLabel: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#D2E4F8'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#D2E4F8'
}
},
name: {
formatter: '{value}',
textStyle: {
color: '#656565',
fontSize: 15
}
},
indicator: indicator
},
series: []
}
row.forEach((item: any, i: any) => {
option.series.push({
name: item.time,
type: 'radar',
symbol: 'none',
areaStyle: {
normal: {
color: '#2a9fe069'
}
},
itemStyle: {
color: '#2a9fe0'
},
data: [item.ratioList]
})
})
// console.log("🚀 ~ echart ~ option:", option)
chart.setOption(option)
}
// 跳转
const Integrity = () => {
router.push({
name: 'harmonic-boot/harmonic/getIntegrityData'
})
}
const OnlineRate = () => {
router.push({
name: 'harmonic-boot/area/OnlineRate'
})
}
onMounted(() => {
// open('4c87b7dff2281254fc55c25a4da31506')
})
defineExpose({ open })
</script>
<style lang="scss" scoped>
.lineInfo {
padding: 0 10px 10px 10px;
}
:deep(.el-page-header__header) {
height: 45px;
border-bottom: 1px solid #f1eded;
}
:deep(.el-descriptions__header) {
margin-bottom: 10px;
}
.evaluationData {
display: grid;
grid-template-rows: repeat(5, auto);
height: 87%;
.row {
margin: 4px 2% 0;
width: 100%;
box-shadow: 1px 1px 1px 1px #e8e3e3;
}
img {
width: 6%;
margin: 0px 15px;
}
}
.iconBox {
display: flex;
span {
display: inline-block;
width: 3px;
height: 18px;
background: var(--el-color-primary);
margin-right: 5px;
margin-left: 3px;
}
}
.conclusion {
display: inline-block;
padding: 2px 5px;
height: 20px;
border-radius: 4px;
}
.background1 {
background-color: #339966;
color: #fff;
}
.background2 {
background-color: #97017e;
color: #fff;
}
.background3 {
background-color: #A52a2a;
color: #fff;
}
</style>

View File

@@ -7,7 +7,7 @@
v-model.trim="inputQuery"
style="height: 46px; width: 334px"
@keyup.enter="DeviceQ"
placeholder="请输入终端名称"
placeholder="请输入监测点相关信息"
>
<template #prefix>
<div class="Icon"></div>
@@ -37,7 +37,7 @@
@click="flyTo(k)"
>
<p>{{ k.psrName }}</p>
<p>{{ k.vlevelName }}|{{ k.maintOrgName }}</p>
<p>{{ k.vlevelName }} | {{ k.maintOrgName }} {{ k.objName ? `| ${k.objName}` : '' }}</p>
</div>
</div>
</el-collapse-item>
@@ -484,6 +484,9 @@ const lookPoint = (e: any) => {
emit('show', true)
}
// 搜索
const matchLineItem = (item: any, regex: RegExp) =>
regex.test(item.lineName) || (item.objName && regex.test(item.objName))
const DeviceQ = () => {
showCollapse.value = true
if (inputQuery.value.length == 0) return
@@ -491,13 +494,14 @@ const DeviceQ = () => {
let list = []
let regex = new RegExp(inputQuery.value, 'i')
let data = areaLineInfo.value
.filter((item: any) => regex.test(item.lineName))
.filter((item: any) => matchLineItem(item, regex))
.map((item: any) => {
return {
psrName: item.lineName,
vlevelName: item.voltageScale,
maintOrgName: item.gdName,
coordinate: [item.lng, item.lat]
coordinate: [item.lng, item.lat],
objName: item.objName
}
})
// data.replace(//s/g,',')
@@ -522,7 +526,7 @@ const flyTo = (e: any, zoom?: number) => {
zoomMap.value = zoom
} else {
zoomMap.value = 15
let data = areaLineInfo.value.filter((item: any) => regex.test(item.lineName))[0]
let data = areaLineInfo.value.filter((item: any) => matchLineItem(item, regex))[0]
if (data) {
markerClick(data)
}

View File

@@ -10,8 +10,7 @@
<img src="@/assets/img/FGX.png" />
</div>
<div style="display: flex">
<div v-for="(item, i) in list" class="cardBox" :style="i == 1 ? 'flex:1.3' : ''">
<div class="card">
<div v-for="(item, i) in list" class="cardBox" :style="i == 1 ? 'flex:1.2' : i === 2 ? 'flex: 1.3' : ''"> <div class="card">
<span style="cursor: pointer" @click="GridDiagram(i)">{{ item.title }}</span>
<span
:style="`color: ${item.color[0]}; cursor:pointer`"
@@ -30,10 +29,10 @@
</div>
<!-- 监测规模 列表 -->
<div>
<div class="vcl mt5">
<div class="vcl mt2">
<p
v-for="(item, i) in vList"
:style="i == 0 ? 'width: 60px' : i == vList?.length - 1 ? 'width: 32px' : 'flex:1'"
v-for="(item, i) in displayVList"
:style="i == 0 ? 'width: 60px' : i == displayVList?.length - 1 ? IS_LN ? 'flex:1' : 'width: 32px' : 'flex:1'"
>
{{ item }}
</p>
@@ -44,7 +43,7 @@
{{ item.title }}
</p>
<p
v-for="(k, i) in vList?.slice(0, vList.length - 2)"
v-for="(k, i) in dataVList"
class="numOne"
@click="LookMap(item.list[i].numOneList, item.list[i].numTwoList, 0)"
>
@@ -59,7 +58,7 @@
<p class="numOne" @click="LookMap(item.list[3].numOneList, item.list[3].numTwoList, 0)">
{{ item.list[3].numOne }}
</p> -->
<p
<p v-if="!IS_LN"
style="font-size: 12px; width: 32px; cursor: pointer; color: var(--el-color-primary)"
@click.stop="open(i)"
>
@@ -69,21 +68,22 @@
</div>
</div>
<!-- 监测点指标统计 -->
<div :style="`height:calc(${boxHeight.height} + 50px)`">
<div :style="`height:calc(${boxHeight.height} + 10px)`" >
<div class="title">
<span>监测点指标统计</span>
</div>
<div style="display: flex" class="mt2">
<img src="@/assets/img/FGX.png" />
</div>
<div :style="`height:calc(${boxHeight.height} + 20px);overflow-y: auto;`">
<div :style="`height:calc(${boxHeight.height} );overflow-y: auto;`">
<div v-for="item in countList">
<div class="title mt5" style="font-weight: 500">
<div class="title mt3" style="font-weight: 500">
<img src="@/assets/img/TJ.png" />
{{ item.title }}
</div>
<div
class="card-Box"
style="max-height: 100px;"
:style="`height:calc((${boxHeight.height} - 50px )/ 3);`"
v-for="val in item.children"
>
@@ -110,7 +110,7 @@
</div>
</div>
<!-- 监测点指标统计 -->
<div :style="`height:calc(${boxHeight.height} - 55px)`">
<div :style="`height:calc(${boxHeight.height} - 75px)`" class="mt20">
<div class="title">
<span>监测运行统计</span>
</div>
@@ -166,7 +166,7 @@
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, provide } from 'vue'
import { onMounted, reactive, ref, provide, computed } from 'vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { ArrowRight, View } from '@element-plus/icons-vue'
@@ -178,15 +178,53 @@ import {
getGridDiagramMonitor,
getGridDiagramDev,
getGridDiagramStatistics,
getGridDiagramRunData
getGridDiagramRunData, getAreaObjAlarm
} from '@/api/device-boot/panorama'
const emit = defineEmits(['LookMap', 'GridDiagram'])
const IS_LN = import.meta.env.VITE_NAME == 'LN' || import.meta.env.VITE_NAME == 'LNqr'
const dictData = useDictData()
const show = ref(false)
const standRef = ref()
const terminalRef = ref()
const pointRef = ref()
const list: any = ref([
{
title: '场站',
color: ['#000', '#bd3124'],
list: [
{
numOne: 0,
numTwo: 0,
numOneList: [],
numTwoList: []
},
{
numOne: 0,
numTwo: 0,
numOneList: [],
numTwoList: []
},
{
numOne: 0,
numTwo: 0,
numOneList: [],
numTwoList: []
},
{
numOne: 0,
numTwo: 0,
numOneList: [],
numTwoList: []
},
{
numOne: 0,
numTwo: 0,
numOneList: [],
numTwoList: []
}
]
},
{
title: '变电站',
color: ['#000', '#bd3124'],
@@ -302,13 +340,14 @@ const countList: any = ref([
{
title: '网公司',
children: [
{ title: '非电网侧监测点', num: 0, onLineNum: 0, integrityRate: 0 },
{
title: '电网侧监测点',
num: 0,
onLineNum: 0,
integrityRate: 0
},
{ title: '非电网侧监测点', num: 0, onLineNum: 0, integrityRate: 0 }
]
},
{
@@ -355,6 +394,10 @@ const legengList = [
]
const panoramicVoltage: any = dictData.getBasicData('Dev_Voltage_Stand')
const vList = ref(['', '500kV', '220kV', '110kV', '35kV', ''])
const displayVList = computed(() => (IS_LN && vList.value.length ? vList.value.slice(0, -1) : vList.value))
const dataVList = computed(() =>
IS_LN ? vList.value.slice(1, -1) : vList.value.slice(0, vList.value.length - 2)
)
const formRow: any = ref({})
const height = mainHeight(30)
const boxHeight = mainHeight(290, 2)
@@ -383,8 +426,8 @@ const info = async (row: any) => {
countList.value[0].title = row.areaName
formRow.value = form
vList.value = ['']
// 变电站
getSubLineGiveAnAlarm(form).then(res => {
getAreaObjAlarm(form).then(res => {
let data = row.isUpToGrid == 1 ? res.data.gwInfo : res.data.info
data[0].data.forEach((item: any) => {
if (item.columnName) vList.value.push(panoramicVoltage.find((v: any) => v.id == item.columnName).name)
@@ -392,16 +435,22 @@ const info = async (row: any) => {
vList.value.push('')
list.value[0].list = data[0].data
})
// 变电站
getSubLineGiveAnAlarm(form).then(res => {
let data = row.isUpToGrid == 1 ? res.data.gwInfo : res.data.info
list.value[1].list = data[0].data
})
// 终端
getGridDiagramDev(form).then(res => {
let data = row.isUpToGrid == 1 ? res.data.gwInfo : res.data.info
list.value[1].list = data[0].data
list.value[2].list = data[0].data
})
// 监测点
await getGridDiagramMonitor(form).then(res => {
let data = row.isUpToGrid == 1 ? res.data.gwInfo : res.data.info
list.value[2].list = data[0].data.map((item: any) => {
list.value[3].list = data[0].data.map((item: any) => {
return {
numOne: item.num,
numTwo: item.onLineNum,
@@ -417,8 +466,8 @@ const info = async (row: any) => {
})
// 监测点指标统计
getGridDiagramStatistics(form).then(res => {
countList.value[0].children[0] = { title: '电网侧监测点', ...res.data.data[0] }
countList.value[0].children[1] = { title: '电网侧监测点', ...res.data.data[1] }
countList.value[0].children[0] = { title: '电网侧监测点', ...res.data.data[1] }
countList.value[0].children[1] = { title: '电网侧监测点', ...res.data.data[0] }
countList.value[1].children[0] = { title: '电网侧监测点', ...res.data.gwData[0] }
})
// 监测运行统计
@@ -464,12 +513,12 @@ defineExpose({ info, show })
.card {
justify-content: center;
margin-top: 15px;
margin-top: 1px;
margin-right: 2%;
display: flex;
flex: 1;
align-items: center;
font-size: 16px;
font-size: 15px;
span:nth-child(2) {
font-weight: 550;

View File

@@ -12,15 +12,15 @@
</template>
<div class="text">
<span style="color: #00B07D">无污染0</span>
<span style="color: #00B07D">优秀:(4.5,5]</span>
<br />
<span style="color: #3399ff">轻微污染(0 , 100]</span>
<span style="color: #3399ff">良好:(4,4.5]</span>
<br />
<span style="color: #ffcc33">轻度污染(100 , 1000]</span>
<span style="color: #ffcc33">合格:(3,4]</span>
<br />
<span style="color: #ff9900">中度污染(1000 , 10000]</span>
<span style="color: #ff9900">较差:(2,3]</span>
<br />
<span style="color: #A52a2a">重度污染(10000</span>
<span style="color: #A52a2a">极差:[1,2]</span>
</div>
</el-popover>
</span>
@@ -169,7 +169,7 @@
</div>
</div>
<!-- 技术监督管理 -->
<div>
<!-- <div>
<div class="title">
<span>技术监督统计</span>
<span class="info" @click="open(3)">
@@ -221,7 +221,7 @@
</el-col>
</el-row>
</div>
</div>
</div> -->
</div>
<img
class="imgR"
@@ -266,8 +266,8 @@ import {
const dictData = useDictData()
const show = ref(false)
const height = mainHeight(30)
const boxHeight: any = mainHeight(420, 2)
const EchHeight: any = mainHeight(510, 2)
const boxHeight: any = mainHeight(230, 2)
const EchHeight: any = mainHeight(320, 2)
const evaluateRef = ref()
const steadyStateRef = ref()
const formRow: any = ref({})

View File

@@ -2,7 +2,7 @@
<template>
<div class="default-main">
<div v-show="addedShow">
<TableHeader>
<TableHeader showCustomColumn>
<template v-slot:select>
<el-form-item label="筛选数据">
<el-input v-model="tableStore.table.params.name" clearable placeholder="筛选数据" />

View File

@@ -142,14 +142,14 @@ narimap.publicKey = 'JBb74325ae94dc49358b7d699660b692'
narimap.Require(
[
'PSRMap',
'ManageGrid',
'Components.Query',
'Components.DeviceCard',
'Popup',
'Components.MapSelector',
'Components.RegionSelector',
'Marker'
'PSRMap',//pwgdgeomapsdk
'ManageGrid',// pwmagdmapsdk
'Components.Query',// EquipSearchCBB
'Components.DeviceCard',//EquipOverviewCBB、EquipDetailCBB
'Popup', //mapsdk
'Components.MapSelector',//MapThemeSwitchCBB
'Components.RegionSelector',//OrgSelectorCBB
'Marker' //mapsdk
],
// ['PSRMap', 'Components.Query', 'Marker'],
() => {

View File

@@ -1,6 +1,6 @@
<template>
<div class="mt10">
<TableHeader :showReset="false" ref="TableHeaderRef" showExport>
<TableHeader :showReset="false" ref="TableHeaderRef" showExport showCustomColumn>
<template #select>
<el-form-item label="导线型号">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入导线型号" show-word-limit

View File

@@ -1,6 +1,6 @@
<template>
<div class="mt10">
<TableHeader :showReset="false" ref="TableHeaderRef" showExport>
<TableHeader :showReset="false" ref="TableHeaderRef" showExport showCustomColumn>
<template #select>
<el-form-item label="谐波源名称">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入谐波源名称" show-word-limit

View File

@@ -1,6 +1,6 @@
<template>
<div class="mt10">
<TableHeader :showReset="false" ref="TableHeaderRef" showExport>
<TableHeader :showReset="false" ref="TableHeaderRef" showExport showCustomColumn>
<template #select>
<el-form-item label="变压器型号">
<el-input v-model="tableStore.table.params.searchValue" clearable placeholder="请输入变压器型号" show-word-limit

View File

@@ -1,7 +1,7 @@
<template>
<div>
<div>
<TableHeader ref="TableHeaderRef" area showExport>
<TableHeader ref="TableHeaderRef" area showExport showCustomColumn>
<template #select>
<!-- <el-form-item label="所在地市">
<el-select v-model="tableStore.table.params.deptId" clearable placeholder="请选择所在地市">

View File

@@ -4,12 +4,8 @@
<template v-slot:select>
<el-form-item label="运行状态">
<el-select v-model="tableStore.table.params.lineRunFlag" clearable placeholder="请选择运行状态">
<el-option
v-for="item in runFlagList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
<el-option v-for="item in runFlagList" :key="item.id" :label="item.name"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="筛选数据">
@@ -40,14 +36,8 @@
</div>
</template>
<div class="mb5" style="height: 40px">
<el-segmented
style="height: 100%"
v-model="segmented"
:props="props"
:options="segmentedList"
block
@change="tableStore.index()"
>
<el-segmented style="height: 100%" v-model="segmented" :props="props" :options="segmentedList"
block @change="tableStore.index()">
<template #default="scope">
<div>
<div>{{ scope.item.name }}</div>
@@ -61,8 +51,8 @@
segmented == 'Power_Network'
? '区域'
: segmented == 'Manufacturer'
? '终端厂家'
: '电网标志'
? '终端厂家'
: '电网标志'
}}
</span>
<span style="width: 90px">终端总数</span>
@@ -77,33 +67,21 @@
{{ o.name }}
</span>
<!-- 终端总数 -->
<span
style="width: 90px; color: #388e3c"
class="text text-style"
@click="renderTable(o.list, o.name + '_')"
>
<span style="width: 90px; color: #388e3c" class="text text-style"
@click="renderTable(o.list, o.name + '_')">
{{ o.count }}
</span>
<!-- -->
<span
style="flex: 1; color: #388e3c"
class="text"
:class="` ${o.integrity < 90 ? 'text-red' : ''}`"
>
<span style="flex: 1; color: #388e3c" class="text"
:class="` ${o.integrity < 90 ? 'text-red' : ''}`">
{{ o.integrity }}
</span>
<span
style="width: 80px; color: #388e3c"
:class="` ${o.online < 90 ? 'text-red' : ''}`"
class="text"
>
<span style="width: 80px; color: #388e3c" :class="` ${o.online < 90 ? 'text-red' : ''}`"
class="text">
{{ o.online }}
</span>
<span
style="width: 80px; color: #388e3c"
:class="` ${o.qualified > 10 ? 'text-red' : ''}`"
class="text"
>
<span style="width: 80px; color: #388e3c" :class="` ${o.qualified > 10 ? 'text-red' : ''}`"
class="text">
{{ o.qualified }}
</span>
</div>
@@ -120,13 +98,8 @@
<!--表格-->
<div :style="{ height: tableStore.table.height }" v-loading="loading">
<vxe-table
height="auto"
:data="dataList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
v-bind="defaultAttribute"
ref="tableRef"
:scroll-y="{ enabled: true }"
>
<vxe-table height="auto" :data="dataList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
v-bind="defaultAttribute" ref="tableRef" :scroll-y="{ enabled: true }">
<vxe-column type="seq" title="序号" width="80px">
<template #default="{ rowIndex }">
<span>
@@ -146,19 +119,16 @@
<vxe-column field="ip" title="终端IP" :formatter="formatter" width="120px"></vxe-column>
<vxe-column field="runFlag" title="运行状态" width="100px">
<template #default="{ row }">
<el-tag
:type="
row.runFlag == '运'
? 'success'
: row.runFlag == '停运'
<el-tag :type="row.runFlag == '运行'
? 'success'
: row.runFlag == '运'
? 'danger'
: row.runFlag == '检修'
? 'warning'
: row.runFlag == '调试'
? 'warning'
: 'danger'
"
>
? 'warning'
: row.runFlag == '调试'
? 'warning'
: 'danger'
">
{{ row.runFlag }}
</el-tag>
</template>
@@ -169,14 +139,9 @@
</vxe-table>
</div>
<div class="table-pagination">
<el-pagination
v-model:currentPage="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100, 200]"
background
layout="sizes,total, ->, prev, pager, next, jumper"
:total="dataList.length"
></el-pagination>
<el-pagination v-model:currentPage="pageNum" v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100, 200]" background
layout="sizes,total, ->, prev, pager, next, jumper" :total="dataList.length"></el-pagination>
</div>
</el-card>
</div>
@@ -187,7 +152,7 @@
import TableStore from '@/utils/tableStore'
import Table from '@/components/table/index.vue'
import TableHeader from '@/components/table/header/index.vue'
import { onMounted, provide, ref } from 'vue'
import { onMounted, provide, ref, nextTick } from 'vue'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
@@ -273,7 +238,7 @@ const tableStore = new TableStore({
if (tableStore.table.params.statisticalType.name == '终端厂家') {
return k.count != 0
} else {
return !k.citName.includes('上送')
return !k.name.includes('上送')
}
})
// 合并子集数据 并去重
@@ -323,21 +288,19 @@ const echart = () => {
z: 0,
zlevel: 0,
itemStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 1,
color: '#57bc6e' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 1,
color: '#57bc6e' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
}
},
@@ -349,34 +312,30 @@ const echart = () => {
z: 0,
zlevel: 0,
itemStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: '#FF9100' // 0% 处的颜色
},
{
offset: 1,
color: '#FF9100' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
color: {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 0,
colorStops: [
{
offset: 0,
color: '#FF9100' // 0% 处的颜色
},
{
offset: 1,
color: '#FF9100' // 100% 处的颜色
}
],
global: false // 缺省为 false
}
}
},
{
type: 'pictorialBar',
itemStyle: {
normal: {
color: '#fff'
}
color: '#fff'
},
symbolRepeat: 50,
// symbolMargin: 300,
@@ -397,11 +356,9 @@ const echart = () => {
barWidth: 18,
itemStyle: {
normal: {
color: 'transparent',
barBorderColor: 'rgb(148,217,249)',
barBorderWidth: 1
}
color: 'transparent',
borderColor: 'rgb(148,217,249)',
borderWidth: 1
},
z: 2
}
@@ -465,15 +422,9 @@ const onExport = () => {
})
}
onMounted(() => {
// TableHeaderRef.value.setDatePicker([
// { label: '年份', value: 1 },
// { label: '季度', value: 2 },
// { label: '月份', value: 3 }
// ])
// 加载数据
tableStore.index()
nextTick(() => {
TableHeaderRef.value?.onComSearch()
})
})
tableStore.table.params.name = ''
@@ -482,14 +433,17 @@ provide('tableStore', tableStore)
<style lang="scss" scoped>
.card-list {
display: flex;
.monitoringPoints {
width: 460px;
position: relative;
.statistics {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-bottom: 10px;
.divBox {
width: 215x;
height: 70px;
@@ -500,33 +454,41 @@ provide('tableStore', tableStore)
font-size: 40px;
margin-right: 5px;
}
.divBox_title {
font-weight: 550;
}
.divBox_num {
font-size: 20px;
font-weight: 550;
margin-left: auto;
font-family: AlimamaDongFangDaKai;
}
align-items: center;
// text-align: center;
border-radius: 5px;
&:nth-child(1) {
background-color: #eef8f0;
}
&:nth-child(2) {
background-color: #fff6ed;
}
&:nth-child(3) {
background-color: #e5f8f6;
}
}
}
}
.detail {
flex: 1;
}
.abnormal {
width: 100%;
background-color: #f3f6f9;
@@ -535,9 +497,11 @@ provide('tableStore', tableStore)
// justify-content: space-between;
align-items: center;
padding: 5px 0px 5px 10px;
.iconDiv {
display: flex;
align-items: center;
div {
width: 4px;
height: 18px;
@@ -545,6 +509,7 @@ provide('tableStore', tableStore)
background-color: var(--el-color-primary);
}
}
.text {
font-weight: 700;
font-size: 16px;
@@ -555,6 +520,7 @@ provide('tableStore', tableStore)
}
}
}
.header {
display: flex;
text-align: center;
@@ -562,40 +528,50 @@ provide('tableStore', tableStore)
font-weight: 700;
padding: 5px;
}
:deep(.el-card__header) {
padding: 10px;
span {
font-weight: 600;
}
}
:deep(.el-card__body) {
padding: 10px;
}
.iconFont {
font-size: 18px;
display: inline-block;
vertical-align: middle;
}
.form {
position: relative;
.form_but {
position: absolute;
right: -22px;
}
}
.card-header {
font-size: 16px;
}
:deep(.table_name) {
color: var(--el-color-primary);
cursor: pointer;
text-underline-offset: 4px;
}
.echartTitle {
display: flex;
justify-content: space-between;
font-size: 14px;
font-weight: 600;
div:nth-child(2) {
font-size: 16px;
color: #ff6600;
@@ -605,20 +581,25 @@ provide('tableStore', tableStore)
:deep(.el-segmented__item-selected, ) {
clip-path: polygon(10% 0, 100% 0, 90% 100%, 0 100%);
}
:deep(.el-segmented__item, ) {
clip-path: polygon(10% 0, 100% 0, 90% 100%, 0 100%);
position: relative;
}
:deep(.el-segmented) {
clip-path: polygon(4% 0, 100% 0, 96% 100%, 0 100%);
}
.text-red {
color: #ff9100 !important;
}
.text-style {
cursor: pointer;
text-decoration: underline;
}
.segmentedIcon {
position: absolute;
top: 1px;
@@ -631,6 +612,7 @@ provide('tableStore', tableStore)
color: #fff;
border-radius: 8px;
}
.table-pagination {
height: 58px;
box-sizing: border-box;

Some files were not shown because too many files have changed in this diff Show More