Compare commits
45 Commits
f1ac67070f
...
2026-01
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd0dab7643 | ||
|
|
cf4291ed9a | ||
|
|
46124f0ea5 | ||
|
|
def48e9c84 | ||
|
|
823d5f4475 | ||
|
|
c09e6f54dd | ||
|
|
5ceb9be9e2 | ||
|
|
054d84778b | ||
|
|
63433aa6dc | ||
|
|
e9d7231a75 | ||
|
|
08afdddc51 | ||
|
|
e21ae50e51 | ||
|
|
4cbd2e43cb | ||
|
|
4c9b677e81 | ||
|
|
0affb17e3a | ||
|
|
c2d0faea08 | ||
|
|
0d155c8680 | ||
|
|
3db01fe32d | ||
|
|
545e3836d1 | ||
|
|
02a95c1dcd | ||
|
|
7a81c008c3 | ||
|
|
5d3d16f8ec | ||
|
|
d25f16bcc7 | ||
|
|
75987c0c6f | ||
|
|
a765cdf9ee | ||
|
|
cc0f8bc8b6 | ||
|
|
7e4db9d4cd | ||
|
|
67e2fa57d0 | ||
|
|
ad1fc11e61 | ||
|
|
6824864db2 | ||
|
|
37ed693cea | ||
|
|
0419af8e50 | ||
|
|
5268b93dd0 | ||
|
|
4e6bd55089 | ||
|
|
4e0db29ab1 | ||
|
|
9b0fd76f48 | ||
|
|
f92b07c555 | ||
|
|
a77db278ac | ||
|
|
51a0ae49a9 | ||
|
|
814e9917d6 | ||
|
|
21f1c41196 | ||
|
|
c188446e76 | ||
|
|
e2a5d084a5 | ||
|
|
4ae27a9d6d | ||
|
|
7783569f91 |
@@ -15,6 +15,13 @@ export function getLineTree() {
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
// 监测点列表治理
|
||||
export function objTree() {
|
||||
return createAxios({
|
||||
url: '/cs-device-boot/csLedger/objTree',
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//云设备录入树
|
||||
@@ -24,4 +31,11 @@ export function getCldTree() {
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
//报表树
|
||||
export function lineTree() {
|
||||
return createAxios({
|
||||
url: '/cs-device-boot/csLedger/lineTree',
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,82 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 新增程序版本
|
||||
export const addEdData = (data) => {
|
||||
export const addEdData = data => {
|
||||
return request({
|
||||
url: '/cs-device-boot/edData/addEdData',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data,
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const auditEdData = (data) => {
|
||||
export const auditEdData = data => {
|
||||
return request({
|
||||
url: '/cs-device-boot/edData/auditEdData',
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
data: data,
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 修改-删除工程
|
||||
export const auditEngineering = (data:any)=> {
|
||||
export const auditEngineering = (data: any) => {
|
||||
return request({
|
||||
url: '/cs-device-boot/engineering/auditEngineering',
|
||||
method: 'post',
|
||||
data: data,
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 修改项目
|
||||
export const updateProject = (data:any) => {
|
||||
export const updateProject = (data: any) => {
|
||||
return request({
|
||||
url: '/cs-device-boot/project/updateProject',
|
||||
method: 'post',
|
||||
|
||||
data: data,
|
||||
data: data
|
||||
})
|
||||
}
|
||||
}
|
||||
// 新增工程
|
||||
export const addEngineering = (data: any) => {
|
||||
return request({
|
||||
url: '/cs-device-boot/engineeringProjectRelation/addEngineering',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 修改工程
|
||||
export const updateEngineering = (data: any) => {
|
||||
return request({
|
||||
url: '/cs-device-boot/engineeringProjectRelation/updateEngineering',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 刪除工程
|
||||
export const deleteEngineering = (data: any) => {
|
||||
return request({
|
||||
url: '/cs-device-boot/engineeringProjectRelation/deleteEngineering',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
// 刪除項目
|
||||
export const deleteProject = (data: any) => {
|
||||
return request({
|
||||
url: '/cs-device-boot/engineeringProjectRelation/deleteProject',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
// 新增项目
|
||||
export const addProject = (data: any) => {
|
||||
return request({
|
||||
url: '/cs-device-boot/engineeringProjectRelation/addProject',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
import { genFileId, ElMessage, ElNotification } from 'element-plus'
|
||||
// 查询设备数据趋势
|
||||
export function getDeviceDataTrend(data: any) {
|
||||
return createAxios({
|
||||
@@ -9,8 +9,6 @@ export function getDeviceDataTrend(data: any) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 波形下载
|
||||
export function getFileZip(params: any) {
|
||||
return createAxios({
|
||||
@@ -27,5 +25,35 @@ export function exportModel(data: any) {
|
||||
method: 'post',
|
||||
data: data,
|
||||
responseType: 'blob'
|
||||
}).then(async res => {
|
||||
let load: any = await readJsonBlob(res)
|
||||
if (load.code) {
|
||||
if (load.data.code == 'A0011') {
|
||||
ElMessage.warning('下载失败!')
|
||||
} else {
|
||||
ElMessage.warning(load.data.message)
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
})
|
||||
}
|
||||
async function readJsonBlob(blob) {
|
||||
try {
|
||||
// 1. Blob.text() 读取二进制 → 直接转为 字符串(自动处理编码)
|
||||
const jsonStr = await blob.text()
|
||||
// 2. JSON.parse 解析字符串 → 得到可用的 JS 对象/数组
|
||||
const jsonData = JSON.parse(jsonStr)
|
||||
// 3. 拿到数据,后续随便用
|
||||
return {
|
||||
code: true,
|
||||
data: jsonData
|
||||
}
|
||||
} catch (err) {
|
||||
return {
|
||||
code: false,
|
||||
data: {}
|
||||
}
|
||||
// console.error('解析Blob的JSON数据失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,67 @@
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
//新增组态项目
|
||||
export function add(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/csconfiguration/add',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//组态项目分页查询
|
||||
export function coFqueryPage(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/csconfiguration/queryPage',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//修改组态项目
|
||||
export function audit(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/csconfiguration/audit',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//组态页面分页查询
|
||||
export function queryPageData(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/cspage/queryPage',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//查询工程列表
|
||||
export function deviceTree(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-device-boot/csLedger/deviceTree',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//三层设备树(项目层根节点为治理设备和便携式设备组态)
|
||||
export function getztProjectTree() {
|
||||
return createAxios({
|
||||
url: '/cs-device-boot/csLedger/getztProjectTree',
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
//新增组态项目
|
||||
export function add(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/csconfiguration/add',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//组态项目分页查询
|
||||
export function coFqueryPage(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/csconfiguration/queryPage',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//修改组态项目
|
||||
export function audit(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/csconfiguration/audit',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//组态页面分页查询
|
||||
export function queryPageData(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/cspage/queryPage',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//查询工程列表
|
||||
export function deviceTree(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-device-boot/csLedger/deviceTree',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//三层设备树(项目层根节点为治理设备和便携式设备组态)
|
||||
export function getztProjectTree() {
|
||||
return createAxios({
|
||||
url: '/cs-device-boot/csLedger/getztProjectTree',
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
||||
//根据用户id获取组件信息
|
||||
export function getByUserId(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/cspage/getByUserId',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
//c保存组态界面与用户的关系
|
||||
export function savePageIdWithUser(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/cspage/savePageIdWithUser',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import createAxios from '@/utils/request'
|
||||
// 获取设备补召页面数据
|
||||
export function getMakeUpData(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/offlineDataUpload/makeUpData?lineId='+data,
|
||||
url: '/cs-harmonic-boot/offlineDataUpload/makeUpData?lineId=' + data,
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
@@ -30,7 +30,14 @@ export function offlineDataUploadMakeUp(data: any) {
|
||||
export function getListByIds() {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/pqSensitiveUser/getListByIds',
|
||||
method: 'POST',
|
||||
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
// 根据id集合获取敏感负荷用户列表
|
||||
export function getList(data) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/pqSensitiveUser/getList',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
// 更新问题状态
|
||||
export function auditFeedBack(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-system-boot/feedback/auditFeedBack',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
//下载文件
|
||||
export function downLoadFile(filePath: any) {
|
||||
return createAxios({
|
||||
url: '/system-boot/file/download',
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
params: { filePath: filePath }
|
||||
})
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
// 更新问题状态
|
||||
export function auditFeedBack(data: any) {
|
||||
return createAxios({
|
||||
url: '/cs-system-boot/feedback/auditFeedBack',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
//下载文件
|
||||
export function downLoadFile(filePath: any) {
|
||||
return createAxios({
|
||||
url: '/system-boot/file/download',
|
||||
method: 'get',
|
||||
responseType: 'blob',
|
||||
params: { filePath: filePath }
|
||||
})
|
||||
}
|
||||
|
||||
//获取文件的一个短期url
|
||||
export function getFileUrl(filePath: any) {
|
||||
return createAxios({
|
||||
url: '/system-boot/file/getFileUrl',
|
||||
method: 'get',
|
||||
// responseType: 'blob',
|
||||
params: { filePath: filePath }
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
import { genFileId, ElMessage, ElNotification } from 'element-plus'
|
||||
// 主要监测点列表查询>>分页
|
||||
export function mainLineList(data: any) {
|
||||
return request({
|
||||
@@ -115,7 +115,6 @@ export function limitProbabilityData(data: any) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 电网侧指标越限统计列表
|
||||
export function gridSideLimitStatisticsList(data: any) {
|
||||
return request({
|
||||
@@ -152,7 +151,6 @@ export function getListByIds(data: any) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 上传治理报告
|
||||
export function uploadReport(data: any) {
|
||||
return request({
|
||||
@@ -260,5 +258,42 @@ export function getSimpleLine() {
|
||||
})
|
||||
}
|
||||
|
||||
export function getLineExport(data: any) {
|
||||
return request({
|
||||
url: '/cs-harmonic-boot/eventReport/getLineExport',
|
||||
method: 'post',
|
||||
data: data,
|
||||
responseType: 'blob'
|
||||
}).then(async res => {
|
||||
let load: any = await readJsonBlob(res)
|
||||
if (load.code) {
|
||||
if (load.data.code == 'A0011') {
|
||||
ElMessage.warning('下载失败!')
|
||||
} else {
|
||||
ElMessage.warning(load.data.message)
|
||||
}
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
async function readJsonBlob(blob) {
|
||||
try {
|
||||
// 1. Blob.text() 读取二进制 → 直接转为 字符串(自动处理编码)
|
||||
const jsonStr = await blob.text()
|
||||
// 2. JSON.parse 解析字符串 → 得到可用的 JS 对象/数组
|
||||
const jsonData = JSON.parse(jsonStr)
|
||||
// 3. 拿到数据,后续随便用
|
||||
return {
|
||||
code: true,
|
||||
data: jsonData
|
||||
}
|
||||
} catch (err) {
|
||||
return {
|
||||
code: false,
|
||||
data: {}
|
||||
}
|
||||
// console.error('解析Blob的JSON数据失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,14 @@ export function getTemplateByDept(params) {
|
||||
params
|
||||
})
|
||||
}
|
||||
// 获取模版
|
||||
export function querySysExcel(params) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/sysExcel/querySysExcel',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
//资源管理 查询数据
|
||||
export function queryData(data) {
|
||||
return createAxios({
|
||||
@@ -168,3 +176,43 @@ export function terminalChooseTree() {
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
//新增模版
|
||||
export function addSysExcel(data:any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/sysExcel/addSysExcel',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//修改模版
|
||||
export function updateSysExcel(data:any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/sysExcel/updateSysExcel',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//删除模版
|
||||
export function deleteSysExcel(params:any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/sysExcel/deleteSysExcel',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
//查詢綁定
|
||||
export function queryList(params:any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/sysExcelRelation/queryList',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
//綁定
|
||||
export function bandRelation(data:any) {
|
||||
return createAxios({
|
||||
url: '/cs-harmonic-boot/sysExcelRelation/bandRelation',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ export const activatePage = (params: any) => {
|
||||
params
|
||||
})
|
||||
}
|
||||
// 全局的驾驶舱页面
|
||||
export const scopePage = (params: any) => {
|
||||
return createAxios({
|
||||
url: '/system-boot/dashboard/scopePage',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 查询激活的驾驶舱页面
|
||||
export const queryActivatePage = () => {
|
||||
return createAxios({
|
||||
|
||||
@@ -1,21 +1,33 @@
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
export function getFunctionsByRoleIndex(data) {
|
||||
return createAxios({
|
||||
url: '/user-boot/roleFunction/getFunctionsByRoleIndex',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRoleMenu(data:any) {
|
||||
return createAxios({
|
||||
url: '/user-boot/function/assignFunctionByRoleIndexes',
|
||||
method: 'post',
|
||||
data: data
|
||||
// params: roleIndex,functionIndexList
|
||||
// data:{
|
||||
// roleIndex,functionIndexList
|
||||
// }
|
||||
})
|
||||
}
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
export function getFunctionsByRoleIndex(data) {
|
||||
return createAxios({
|
||||
url: '/user-boot/roleFunction/getFunctionsByRoleIndex',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRoleMenu(data: any) {
|
||||
return createAxios({
|
||||
url: '/user-boot/function/assignFunctionByRoleIndexes',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 新增角色与系统关系
|
||||
export function systemAdd(data: any) {
|
||||
return createAxios({
|
||||
url: '/user-boot/sysRoleSystem/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 根据角色id获取系统信息
|
||||
export function getSystemByRoleId(params: any) {
|
||||
return createAxios({
|
||||
url: '/user-boot/sysRoleSystem/getSystemByRoleId',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 213 KiB |
@@ -1,7 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--F47曲线 -->
|
||||
<TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader>
|
||||
<TableHeader
|
||||
ref="TableHeaderRef"
|
||||
:showReset="false"
|
||||
:timeKeyList="prop.timeKey"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
></TableHeader>
|
||||
<el-descriptions class="mt2" direction="vertical" :column="4" border>
|
||||
<el-descriptions-item align="center" label="名称">{{ data.name }}</el-descriptions-item>
|
||||
<el-descriptions-item align="center" label="事件总数">{{ data.gs }}</el-descriptions-item>
|
||||
@@ -22,7 +29,6 @@
|
||||
<el-dialog v-model="isWaveCharts" v-if="isWaveCharts" draggable :title="dialogTitle" append-to-body width="70%">
|
||||
<waveFormAnalysis
|
||||
v-loading="loading"
|
||||
|
||||
ref="waveFormAnalysisRef"
|
||||
@handleHideCharts="isWaveCharts = false"
|
||||
:wp="wp"
|
||||
@@ -45,7 +51,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -110,8 +116,8 @@ const tableStore: any = new TableStore({
|
||||
loadCallback: () => {
|
||||
const gongData = gongfunction(tableStore.table.data)
|
||||
data.gs = tableStore.table.data.length
|
||||
data.krr = gongData.pointI.length
|
||||
data.bkrr = gongData.pointIun.length
|
||||
data.krr = gongData.pointF.length
|
||||
data.bkrr = gongData.pointFun.length
|
||||
echartList.value = {
|
||||
title: {
|
||||
text: `F47曲线`
|
||||
@@ -163,11 +169,16 @@ const tableStore: any = new TableStore({
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
max: function (value: any) {
|
||||
return value.max + 20
|
||||
// max: function (value: any) {
|
||||
// return value.max + 20
|
||||
// },
|
||||
max: function (value) {
|
||||
// 先取原始最大值+20,再向上取整到最近的10的倍数,确保刻度够用且规整
|
||||
return Math.ceil((value.max + 20) / 10) * 10
|
||||
},
|
||||
splitNumber: 10,
|
||||
minInterval: 0.1,
|
||||
// splitNumber: 10,
|
||||
// interval: 10,
|
||||
// minInterval: 10,
|
||||
name: '%'
|
||||
}
|
||||
],
|
||||
@@ -204,15 +215,7 @@ const tableStore: any = new TableStore({
|
||||
// [0.4, 50, '2023-01-01 11:00:00']
|
||||
// ],
|
||||
legendSymbol: 'circle',
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'item',
|
||||
@@ -447,10 +450,10 @@ const handleTolerableEventClick = async (row: any) => {
|
||||
nextTick(() => {
|
||||
if (waveFormAnalysisRef.value) {
|
||||
//waveFormAnalysisRef.value.setHeight(false, 360)
|
||||
waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
|
||||
// waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
|
||||
}
|
||||
})
|
||||
const messageInstance = ElMessage.info(`正在加载,请稍等...`)
|
||||
const messageInstance = ElMessage.info(`正在加载,请稍等...`)
|
||||
await analyseWave(row.value[3]) //eventId
|
||||
.then(res => {
|
||||
row.loading1 = false
|
||||
@@ -475,6 +478,7 @@ const handleTolerableEventClick = async (row: any) => {
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(999, 130, 1.6666666)
|
||||
waveFormAnalysisRef.value && waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
|
||||
})
|
||||
}
|
||||
|
||||
308
src/components/cockpit/crossingTime/index.vue
Normal file
308
src/components/cockpit/crossingTime/index.vue
Normal file
@@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--暂态越限时间分布 -->
|
||||
<TableHeader
|
||||
ref="TableHeaderRef"
|
||||
:timeKeyList="prop.timeKey"
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
></TableHeader>
|
||||
<my-echart
|
||||
class="tall"
|
||||
:options="echartList1"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
<!-- <my-echart
|
||||
class="mt10"
|
||||
:options="echartList1"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
|
||||
}"
|
||||
/> -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, provide, reactive, watch } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import MyEchart from '@/components/echarts/MyEchart.vue'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
import { getTime } from '@/utils/formatTime'
|
||||
|
||||
const prop = defineProps({
|
||||
w: { type: [String, Number] },
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
|
||||
const TableHeaderRef = ref()
|
||||
|
||||
const headerHeight = ref(57)
|
||||
|
||||
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
|
||||
headerHeight.value = height
|
||||
|
||||
if (datePickerValue && datePickerValue.timeValue) {
|
||||
// 更新时间参数
|
||||
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
}
|
||||
}
|
||||
|
||||
// 计算是否全屏展示
|
||||
const fullscreen = computed(() => {
|
||||
const w = Number(prop.w)
|
||||
const h = Number(prop.h)
|
||||
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
|
||||
// 执行相应逻辑
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
const config = useConfig()
|
||||
|
||||
const echartList = ref({})
|
||||
|
||||
const echartList1 = ref({})
|
||||
|
||||
const processDataForChart = (rawData: any[]) => {
|
||||
// 将后端返回的扁平数据转换为 ECharts 需要的三维坐标格式 [x, y, z]
|
||||
const chartData = rawData.map(item => [item.x, item.y, item.z])
|
||||
|
||||
return chartData
|
||||
}
|
||||
|
||||
const tableStore: any = new TableStore({
|
||||
url: '/cs-harmonic-boot/csevent/getEventCoords',
|
||||
method: 'POST',
|
||||
showPage: false,
|
||||
column: [],
|
||||
beforeSearchFun: () => {
|
||||
setTime()
|
||||
},
|
||||
loadCallback: () => {
|
||||
const processedData = processDataForChart(tableStore.table.data.innerList || [])
|
||||
const trendList = tableStore.table.data.trendList || []
|
||||
const xlist = tableStore.table.data.xlist || []
|
||||
|
||||
// 处理趋势图数据
|
||||
const seriesData = trendList.map((item: any) => {
|
||||
// 根据接口返回的name字段确定系列名称和颜色
|
||||
let name = ''
|
||||
let color = ''
|
||||
|
||||
switch (item.name) {
|
||||
case 'Evt_Sys_DipStr':
|
||||
name = '电压暂降'
|
||||
color = '#FFBF00'
|
||||
break
|
||||
case 'Evt_Sys_IntrStr':
|
||||
name = '电压中断'
|
||||
color = '#FF9100'
|
||||
break
|
||||
case 'Evt_Sys_SwlStr':
|
||||
name = '电压暂升'
|
||||
color = config.layout.elementUiPrimary[0]
|
||||
break
|
||||
default:
|
||||
name = item.name
|
||||
color = '#000000'
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
color: color,
|
||||
data: item.trendList?.map((value: number, index: number) => [xlist[index], value]) || []
|
||||
}
|
||||
})
|
||||
|
||||
// 获取x轴和y轴的标签值
|
||||
const xLabels = [
|
||||
'0-10%',
|
||||
'10%-20%',
|
||||
'20%-30%',
|
||||
'30%-40%',
|
||||
'40%-50%',
|
||||
'50%-60%',
|
||||
'60%-70%',
|
||||
'70%-80%',
|
||||
'80%-90%',
|
||||
'90%-100%'
|
||||
]
|
||||
const yLabels = ['0-0.01s', '0.01s-0.1s', '0.1s-1s', '1s-10s', '10s']
|
||||
|
||||
echartList.value = {
|
||||
options: {
|
||||
xAxis: null,
|
||||
yAxis: null,
|
||||
dataZoom: null,
|
||||
backgroundColor: '#fff',
|
||||
tooltip: {
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||
borderWidth: 0,
|
||||
formatter: function (params: any) {
|
||||
var tips = ''
|
||||
tips += '持续时间: ' + yLabels[params.value[1]] + '</br>'
|
||||
tips += '特征幅值: ' + xLabels[params.value[0]] + '</br>'
|
||||
tips += '事件次数: ' + params.value[2] + '</br>'
|
||||
return tips
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: '暂态事件概率分布',
|
||||
x: 'center'
|
||||
},
|
||||
visualMap: {
|
||||
max: 500,
|
||||
show: false,
|
||||
inRange: {
|
||||
color: ['#313695', '#00BB00', '#ff8000', '#a50026']
|
||||
}
|
||||
},
|
||||
xAxis3D: {
|
||||
type: 'category',
|
||||
name: '特征幅值',
|
||||
data: xLabels,
|
||||
nameGap: 40
|
||||
},
|
||||
yAxis3D: {
|
||||
type: 'category',
|
||||
name: '持续时间',
|
||||
data: yLabels,
|
||||
nameGap: 40,
|
||||
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
zAxis3D: {
|
||||
type: 'value',
|
||||
minInterval: 10,
|
||||
name: '暂态事件次数',
|
||||
nameGap: 30
|
||||
},
|
||||
grid3D: {
|
||||
viewControl: {
|
||||
projection: 'perspective',
|
||||
distance: 260
|
||||
},
|
||||
boxWidth: 200,
|
||||
boxDepth: 80,
|
||||
light: {
|
||||
main: {
|
||||
intensity: 1.2
|
||||
},
|
||||
ambient: {
|
||||
intensity: 0.3
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar3D',
|
||||
data: processedData,
|
||||
shading: 'realistic',
|
||||
label: {
|
||||
show: false,
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
borderWidth: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
echartList1.value = {
|
||||
title: {
|
||||
text: '暂态越限时间概率分布'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xlist,
|
||||
axisLabel: {
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
name: '次'
|
||||
},
|
||||
grid: {
|
||||
left: '10px',
|
||||
right: '20px'
|
||||
},
|
||||
series: seriesData
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const tableRef = ref()
|
||||
provide('tableRef', tableRef)
|
||||
|
||||
provide('tableStore', tableStore)
|
||||
|
||||
onMounted(() => {
|
||||
tableStore.index()
|
||||
})
|
||||
|
||||
const setTime = () => {
|
||||
const time = getTime(
|
||||
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
prop.timeKey,
|
||||
fullscreen.value
|
||||
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
: prop.timeValue
|
||||
)
|
||||
|
||||
if (Array.isArray(time)) {
|
||||
tableStore.table.params.searchBeginTime = time[0]
|
||||
tableStore.table.params.searchEndTime = time[1]
|
||||
TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
} else {
|
||||
console.warn('获取时间失败,time 不是一个有效数组')
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => prop.timeKey,
|
||||
val => {
|
||||
tableStore.index()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => prop.timeValue,
|
||||
(newVal, oldVal) => {
|
||||
tableStore.index()
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -6,6 +6,7 @@
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
:timeKeyList="prop.timeKey"
|
||||
v-if="fullscreen"
|
||||
></TableHeader>
|
||||
<my-echart
|
||||
@@ -28,7 +29,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ import { yMethod } from '@/utils/echartMethod'
|
||||
const prop = defineProps({
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object }
|
||||
})
|
||||
|
||||
@@ -47,6 +47,7 @@ const init = () => {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: (params: any) => {
|
||||
console.log('🚀 ~ init ~ params:', params)
|
||||
if (!params || params.length === 0) return ''
|
||||
|
||||
// 使用第一个项目的轴标签作为时间标题
|
||||
@@ -100,28 +101,33 @@ const initData = async (row: any) => {
|
||||
}
|
||||
|
||||
// 处理每条相位数据
|
||||
const seriesData = res.data.map((item: any) => {
|
||||
const values = xAxisData.map((time: string, index: number) => {
|
||||
// 将传入的日期与时间拼接成完整的时间字符串
|
||||
const fullTime = `${row.time} ${time}`
|
||||
const value = parseFloat(item.value.split(',')[index]) || 0
|
||||
return [fullTime, value]
|
||||
const seriesData = res.data
|
||||
.filter(item => item.valueType == 'max')
|
||||
.sort((a, b) => {
|
||||
return a.phasic.localeCompare(b.phasic)
|
||||
})
|
||||
.map((item: any) => {
|
||||
const values = xAxisData.map((time: string, index: number) => {
|
||||
// 将传入的日期与时间拼接成完整的时间字符串
|
||||
const fullTime = `${row.time} ${time}`
|
||||
const value = parseFloat(item.value.split(',')[index]) || 0
|
||||
return [fullTime, value]
|
||||
})
|
||||
|
||||
return {
|
||||
name: `${item.phasic}相`,
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
smooth: true,
|
||||
data: values,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
// 根据相位设置对应颜色
|
||||
color: phaseColors[item.phasic] || config.layout.elementUiPrimary[0]
|
||||
return {
|
||||
name: `${item.phasic}相`,
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
smooth: true,
|
||||
data: values,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
// 根据相位设置对应颜色
|
||||
color: phaseColors[item.phasic] || config.layout.elementUiPrimary[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
echartList.value.yAxis.max = max
|
||||
echartList.value.yAxis.min = min
|
||||
// 更新图表配置
|
||||
@@ -135,7 +141,7 @@ onMounted(() => {})
|
||||
const open = async (row: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = row.name + '日趋势图'
|
||||
dialogText.value = `监测点名称:${row.lineName}_越限时间:${row.time}_指标名称:${row.name}`
|
||||
dialogText.value = `监测点名称:${row.lineName} 越限时间:${row.time} 指标名称:${row.name}`
|
||||
nextTick(() => {
|
||||
initData(row)
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
:timeKeyList="prop.timeKey"
|
||||
v-if="fullscreen"
|
||||
></TableHeader>
|
||||
<my-echart
|
||||
@@ -38,7 +39,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -97,7 +98,7 @@ const tableStore: any = new TableStore({
|
||||
{
|
||||
title: '越限最大值',
|
||||
field: 'maxValue',
|
||||
minWidth: '60',
|
||||
minWidth: '70',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
const extentValue =
|
||||
@@ -115,41 +116,25 @@ const tableStore: any = new TableStore({
|
||||
{
|
||||
title: '越限程度(%)',
|
||||
field: 'extent',
|
||||
minWidth: '60',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
// 保留两个小数
|
||||
const extentValue =
|
||||
row.extent !== null && row.extent !== undefined && row.extent !== ''
|
||||
? Math.floor(row.extent * 100) / 100
|
||||
: '/'
|
||||
return `<span>${extentValue}</span>`
|
||||
minWidth: '70',
|
||||
formatter: (row: any) => {
|
||||
return Math.floor(row.cellValue * 100) / 100
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '越限时间',
|
||||
field: 'time',
|
||||
minWidth: '60',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
if (row.time !== null && row.time !== undefined && row.time !== '') {
|
||||
return `<span>${row.time}</span>`
|
||||
} else {
|
||||
return `<span>/</span>`
|
||||
}
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '越限最高监测点',
|
||||
field: 'lineName',
|
||||
minWidth: '90',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
if (row.lineName !== null && row.lineName !== undefined && row.lineName !== '') {
|
||||
return `<span>${row.lineName}</span>`
|
||||
} else {
|
||||
return `<span>/</span>`
|
||||
}
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -158,7 +143,7 @@ const tableStore: any = new TableStore({
|
||||
},
|
||||
loadCallback: () => {
|
||||
// 定义 x 轴标签顺序
|
||||
const xAxisLabels = ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
|
||||
const xAxisLabels = ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
|
||||
|
||||
// 根据指标名称顺序提取对应的 extent 数据
|
||||
const chartData = xAxisLabels.map(label => {
|
||||
@@ -207,6 +192,7 @@ provide('tableStore', tableStore)
|
||||
// 点击行
|
||||
const cellClickEvent = ({ row, column }: any) => {
|
||||
dialogTrendChart.value = true
|
||||
|
||||
if (column.field == 'maxValue' && row.lineId) {
|
||||
nextTick(() => {
|
||||
dailyTrendChartRef.value.open(row)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--治理效果报表 -->
|
||||
<TableHeader :showReset="false" ref="TableHeaderRef" datePicker @selectChange="selectChange" v-if="fullscreen">
|
||||
<TableHeader :showReset="false" :timeKeyList="prop.timeKey" ref="TableHeaderRef" datePicker @selectChange="selectChange" v-if="fullscreen">
|
||||
<template v-slot:select>
|
||||
<el-form-item label="报表模板">
|
||||
<el-select v-model="tableStore.table.params.tempId" placeholder="请选择报表模板" clearable>
|
||||
<el-option v-for="item in templateList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<el-form-item label="模板策略">
|
||||
<el-select filterable v-model="tableStore.table.params.tempId" placeholder="请选择模板策略" clearable>
|
||||
<el-option v-for="item in templateList" :key="item.id" :label="item.excelName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="监测对象">
|
||||
<el-select v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象" clearable>
|
||||
<el-select filterable v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象" clearable>
|
||||
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-slot:operation>
|
||||
<el-button @click="downloadExcel" class="" type="primary" icon="el-icon-Download">导出excel</el-button>
|
||||
<el-button @click="downloadExcel" class="" type="primary" icon="el-icon-Download">导出</el-button>
|
||||
</template>
|
||||
</TableHeader>
|
||||
<div style="display: flex">
|
||||
@@ -34,16 +34,16 @@ import { ref, onMounted, provide, reactive, watch, h, computed, nextTick } from
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { exportExcel } from '@/views/govern/reportForms/export.js'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
import { getTemplateList } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
|
||||
import { getTime } from '@/utils/formatTime'
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
const prop = defineProps({
|
||||
w: { type: [String, Number] },
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -71,8 +71,8 @@ const initListByIds = () => {
|
||||
}
|
||||
|
||||
const templateListData = () => {
|
||||
getTemplateList({}).then(res => {
|
||||
templateList.value = res.data
|
||||
querySysExcel({}).then(res => {
|
||||
templateList.value = res.data.filter(item => item.excelType == 4)
|
||||
if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
|
||||
tableStore.table.params.tempId = templateList.value[0].id
|
||||
}
|
||||
@@ -118,12 +118,15 @@ const tableStore: any = new TableStore({
|
||||
column: [],
|
||||
beforeSearchFun: () => {
|
||||
setTime()
|
||||
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
|
||||
tableStore.table.params.sensitiveUserId = idList.value[0].id
|
||||
}
|
||||
if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
|
||||
tableStore.table.params.tempId = templateList.value[0].id
|
||||
}
|
||||
// if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
|
||||
// tableStore.table.params.sensitiveUserId = idList.value[0].id
|
||||
// }
|
||||
// if (!tableStore.table.params.tempId && templateList.value?.length > 0) {
|
||||
// tableStore.table.params.tempId = templateList.value[0].id
|
||||
// }
|
||||
// if( !tableStore.table.params.tempId){
|
||||
// return ElMessage.warning('请选择模板')
|
||||
// }
|
||||
},
|
||||
loadCallback: () => {
|
||||
luckysheet.create({
|
||||
@@ -156,6 +159,8 @@ const setTime = () => {
|
||||
if (Array.isArray(time)) {
|
||||
tableStore.table.params.searchBeginTime = time[0]
|
||||
tableStore.table.params.searchEndTime = time[1]
|
||||
tableStore.table.params.startTime = time[0]
|
||||
tableStore.table.params.endTime = time[1]
|
||||
TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
} else {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
v-model="searchForm.index"
|
||||
placeholder="请选择统计指标"
|
||||
@change="onIndexChange($event)"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in indexOptions"
|
||||
@@ -33,14 +34,15 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="统计类型">
|
||||
<el-select
|
||||
style="min-width: 120px !important"
|
||||
style="min-width: 90px !important"
|
||||
placeholder="请选择"
|
||||
v-model="searchForm.valueType"
|
||||
filterable
|
||||
>
|
||||
<el-option value="max" label="最大值"></el-option>
|
||||
<el-option value="min" label="最小值"></el-option>
|
||||
<el-option value="avg" label="平均值"></el-option>
|
||||
<el-option value="cp95" label="cp95"></el-option>
|
||||
<el-option value="cp95" label="CP95"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -59,11 +61,12 @@
|
||||
placeholder="请选择谐波次数"
|
||||
style="width: 100px"
|
||||
class="mr20"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="vv in item.countOptions"
|
||||
:key="vv"
|
||||
:label="vv"
|
||||
:label="item.name.includes('间谐波') ? vv - 0.5 : vv"
|
||||
:value="vv"
|
||||
></el-option>
|
||||
</el-select>
|
||||
@@ -79,11 +82,7 @@
|
||||
</TableHeader>
|
||||
</div>
|
||||
<div class="history_chart" :style="pageHeight" v-loading="loading">
|
||||
<MyEchart
|
||||
ref="historyChart"
|
||||
:options="echartsData"
|
||||
v-if="showEchart"
|
||||
/>
|
||||
<MyEchart ref="historyChart" :options="echartsData" v-if="showEchart" />
|
||||
<el-empty :style="pageHeight" v-else description="暂无数据" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -157,28 +156,40 @@ const countOptions: any = ref([])
|
||||
const legendDictList: any = ref([])
|
||||
|
||||
const initCode = (field: string, title: string) => {
|
||||
queryByCode('steady_state_limit_trend').then(res => {
|
||||
queryByCode('gridSide_exceedTheLimit').then(res => {
|
||||
queryCsDictTree(res.data.id).then(item => {
|
||||
//排序
|
||||
indexOptions.value = item.data.sort((a: any, b: any) => {
|
||||
return a.sort - b.sort
|
||||
})
|
||||
const titleMap: Record<string, number> = {
|
||||
flickerOvertime: 0,
|
||||
uaberranceOvertime: 3,
|
||||
ubalanceOvertime: 4,
|
||||
freqDevOvertime: 5
|
||||
}
|
||||
let codeKey = field.includes('flickerOvertime')
|
||||
? '闪变'
|
||||
: field.includes('uharm')
|
||||
? '谐波电压'
|
||||
: field.includes('iharm')
|
||||
? '谐波电流'
|
||||
: field.includes('voltageDevOvertime')
|
||||
? '电压偏差'
|
||||
: field.includes('ubalanceOvertime')
|
||||
? '不平衡'
|
||||
: ''
|
||||
|
||||
let defaultIndex = 0 // 默认值
|
||||
// const titleMap: Record<string, number> = {
|
||||
// flickerOvertime: 0,
|
||||
// uaberranceOvertime: 3,
|
||||
// ubalanceOvertime: 4,
|
||||
// freqDevOvertime: 5
|
||||
// }
|
||||
|
||||
if (field in titleMap) {
|
||||
defaultIndex = titleMap[field]
|
||||
} else if (field.includes('uharm')) {
|
||||
defaultIndex = 1
|
||||
} else if (field.includes('iharm')) {
|
||||
defaultIndex = 2
|
||||
}
|
||||
// let defaultIndex = 0 // 默认值
|
||||
let defaultIndex = indexOptions.value.findIndex((item: any) => item.name.includes(codeKey)) || 0
|
||||
// if (field in titleMap) {
|
||||
// defaultIndex = titleMap[field]
|
||||
// } else if (field.includes('uharm')) {
|
||||
// defaultIndex = indexOptions.value.findIndex((item: any) => item.code === 'uharm')
|
||||
// } else if (field.includes('iharm')) {
|
||||
// defaultIndex = indexOptions.value.findIndex((item: any) => item.code === 'iharm')
|
||||
// }
|
||||
|
||||
searchForm.value.index[0] = indexOptions.value[defaultIndex].id
|
||||
})
|
||||
@@ -202,7 +213,7 @@ const initCode = (field: string, title: string) => {
|
||||
if (kk.harmStart && kk.harmEnd) {
|
||||
range(0, 0, 0)
|
||||
|
||||
if (kk.showName == '间谐波电压含有率') {
|
||||
if (kk.showName.includes('间谐波电压')) {
|
||||
countDataCopy.value[index].countOptions = range(kk.harmStart, kk.harmEnd, 1).map(
|
||||
(item: any) => {
|
||||
return item - 0.5
|
||||
@@ -284,14 +295,15 @@ const init = async () => {
|
||||
let lists: any = []
|
||||
let frequencys: any = null
|
||||
countData.value.map((item: any, index: any) => {
|
||||
if (item.name.includes('谐波含有率')) {
|
||||
if (item.name.includes('谐波')) {
|
||||
frequencys = item.count
|
||||
} else {
|
||||
frequencys = ''
|
||||
}
|
||||
|
||||
lists[index] = {
|
||||
statisticalId: item.index,
|
||||
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
|
||||
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
|
||||
}
|
||||
})
|
||||
let obj = {
|
||||
@@ -597,12 +609,12 @@ const formatCountOptions = () => {
|
||||
})
|
||||
|
||||
countData.value.map((item: any, key: any) => {
|
||||
if (item.name == '谐波电流有效值') {
|
||||
item.name = '谐波电流次数'
|
||||
} else if (item.name == '谐波电压含有率') {
|
||||
item.name = '谐波电压次数'
|
||||
} else if (item.name == '间谐波电压含有率') {
|
||||
if (item.name.includes('间谐波电压')) {
|
||||
item.name = '间谐波电压次数'
|
||||
} else if (item.name.includes('谐波电流')) {
|
||||
item.name = '谐波电流次数'
|
||||
} else if (item.name.includes('谐波电压')) {
|
||||
item.name = '谐波电压次数'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
v-model="tableStore.table.params.lineId"
|
||||
placeholder="请选择监测点"
|
||||
style="width: 150px"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.lineId"
|
||||
:label="item.name"
|
||||
:label="item.lineName"
|
||||
:value="item.lineId"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -48,7 +49,7 @@ const loop50 = (key: string) => {
|
||||
list.push({
|
||||
title: i + '次',
|
||||
field: key + i + 'Overtime',
|
||||
width: '80',
|
||||
width: '60',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
|
||||
@@ -84,9 +85,9 @@ const tableStore: any = new TableStore({
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
title: '闪变越限(%)',
|
||||
title: '长时闪变越限(%)',
|
||||
field: 'flickerOvertime',
|
||||
width: '80',
|
||||
width: '90',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
|
||||
@@ -100,16 +101,7 @@ const tableStore: any = new TableStore({
|
||||
title: '谐波电流越限(%)',
|
||||
children: loop50('iharm')
|
||||
},
|
||||
{
|
||||
title: '三相不平衡度越限(%)',
|
||||
field: 'ubalanceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
title: '电压偏差越限(%)',
|
||||
field: 'voltageDevOvertime',
|
||||
width: '100',
|
||||
@@ -119,14 +111,24 @@ const tableStore: any = new TableStore({
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '频率偏差越限(%)',
|
||||
field: 'freqDevOvertime',
|
||||
title: '三相不平衡度越限(%)',
|
||||
field: 'ubalanceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// {
|
||||
// title: '频率偏差越限(%)',
|
||||
// field: 'freqDevOvertime',
|
||||
// width: '100',
|
||||
// render: 'customTemplate',
|
||||
// customTemplate: (row: any) => {
|
||||
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
// }
|
||||
// }
|
||||
],
|
||||
beforeSearchFun: () => {
|
||||
},
|
||||
@@ -138,16 +140,20 @@ const tableStore: any = new TableStore({
|
||||
provide('tableStore', tableStore)
|
||||
tableStore.table.params.sortBy = ''
|
||||
tableStore.table.params.orderBy = ''
|
||||
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => {
|
||||
const open = async (row: any,searchBeginTime:any,searchEndTime:any,interval:any,list:any) => {
|
||||
dialogVisible.value = true
|
||||
initCSlineList()
|
||||
options.value = list
|
||||
// initCSlineList()
|
||||
tableStore.table.params.lineId = row.lineId
|
||||
|
||||
nextTick(() => {
|
||||
tableHeaderRef.value.setTimeInterval([searchBeginTime, searchEndTime])
|
||||
tableHeaderRef.value.setInterval(interval)
|
||||
setTimeout(() => {
|
||||
tableHeaderRef.value.setTimeInterval([searchBeginTime, searchEndTime])
|
||||
tableStore.table.params.searchBeginTime =searchBeginTime
|
||||
tableStore.table.params.searchEndTime = searchEndTime
|
||||
tableStore.index()
|
||||
},100)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
ref="TableHeaderRef"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
:timeKeyList="prop.timeKey"
|
||||
v-if="fullscreen"
|
||||
></TableHeader>
|
||||
<my-echart
|
||||
@@ -41,7 +42,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -87,7 +88,7 @@ const initEcharts = () => {
|
||||
|
||||
xAxis: {
|
||||
// name: '(区域)',
|
||||
data: ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
|
||||
data: ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
|
||||
},
|
||||
|
||||
yAxis: {
|
||||
@@ -148,7 +149,7 @@ const tableStore: any = new TableStore({
|
||||
title: '越限占比(%)',
|
||||
children: [
|
||||
{
|
||||
title: '闪变',
|
||||
title: '长时闪变',
|
||||
field: 'flicker',
|
||||
minWidth: '70',
|
||||
render: 'customTemplate',
|
||||
@@ -197,6 +198,7 @@ const tableStore: any = new TableStore({
|
||||
],
|
||||
beforeSearchFun: () => {
|
||||
setTime()
|
||||
tableStore.table.params.interval = TableHeaderRef.value?.datePickerRef?.interval || 3
|
||||
},
|
||||
loadCallback: () => {
|
||||
tableStore.table.height = `calc(${prop.height} - 80px)`
|
||||
@@ -211,13 +213,13 @@ provide('tableStore', tableStore)
|
||||
|
||||
// 点击行
|
||||
const cellClickEvent = ({ row, column }: any) => {
|
||||
if (column.field != 'name') {
|
||||
OverLimitDetailsRef.value.open(
|
||||
row,
|
||||
tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
|
||||
tableStore.table.params.searchEndTime || prop.timeValue?.[1]
|
||||
)
|
||||
}
|
||||
OverLimitDetailsRef.value.open(
|
||||
row,
|
||||
tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
|
||||
tableStore.table.params.searchEndTime || prop.timeValue?.[1],
|
||||
tableStore.table.params.interval || prop.interval,
|
||||
tableStore.table.data
|
||||
)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
480
src/components/cockpit/indicatorCrossingTime/index.vue
Normal file
480
src/components/cockpit/indicatorCrossingTime/index.vue
Normal file
@@ -0,0 +1,480 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--指标越限时间分布
|
||||
-->
|
||||
<TableHeader
|
||||
:showReset="false"
|
||||
:timeKeyList="prop.timeKey"
|
||||
ref="TableHeaderRef"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="监测点">
|
||||
<el-select size="small" filterable v-model="tableStore.table.params.lineId">
|
||||
<el-option
|
||||
v-for="item in lineList"
|
||||
:key="item.lineId"
|
||||
:label="item.name"
|
||||
:value="item.lineId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</TableHeader>
|
||||
<div v-loading="tableStore.table.loading">
|
||||
<my-echart
|
||||
class="tall"
|
||||
v-if="lineShow"
|
||||
:options="echartList1"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
<el-empty
|
||||
v-else
|
||||
description="暂无监测点"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
<!-- <my-echart
|
||||
class="mt10"
|
||||
:options="echartList1"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
|
||||
}"
|
||||
/> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, provide, reactive, watch, h } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
import MyEchart from '@/components/echarts/MyEchart.vue'
|
||||
import { limitProbabilityData, cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
|
||||
import { getTime } from '@/utils/formatTime'
|
||||
|
||||
const prop = defineProps({
|
||||
w: { type: [String, Number] },
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
|
||||
// const options = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]'))
|
||||
|
||||
const lineList = ref()
|
||||
|
||||
const headerHeight = ref(57)
|
||||
|
||||
const TableHeaderRef = ref()
|
||||
const lineShow = ref(true)
|
||||
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
|
||||
headerHeight.value = height
|
||||
|
||||
if (datePickerValue && datePickerValue.timeValue) {
|
||||
// 更新时间参数
|
||||
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
}
|
||||
}
|
||||
|
||||
// 计算是否全屏展示
|
||||
const fullscreen = computed(() => {
|
||||
const w = Number(prop.w)
|
||||
const h = Number(prop.h)
|
||||
if (!isNaN(w) && !isNaN(h) && w === 12 && h === 6) {
|
||||
// 执行相应逻辑
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
const echartList = ref()
|
||||
|
||||
const echartList1 = ref()
|
||||
|
||||
const probabilityData = ref()
|
||||
|
||||
const initLineList = async () => {
|
||||
cslineList({}).then(res => {
|
||||
if (res.data.length == 0) {
|
||||
lineShow.value = false
|
||||
return (tableStore.table.loading = false)
|
||||
}
|
||||
lineShow.value = true
|
||||
lineList.value = res.data
|
||||
tableStore.table.params.lineId = lineList.value[0].lineId
|
||||
tableStore.index()
|
||||
})
|
||||
}
|
||||
|
||||
// 越限程度概率分布
|
||||
const initProbabilityData = () => {
|
||||
// 只有当 lineList 已加载且有数据时才设置默认 lineId
|
||||
if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) {
|
||||
tableStore.table.params.lineId = lineList.value[0].lineId
|
||||
}
|
||||
const params = {
|
||||
searchBeginTime: tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
|
||||
searchEndTime: tableStore.table.params.searchEndTime || prop.timeValue?.[1],
|
||||
lineId: tableStore.table.params.lineId
|
||||
}
|
||||
limitProbabilityData(params).then((res: any) => {
|
||||
probabilityData.value = res.data
|
||||
|
||||
// 处理接口返回的数据,转换为图表所需格式
|
||||
if (res.data && Array.isArray(res.data)) {
|
||||
// 定义指标类型顺序
|
||||
const indicatorOrder = ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相电压不平衡度', '频率偏差']
|
||||
// 按照指定顺序排序数据
|
||||
const sortedData = [...res.data].sort((a, b) => {
|
||||
return indicatorOrder.indexOf(a.indexName) - indicatorOrder.indexOf(b.indexName)
|
||||
})
|
||||
|
||||
// 构造 series 数据
|
||||
const seriesData: any = []
|
||||
let maxValue: any = 0 // 用于存储数据中的最大值
|
||||
// 遍历每个越限程度区间(0-20%, 20-40%, 40-60%, 60-80%, 80-100%)
|
||||
for (let xIndex = 0; xIndex < 5; xIndex++) {
|
||||
// 遍历每个指标类型
|
||||
sortedData.forEach((item, yIndex) => {
|
||||
// 从 extentGrades 中获取对应区间的值
|
||||
const extentGrade = item.extentGrades[xIndex]
|
||||
const value = extentGrade ? (Object.values(extentGrade)[0] as number) : 0
|
||||
seriesData.push([xIndex, yIndex, value])
|
||||
|
||||
// 更新最大值
|
||||
if (value > maxValue) {
|
||||
maxValue = value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 计算 z 轴最大值(最大值加 5)
|
||||
const zAxisMax = Math.ceil(maxValue) + 5
|
||||
// 构造 yAxis 数据(指标类型名称)
|
||||
const yAxisData = sortedData.map(item => item.indexName)
|
||||
|
||||
echartList.value = {
|
||||
title: {
|
||||
text: '指标越限概率分布'
|
||||
},
|
||||
options: {
|
||||
backgroundColor: '#fff',
|
||||
tooltip: {
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||
borderWidth: 0,
|
||||
formatter: function (params: any) {
|
||||
var yIndex = params.value[1] //获取y轴索引
|
||||
var tips = ''
|
||||
tips += '指标类型: ' + yAxisData[yIndex] + '</br>'
|
||||
tips += '越限程度: ' + params.seriesName + '</br>'
|
||||
tips += '越限次数: ' + params.value[2] + '</br>'
|
||||
return tips
|
||||
}
|
||||
},
|
||||
|
||||
// 移除或隐藏 visualMap 组件
|
||||
visualMap: {
|
||||
show: false, // 设置为 false 隐藏右侧颜色条
|
||||
min: 0,
|
||||
// max: 100,
|
||||
max: zAxisMax, // 使用计算出的最大值加5
|
||||
inRange: {
|
||||
color: ['#313695', '#00BB00', '#ff8000', '#d73027', '#a50026']
|
||||
}
|
||||
},
|
||||
// 添加 legend 配置并设置为不显示
|
||||
legend: {
|
||||
show: false // 隐藏图例
|
||||
},
|
||||
xAxis3D: {
|
||||
type: 'category',
|
||||
name: '越限程度',
|
||||
nameLocation: 'middle',
|
||||
nameGap: 50,
|
||||
data: ['0-20%', '20-40%', '40-60%', '60-80%', '80-100%']
|
||||
},
|
||||
yAxis3D: {
|
||||
type: 'category',
|
||||
name: '指标类型',
|
||||
nameLocation: 'middle',
|
||||
nameGap: 50,
|
||||
data: yAxisData,
|
||||
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
zAxis3D: {
|
||||
type: 'value',
|
||||
name: '越限次数',
|
||||
nameLocation: 'middle',
|
||||
nameGap: 30,
|
||||
minInterval: 10
|
||||
|
||||
// max: 100
|
||||
},
|
||||
grid3D: {
|
||||
viewControl: {
|
||||
projection: 'perspective',
|
||||
distance: 260,
|
||||
rotateSensitivity: 10,
|
||||
zoomSensitivity: 2
|
||||
},
|
||||
boxWidth: 150,
|
||||
boxDepth: 100,
|
||||
boxHeight: 100,
|
||||
light: {
|
||||
main: {
|
||||
intensity: 1.2
|
||||
},
|
||||
ambient: {
|
||||
intensity: 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar3D',
|
||||
name: '0-20%',
|
||||
data: seriesData.filter((item: any) => item[0] === 0),
|
||||
shading: 'realistic',
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
opacity: 0.9
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
color: '#000'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#ff8000'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'bar3D',
|
||||
name: '20-40%',
|
||||
data: seriesData.filter((item: any) => item[0] === 1),
|
||||
shading: 'realistic',
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
opacity: 0.9
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
color: '#000'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#ff8000'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'bar3D',
|
||||
name: '40-60%',
|
||||
data: seriesData.filter((item: any) => item[0] === 2),
|
||||
shading: 'realistic',
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
opacity: 0.9
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
color: '#000'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#ff8000'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'bar3D',
|
||||
name: '60-80%',
|
||||
data: seriesData.filter((item: any) => item[0] === 3),
|
||||
shading: 'realistic',
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
opacity: 0.9
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
color: '#000'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#ff8000'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'bar3D',
|
||||
name: '80-100%',
|
||||
data: seriesData.filter((item: any) => item[0] === 4),
|
||||
shading: 'realistic',
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
opacity: 0.9
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
color: '#000'
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#ff8000'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const tableStore: any = new TableStore({
|
||||
url: '/cs-harmonic-boot/limitRateDetailD/limitTimeProbabilityData',
|
||||
method: 'POST',
|
||||
showPage: false,
|
||||
column: [],
|
||||
beforeSearchFun: () => {
|
||||
setTime()
|
||||
// 只有当 lineList 已加载且有数据时才设置默认 lineId
|
||||
if (!tableStore.table.params.lineId && lineList.value && lineList.value.length > 0) {
|
||||
tableStore.table.params.lineId = lineList.value[0].lineId
|
||||
}
|
||||
},
|
||||
loadCallback: () => {
|
||||
// 处理返回的数据,将其转换为图表所需格式
|
||||
const indexNames: any = [...new Set(tableStore.table.data.map((item: any) => item.indexName))]
|
||||
const timePeriods = [...new Set(tableStore.table.data.map((item: any) => item.timePeriod))]
|
||||
|
||||
// 构建系列数据
|
||||
const seriesData = indexNames.map((indexName: string) => {
|
||||
const dataIndex = tableStore.table.data.filter((item: any) => item.indexName === indexName)
|
||||
return {
|
||||
name: indexName,
|
||||
type: 'line',
|
||||
symbol: 'none',
|
||||
data: dataIndex.map((item: any) => [item.timePeriod, item.times])
|
||||
}
|
||||
})
|
||||
|
||||
echartList1.value = {
|
||||
title: {
|
||||
text: '指标越限时间概率分布'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: indexNames
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '时间段',
|
||||
data: timePeriods
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
// name: '次数'
|
||||
},
|
||||
series: seriesData
|
||||
}
|
||||
initProbabilityData()
|
||||
}
|
||||
})
|
||||
|
||||
provide('tableStore', tableStore)
|
||||
|
||||
onMounted(() => {
|
||||
initLineList()
|
||||
})
|
||||
|
||||
const setTime = () => {
|
||||
const time = getTime(
|
||||
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
prop.timeKey,
|
||||
fullscreen.value
|
||||
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
: prop.timeValue
|
||||
)
|
||||
|
||||
if (Array.isArray(time)) {
|
||||
tableStore.table.params.searchBeginTime = time[0]
|
||||
tableStore.table.params.searchEndTime = time[1]
|
||||
TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
} else {
|
||||
console.warn('获取时间失败,time 不是一个有效数组')
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => prop.timeKey,
|
||||
val => {
|
||||
tableStore.index()
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => prop.timeValue,
|
||||
(newVal, oldVal) => {
|
||||
tableStore.index()
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
const addMenu = () => {}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -5,9 +5,9 @@
|
||||
:showReset="false"
|
||||
ref="TableHeaderRef"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
:timeKeyList="['3']"
|
||||
:timeKeyList="prop.timeKey"
|
||||
></TableHeader>
|
||||
<el-calendar
|
||||
v-model="value"
|
||||
@@ -51,7 +51,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--指标越限概率分布 -->
|
||||
<TableHeader :showReset="false" ref="TableHeaderRef" @selectChange="selectChange" datePicker v-if="fullscreen">
|
||||
<TableHeader
|
||||
:showReset="false"
|
||||
:timeKeyList="prop.timeKey"
|
||||
ref="TableHeaderRef"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="监测点">
|
||||
<el-select size="small" v-model="tableStore.table.params.lineId">
|
||||
<el-select size="small" filterable v-model="tableStore.table.params.lineId">
|
||||
<el-option
|
||||
v-for="item in lineList"
|
||||
:key="item.lineId"
|
||||
@@ -17,21 +24,30 @@
|
||||
</TableHeader>
|
||||
<div v-loading="tableStore.table.loading">
|
||||
<my-echart
|
||||
v-if="lineShow"
|
||||
class="tall"
|
||||
:options="echartList"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
<my-echart
|
||||
<el-empty
|
||||
v-else
|
||||
description="暂无监测点"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
<!-- <my-echart
|
||||
class="mt10"
|
||||
:options="echartList1"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
|
||||
}"
|
||||
/>
|
||||
/> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -48,11 +64,11 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
|
||||
const lineShow = ref(true)
|
||||
// const options = ref(JSON.parse(window.localStorage.getItem('lineIdList') || '[]'))
|
||||
|
||||
const lineList = ref()
|
||||
@@ -91,6 +107,11 @@ const probabilityData = ref()
|
||||
|
||||
const initLineList = async () => {
|
||||
cslineList({}).then(res => {
|
||||
if (res.data.length == 0) {
|
||||
lineShow.value = false
|
||||
return (tableStore.table.loading = false)
|
||||
}
|
||||
lineShow.value = true
|
||||
lineList.value = res.data
|
||||
tableStore.table.params.lineId = lineList.value[0].lineId
|
||||
tableStore.index()
|
||||
@@ -114,7 +135,7 @@ const initProbabilityData = () => {
|
||||
// 处理接口返回的数据,转换为图表所需格式
|
||||
if (res.data && Array.isArray(res.data)) {
|
||||
// 定义指标类型顺序
|
||||
const indicatorOrder = ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相电压不平衡度', '频率偏差']
|
||||
const indicatorOrder = ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相电压不平衡度', '频率偏差']
|
||||
// 按照指定顺序排序数据
|
||||
const sortedData = [...res.data].sort((a, b) => {
|
||||
return indicatorOrder.indexOf(a.indexName) - indicatorOrder.indexOf(b.indexName)
|
||||
@@ -145,6 +166,9 @@ const initProbabilityData = () => {
|
||||
const yAxisData = sortedData.map(item => item.indexName)
|
||||
|
||||
echartList.value = {
|
||||
title: {
|
||||
text: '指标越限概率分布'
|
||||
},
|
||||
options: {
|
||||
backgroundColor: '#fff',
|
||||
tooltip: {
|
||||
@@ -165,14 +189,7 @@ const initProbabilityData = () => {
|
||||
return tips
|
||||
}
|
||||
},
|
||||
title: {
|
||||
text: '指标越限概率分布',
|
||||
x: 'center',
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'normal'
|
||||
}
|
||||
},
|
||||
|
||||
// 移除或隐藏 visualMap 组件
|
||||
visualMap: {
|
||||
show: false, // 设置为 false 隐藏右侧颜色条
|
||||
@@ -220,7 +237,7 @@ const initProbabilityData = () => {
|
||||
grid3D: {
|
||||
viewControl: {
|
||||
projection: 'perspective',
|
||||
distance: 250,
|
||||
distance: 260,
|
||||
rotateSensitivity: 10,
|
||||
zoomSensitivity: 2
|
||||
},
|
||||
@@ -394,7 +411,7 @@ const tableStore: any = new TableStore({
|
||||
|
||||
echartList1.value = {
|
||||
title: {
|
||||
text: '越限时间概率分布'
|
||||
text: '指标越限时间概率分布'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
v-model="searchForm.index"
|
||||
placeholder="请选择统计指标"
|
||||
@change="onIndexChange($event)"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in indexOptions"
|
||||
@@ -36,6 +37,7 @@
|
||||
style="min-width: 120px !important"
|
||||
placeholder="请选择"
|
||||
v-model="searchForm.valueType"
|
||||
filterable
|
||||
>
|
||||
<el-option value="max" label="最大值"></el-option>
|
||||
<el-option value="min" label="最小值"></el-option>
|
||||
@@ -59,11 +61,12 @@
|
||||
placeholder="请选择谐波次数"
|
||||
style="width: 100px"
|
||||
class="mr20"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="vv in item.countOptions"
|
||||
:key="vv"
|
||||
:label="vv"
|
||||
:label="item.name.includes('间谐波') ? vv - 0.5 : vv"
|
||||
:value="vv"
|
||||
></el-option>
|
||||
</el-select>
|
||||
@@ -78,12 +81,8 @@
|
||||
</template>
|
||||
</TableHeader>
|
||||
</div>
|
||||
<div class="history_chart" :style="pageHeight" v-loading="loading">
|
||||
<MyEchart
|
||||
ref="historyChart"
|
||||
:options="echartsData"
|
||||
v-if="showEchart"
|
||||
/>
|
||||
<div class="history_chart" :style="pageHeight" v-loading="loading">
|
||||
<MyEchart ref="historyChart" :options="echartsData" v-if="showEchart" />
|
||||
<el-empty :style="pageHeight" v-else description="暂无数据" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -157,28 +156,40 @@ const countOptions: any = ref([])
|
||||
const legendDictList: any = ref([])
|
||||
|
||||
const initCode = (field: string, title: string) => {
|
||||
queryByCode('steady_state_limit_trend').then(res => {
|
||||
queryByCode('gridSide_exceedTheLimit').then(res => {
|
||||
queryCsDictTree(res.data.id).then(item => {
|
||||
//排序
|
||||
indexOptions.value = item.data.sort((a: any, b: any) => {
|
||||
return a.sort - b.sort
|
||||
})
|
||||
const titleMap: Record<string, number> = {
|
||||
flickerOvertime: 0,
|
||||
uaberranceOvertime: 3,
|
||||
ubalanceOvertime: 4,
|
||||
freqDevOvertime: 5
|
||||
}
|
||||
// const titleMap: Record<string, number> = {
|
||||
// flickerOvertime: 0,
|
||||
// uaberranceOvertime: 3,
|
||||
// ubalanceOvertime: 4,
|
||||
// freqDevOvertime: 5
|
||||
// }
|
||||
|
||||
let defaultIndex = 0 // 默认值
|
||||
// let defaultIndex = 0 // 默认值
|
||||
|
||||
if (field in titleMap) {
|
||||
defaultIndex = titleMap[field]
|
||||
} else if (field.includes('uharm')) {
|
||||
defaultIndex = 1
|
||||
} else if (field.includes('iharm')) {
|
||||
defaultIndex = 2
|
||||
}
|
||||
// if (field in titleMap) {
|
||||
// defaultIndex = titleMap[field]
|
||||
// } else if (field.includes('uharm')) {
|
||||
// defaultIndex = 1
|
||||
// } else if (field.includes('iharm')) {
|
||||
// defaultIndex = 2
|
||||
// }
|
||||
let codeKey = field.includes('flickerOvertime')
|
||||
? '闪变'
|
||||
: field.includes('uharm')
|
||||
? '谐波电压'
|
||||
: field.includes('iharm')
|
||||
? '谐波电流'
|
||||
: field.includes('voltageDevOvertime')
|
||||
? '电压偏差'
|
||||
: field.includes('ubalanceOvertime')
|
||||
? '不平衡'
|
||||
: ''
|
||||
let defaultIndex = indexOptions.value.findIndex((item: any) => item.name.includes(codeKey)) || 0
|
||||
|
||||
searchForm.value.index[0] = indexOptions.value[defaultIndex].id
|
||||
})
|
||||
@@ -291,7 +302,7 @@ const init = async () => {
|
||||
}
|
||||
lists[index] = {
|
||||
statisticalId: item.index,
|
||||
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
|
||||
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
|
||||
}
|
||||
})
|
||||
let obj = {
|
||||
@@ -576,7 +587,6 @@ const setTimeControl = () => {
|
||||
}
|
||||
|
||||
const selectChange = (flag: boolean, height: any) => {
|
||||
|
||||
pageHeight.value = mainHeight(height * 1.6, 1.6)
|
||||
}
|
||||
//导出
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
v-model="tableStore.table.params.lineId"
|
||||
placeholder="请选择监测点"
|
||||
style="width: 150px"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.lineId"
|
||||
:label="item.name"
|
||||
:label="item.lineName"
|
||||
:value="item.lineId"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -23,11 +24,7 @@
|
||||
<Table ref="tableRef" @cell-click="cellClickEvent" isGroup :height="height"></Table>
|
||||
</el-dialog>
|
||||
<!-- 谐波电流、谐波电压占有率 -->
|
||||
<HarmonicRatio
|
||||
ref="harmonicRatioRef"
|
||||
@close="onHarmonicRatioClose"
|
||||
v-if="dialogFlag"
|
||||
/>
|
||||
<HarmonicRatio ref="harmonicRatioRef" @close="onHarmonicRatioClose" v-if="dialogFlag" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@@ -53,7 +50,7 @@ const loop50 = (key: string) => {
|
||||
list.push({
|
||||
title: i + '次',
|
||||
field: key + i + 'Overtime',
|
||||
width: '80',
|
||||
width: '60',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
|
||||
@@ -89,9 +86,9 @@ const tableStore: any = new TableStore({
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
title: '闪变越限(分钟)',
|
||||
title: '越限(分钟)',
|
||||
field: 'flickerOvertime',
|
||||
width: '80',
|
||||
width: '90',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
|
||||
@@ -104,6 +101,14 @@ const tableStore: any = new TableStore({
|
||||
{
|
||||
title: '谐波电流越限(分钟)',
|
||||
children: loop50('iharm')
|
||||
}, {
|
||||
title: '电压偏差越限(分钟)',
|
||||
field: 'uaberranceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '三相不平衡度越限(分钟)',
|
||||
@@ -114,24 +119,16 @@ const tableStore: any = new TableStore({
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '电压偏差越限(分钟)',
|
||||
field: 'uaberranceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.uaberranceOvertime}</span>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '频率偏差越限(分钟)',
|
||||
field: 'freqDevOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
}
|
||||
}
|
||||
|
||||
// {
|
||||
// title: '频率偏差越限(分钟)',
|
||||
// field: 'freqDevOvertime',
|
||||
// width: '100',
|
||||
// render: 'customTemplate',
|
||||
// customTemplate: (row: any) => {
|
||||
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
// }
|
||||
// }
|
||||
],
|
||||
beforeSearchFun: () => {
|
||||
},
|
||||
@@ -143,9 +140,10 @@ const tableStore: any = new TableStore({
|
||||
provide('tableStore', tableStore)
|
||||
tableStore.table.params.sortBy = ''
|
||||
tableStore.table.params.orderBy = ''
|
||||
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => {
|
||||
const open = async (row: any,searchBeginTime:any,searchEndTime:any,data:any=[]) => {
|
||||
dialogVisible.value = true
|
||||
initCSlineList()
|
||||
// initCSlineList()
|
||||
options.value = data
|
||||
tableStore.table.params.lineId = row.lineId
|
||||
|
||||
nextTick(() => {
|
||||
@@ -179,8 +177,8 @@ const onHarmonicRatioClose = () => {
|
||||
}
|
||||
|
||||
const initCSlineList = async () => {
|
||||
const res = await cslineList({})
|
||||
options.value = res.data
|
||||
// const res = await cslineList({})
|
||||
// options.value = res.data
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--主要监测点列表 -->
|
||||
<TableHeader :showReset="false" @selectChange="selectChange" v-if="fullscreen" datePicker ref="TableHeaderRef">
|
||||
<TableHeader
|
||||
:showReset="false"
|
||||
:timeKeyList="prop.timeKey"
|
||||
@selectChange="selectChange"
|
||||
v-if="fullscreen"
|
||||
|
||||
ref="TableHeaderRef"
|
||||
>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model="tableStore.table.params.keywords" clearable placeholder="请输关键字" />
|
||||
<el-form-item label="关键字筛选">
|
||||
<el-input v-model="tableStore.table.params.keywords" clearable placeholder="请输入监测点名称" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</TableHeader>
|
||||
@@ -22,7 +29,7 @@ import { ref, onMounted, provide, reactive, watch, nextTick } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
import { getTime } from '@/utils/formatTime'
|
||||
import { getTimeOfTheMonth } from '@/utils/formatTime'
|
||||
import OverLimitDetails from '@/components/cockpit/indicatorFittingChart/components/overLimitDetails.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useTimeCacheStore } from '@/stores/timeCache'
|
||||
@@ -32,7 +39,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -59,11 +66,11 @@ const fullscreen = computed(() => {
|
||||
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
|
||||
headerHeight.value = height
|
||||
|
||||
if (datePickerValue && datePickerValue.timeValue) {
|
||||
// 更新时间参数
|
||||
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
}
|
||||
// if (datePickerValue && datePickerValue.timeValue) {
|
||||
// // 更新时间参数
|
||||
// tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
// tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
// }
|
||||
}
|
||||
|
||||
const tableStore: any = new TableStore({
|
||||
@@ -93,18 +100,24 @@ const tableStore: any = new TableStore({
|
||||
{
|
||||
title: '监测对象类型',
|
||||
field: 'objType',
|
||||
minWidth: '90'
|
||||
minWidth: '90',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '是否治理',
|
||||
field: 'govern',
|
||||
minWidth: '70'
|
||||
minWidth: '80',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
},
|
||||
|
||||
{ title: '主要存在的电能质量问题', field: 'problems', minWidth: '150', showOverflow: true }
|
||||
],
|
||||
beforeSearchFun: () => {
|
||||
setTime()
|
||||
setTime()
|
||||
},
|
||||
loadCallback: () => {
|
||||
tableStore.table.height = `calc(${prop.height} - 80px)`
|
||||
@@ -119,31 +132,34 @@ provide('tableStore', tableStore)
|
||||
// 点击行
|
||||
const cellClickEvent = ({ row, column }: any) => {
|
||||
if (column.field == 'lineName') {
|
||||
|
||||
let time = getTimeOfTheMonth('3');
|
||||
OverLimitDetailsRef.value.open(
|
||||
row,
|
||||
tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
|
||||
tableStore.table.params.searchEndTime || prop.timeValue?.[1]
|
||||
time[0],
|
||||
time[1],
|
||||
tableStore.table.data
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const setTime = () => {
|
||||
const time = getTime(
|
||||
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
prop.timeKey,
|
||||
fullscreen.value
|
||||
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
: prop.timeValue
|
||||
)
|
||||
// const time = getTime(
|
||||
// (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
// prop.timeKey,
|
||||
// fullscreen.value
|
||||
// ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
// : prop.timeValue
|
||||
// )
|
||||
|
||||
if (Array.isArray(time)) {
|
||||
tableStore.table.params.searchBeginTime = time[0]
|
||||
tableStore.table.params.searchEndTime = time[1]
|
||||
// TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
// TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
} else {
|
||||
console.warn('获取时间失败,time 不是一个有效数组')
|
||||
}
|
||||
// if (Array.isArray(time)) {
|
||||
// tableStore.table.params.searchBeginTime = time[0]
|
||||
// tableStore.table.params.searchEndTime = time[1]
|
||||
// // TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
// // TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
// } else {
|
||||
// console.warn('获取时间失败,time 不是一个有效数组')
|
||||
// }
|
||||
}
|
||||
|
||||
// 在组件挂载时设置缓存值到 DatePicker
|
||||
|
||||
@@ -69,7 +69,7 @@ const tableStore: any = new TableStore({
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
title: '闪变越限(分钟)',
|
||||
title: '长时闪变越限(分钟)',
|
||||
field: 'flicker',
|
||||
width: '80'
|
||||
},
|
||||
@@ -99,26 +99,7 @@ const tableStore: any = new TableStore({
|
||||
],
|
||||
beforeSearchFun: () => {},
|
||||
loadCallback: () => {
|
||||
tableStore.table.data = [
|
||||
{
|
||||
time: '2024-01-01 00:00:00',
|
||||
name: '35kV进线',
|
||||
flicker: '0',
|
||||
|
||||
},
|
||||
{
|
||||
time: '2024-01-01 00:00:00',
|
||||
name: '35kV进线',
|
||||
flicker: '0',
|
||||
|
||||
},
|
||||
{
|
||||
time: '2024-01-01 00:00:00',
|
||||
name: '35kV进线',
|
||||
flicker: '0',
|
||||
|
||||
},
|
||||
]
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ const tableStore: any = new TableStore({
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
title: '闪变越限(分钟)',
|
||||
title: '长时闪变越限(分钟)',
|
||||
field: 'flicker',
|
||||
width: '80',
|
||||
render: 'customTemplate',
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
@selectChange="selectChange"
|
||||
v-if="fullscreen"
|
||||
ref="TableHeaderRef"
|
||||
:timeKeyList="['4', '5']"
|
||||
:timeKeyList="prop.timeKey"
|
||||
>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="监测点">
|
||||
<el-select v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable>
|
||||
<el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点" clearable>
|
||||
<el-option
|
||||
v-for="item in lineList"
|
||||
:key="item.lineId"
|
||||
@@ -20,7 +20,12 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户功率">
|
||||
<el-select v-model="tableStore.table.params.power" placeholder="请选择用户功率" clearable>
|
||||
<el-select
|
||||
filterable
|
||||
v-model="tableStore.table.params.power"
|
||||
placeholder="请选择用户功率"
|
||||
clearable
|
||||
>
|
||||
<el-option v-for="item in powerList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -29,6 +34,7 @@
|
||||
style="min-width: 120px !important"
|
||||
placeholder="请选择"
|
||||
v-model="tableStore.table.params.valueType"
|
||||
filterable
|
||||
>
|
||||
<el-option value="max" label="最大值"></el-option>
|
||||
<el-option value="min" label="最小值"></el-option>
|
||||
@@ -37,7 +43,12 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="电能质量指标">
|
||||
<el-select v-model="tableStore.table.params.indicator" placeholder="请选择电能质量指标" clearable>
|
||||
<el-select
|
||||
filterable
|
||||
v-model="tableStore.table.params.indicator"
|
||||
placeholder="请选择电能质量指标"
|
||||
clearable
|
||||
>
|
||||
<el-option v-for="item in indicatorList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -48,6 +59,7 @@
|
||||
v-model="tableStore.table.params.harmonicCount"
|
||||
placeholder="请选择谐波次数"
|
||||
style="min-width: 80px !important"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="num in harmonicCountOptions"
|
||||
@@ -63,12 +75,21 @@
|
||||
<div v-loading="tableStore.table.loading">
|
||||
<my-echart
|
||||
class="tall"
|
||||
v-if="lineShow"
|
||||
:options="echartList"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px )`
|
||||
}"
|
||||
/>
|
||||
<el-empty
|
||||
v-else
|
||||
description="暂无监测点"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -88,7 +109,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -102,7 +123,7 @@ const lineList: any = ref()
|
||||
const powerList: any = ref()
|
||||
|
||||
const chartsList = ref<any>([])
|
||||
|
||||
const lineShow = ref(true)
|
||||
// 计算是否全屏展示
|
||||
const fullscreen = computed(() => {
|
||||
const w = Number(prop.w)
|
||||
@@ -129,8 +150,14 @@ const indicatorList = ref()
|
||||
|
||||
const initLineList = async () => {
|
||||
cslineList({}).then(res => {
|
||||
if (res.data.length == 0) {
|
||||
lineShow.value = false
|
||||
return (tableStore.table.loading = false)
|
||||
}
|
||||
lineShow.value = true
|
||||
lineList.value = res.data
|
||||
tableStore.table.params.lineId = lineList.value[0].lineId
|
||||
initCode()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -156,6 +183,30 @@ const setEchart = () => {
|
||||
title: {
|
||||
text: `${indicatorName}与${powerName}负荷曲线拟合图`
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (params: any) {
|
||||
let result = params[0].axisValueLabel
|
||||
params.forEach((item: any) => {
|
||||
if (item.seriesName === indicatorName) {
|
||||
// 对于电能质量指标,格式化Y轴值显示
|
||||
let valueText = ''
|
||||
if (item.value[1] == 0) {
|
||||
valueText = '不越限'
|
||||
} else if (item.value[1] == 1) {
|
||||
valueText = '越限'
|
||||
} else {
|
||||
valueText = item.value[1]
|
||||
}
|
||||
result += `<br/>${item.marker}${item.seriesName}: ${valueText}`
|
||||
} else {
|
||||
// 对于功率数据,正常显示数值
|
||||
result += `<br/>${item.marker}${item.seriesName}: ${item.value[1]} ${item.value[2]}`
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
axisLabel: {
|
||||
@@ -168,20 +219,22 @@ const setEchart = () => {
|
||||
},
|
||||
yAxis: [
|
||||
{},
|
||||
indicatorName ? {
|
||||
min: 0,
|
||||
max: 1,
|
||||
axisLabel: {
|
||||
formatter: function(value: number) {
|
||||
if (value === 0) {
|
||||
return '不越限'
|
||||
} else if (value === 1) {
|
||||
return '越限'
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
} : {}
|
||||
indicatorName
|
||||
? {
|
||||
min: 0,
|
||||
max: 1,
|
||||
axisLabel: {
|
||||
formatter: function (value: number) {
|
||||
if (value === 0) {
|
||||
return '不越限'
|
||||
} else if (value === 1) {
|
||||
return '越限'
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
: {}
|
||||
],
|
||||
grid: {
|
||||
left: '10px',
|
||||
@@ -209,6 +262,7 @@ const setEchart = () => {
|
||||
{
|
||||
name: indicatorName, // 动态设置指标名称
|
||||
type: 'line',
|
||||
step: 'end',
|
||||
showSymbol: false,
|
||||
// smooth: true,
|
||||
data: [],
|
||||
@@ -238,7 +292,8 @@ const setEchart = () => {
|
||||
item.time,
|
||||
item.statisticalData !== null && item.statisticalData !== undefined
|
||||
? parseFloat(item.statisticalData.toFixed(2))
|
||||
: null
|
||||
: null,
|
||||
item.unit
|
||||
]
|
||||
})
|
||||
|
||||
@@ -248,7 +303,8 @@ const setEchart = () => {
|
||||
item.time,
|
||||
item.statisticalData !== null && item.statisticalData !== undefined
|
||||
? parseFloat(item.statisticalData.toFixed(2))
|
||||
: null
|
||||
: null,
|
||||
item.unit
|
||||
]
|
||||
})
|
||||
|
||||
@@ -393,10 +449,8 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
initLineList().then(() => {
|
||||
initCode()
|
||||
})
|
||||
onMounted(async () => {
|
||||
await initLineList()
|
||||
})
|
||||
|
||||
watch(
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
v-model="tableStore.table.params.lineId"
|
||||
placeholder="请选择监测点"
|
||||
style="width: 150px"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.lineId"
|
||||
:label="item.name"
|
||||
:label="item.lineName"
|
||||
:value="item.lineId"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -33,7 +34,7 @@ import TableHeader from '@/components/table/header/index.vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { mainHeight } from '@/utils/layout'
|
||||
import HarmonicRatio from '@/components/cockpit/gridSideStatistics/components/harmonicRatio.vue'
|
||||
import { cslineList } from '@/api/harmonic-boot/cockpit/cockpit'
|
||||
import { cslineList ,governLineList} from '@/api/harmonic-boot/cockpit/cockpit'
|
||||
|
||||
const dialogVisible: any = ref(false)
|
||||
const harmonicRatioRef: any = ref(null)
|
||||
@@ -48,7 +49,7 @@ const loop50 = (key: string) => {
|
||||
list.push({
|
||||
title: i + '次',
|
||||
field: key + i + 'Overtime',
|
||||
width: '80',
|
||||
width: '60',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
|
||||
@@ -84,9 +85,9 @@ const tableStore: any = new TableStore({
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
title: '闪变越限(%)',
|
||||
title: '长时闪变越限(%)',
|
||||
field: 'flickerOvertime',
|
||||
width: '80',
|
||||
width: '90',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
|
||||
@@ -100,16 +101,7 @@ const tableStore: any = new TableStore({
|
||||
title: '谐波电流越限(%)',
|
||||
children: loop50('iharm')
|
||||
},
|
||||
{
|
||||
title: '三相不平衡度越限(%)',
|
||||
field: 'ubalanceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
title: '电压偏差越限(%)',
|
||||
field: 'voltageDevOvertime',
|
||||
width: '100',
|
||||
@@ -119,14 +111,24 @@ const tableStore: any = new TableStore({
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '频率偏差越限(%)',
|
||||
field: 'freqDevOvertime',
|
||||
title: '三相不平衡度越限(%)',
|
||||
field: 'ubalanceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// {
|
||||
// title: '频率偏差越限(%)',
|
||||
// field: 'freqDevOvertime',
|
||||
// width: '100',
|
||||
// render: 'customTemplate',
|
||||
// customTemplate: (row: any) => {
|
||||
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
// }
|
||||
// }
|
||||
],
|
||||
beforeSearchFun: () => {
|
||||
},
|
||||
@@ -138,8 +140,10 @@ const tableStore: any = new TableStore({
|
||||
provide('tableStore', tableStore)
|
||||
tableStore.table.params.sortBy = ''
|
||||
tableStore.table.params.orderBy = ''
|
||||
const time:any=ref([])
|
||||
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => {
|
||||
dialogVisible.value = true
|
||||
time.value=[searchBeginTime,searchEndTime]
|
||||
initCSlineList()
|
||||
tableStore.table.params.lineId = row.lineId
|
||||
|
||||
@@ -174,8 +178,19 @@ const onHarmonicRatioClose = () => {
|
||||
}
|
||||
|
||||
const initCSlineList = async () => {
|
||||
const res = await cslineList({})
|
||||
options.value = res.data
|
||||
// const res = await cslineList({})
|
||||
const res = await governLineList({
|
||||
endTime:time.value[1],
|
||||
keywords:"",
|
||||
pageNum:1,
|
||||
pageSize:10000,
|
||||
searchBeginTime:time.value[0],
|
||||
searchEndTime:time.value[1],
|
||||
startTime:time.value[0],
|
||||
timeFlag:1
|
||||
})
|
||||
options.value = res.data.records
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
ref="TableHeaderRef"
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
|
||||
v-if="fullscreen"
|
||||
:timeKeyList="prop.timeKey"
|
||||
></TableHeader>
|
||||
<Table
|
||||
ref="tableRef"
|
||||
@@ -17,27 +18,35 @@
|
||||
<OverLimitDetails ref="OverLimitDetailsRef" />
|
||||
|
||||
<!-- 上传对话框 -->
|
||||
<el-dialog v-model="uploadDialogVisible" title="上传报告" width="500px" @closed="handleDialogClosed">
|
||||
<el-dialog
|
||||
v-model="uploadDialogVisible"
|
||||
title="上传报告"
|
||||
append-to-body
|
||||
width="500px"
|
||||
@closed="handleDialogClosed"
|
||||
>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
class="upload-demo"
|
||||
:auto-upload="true"
|
||||
action=""
|
||||
accept=".doc,.docx,.PDF"
|
||||
:on-change="handleChange"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:limit="1"
|
||||
:auto-upload="false"
|
||||
:on-exceed="handleExceed"
|
||||
:on-remove="handleRemove"
|
||||
:file-list="fileList"
|
||||
>
|
||||
<el-button type="primary">点击上传</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传Word或PDF文件,且不超过10MB</div>
|
||||
<div class="el-upload__tip">请上传Word或PDF文件</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="uploadDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="uploadDialogVisible = false">确定</el-button>
|
||||
<el-button type="primary" @click="handleUpload">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -59,7 +68,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -77,11 +86,11 @@ const fileList = ref([])
|
||||
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
|
||||
headerHeight.value = height
|
||||
|
||||
if (datePickerValue && datePickerValue.timeValue) {
|
||||
// 更新时间参数
|
||||
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
}
|
||||
// if (datePickerValue && datePickerValue.timeValue) {
|
||||
// // 更新时间参数
|
||||
// tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
// tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
// }
|
||||
}
|
||||
|
||||
// 计算是否全屏展示
|
||||
@@ -111,32 +120,6 @@ const tableStore: any = new TableStore({
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '治理对象',
|
||||
field: 'sensitiveUser',
|
||||
minWidth: '80'
|
||||
},
|
||||
|
||||
{
|
||||
title: '电压等级',
|
||||
field: 'volGrade',
|
||||
minWidth: '70'
|
||||
},
|
||||
{
|
||||
title: '是否治理',
|
||||
field: 'govern',
|
||||
minWidth: '70'
|
||||
},
|
||||
|
||||
// {
|
||||
// title: '治理前报告',
|
||||
// field: 'reportFileName',
|
||||
// minWidth: '80',
|
||||
// render: 'customTemplate',
|
||||
// customTemplate: (row: any) => {
|
||||
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.reportFileName}</span>`
|
||||
// }
|
||||
// },
|
||||
{
|
||||
title: '监测点名称',
|
||||
field: 'lineName',
|
||||
@@ -146,16 +129,78 @@ const tableStore: any = new TableStore({
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.lineName}</span>`
|
||||
}
|
||||
},
|
||||
{ title: '监测类型', field: 'position', minWidth: '70' },
|
||||
{
|
||||
title: '监测类型',
|
||||
field: 'position',
|
||||
minWidth: '80',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
},
|
||||
// {
|
||||
// title: '监测点状态',
|
||||
// field: 'runStatus',
|
||||
// minWidth: '90',
|
||||
// render: 'customTemplate',
|
||||
// customTemplate: (row: any) => {
|
||||
// return `<span style='color: ${row.runStatus === '中断' ? '#FF0000' : ''}'>${row.runStatus==null?'/':row.runStatus}</span>`
|
||||
// }
|
||||
// },
|
||||
{
|
||||
title: '监测点状态',
|
||||
field: 'runStatus',
|
||||
minWidth: '80',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='color: ${row.runStatus === '中断' ? '#FF0000' : ''}'>${row.runStatus}</span>`
|
||||
render: 'tag',
|
||||
|
||||
width: 100,
|
||||
custom: {
|
||||
停运: 'danger',
|
||||
退运: 'danger',
|
||||
运行: 'success',
|
||||
在线: 'success',
|
||||
中断: 'warning',
|
||||
离线: 'danger',
|
||||
检修: 'warning',
|
||||
调试: 'warning',
|
||||
null: 'info'
|
||||
},
|
||||
replaceValue: {
|
||||
运行: '运行',
|
||||
在线: '在线',
|
||||
退运: '退运',
|
||||
停运: '停运',
|
||||
中断: '中断',
|
||||
检修: '检修',
|
||||
离线: '离线',
|
||||
调试: '调试',
|
||||
null: '/'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '治理对象',
|
||||
field: 'sensitiveUser',
|
||||
minWidth: '90',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
title: '电压等级',
|
||||
field: 'volGrade',
|
||||
minWidth: '80',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue==0?'/': row.cellValue+'kV' || '/'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '是否治理',
|
||||
field: 'govern',
|
||||
minWidth: '80',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
title: '最新数据时间',
|
||||
field: 'latestTime',
|
||||
@@ -169,10 +214,30 @@ const tableStore: any = new TableStore({
|
||||
}
|
||||
}
|
||||
},
|
||||
// {
|
||||
// title: '报告',
|
||||
// field: 'reportFilePath',
|
||||
// minWidth: '120',
|
||||
// render: 'customTemplate',
|
||||
// customTemplate: (row: any) => {
|
||||
// return row.reportFilePath == null
|
||||
// ? '/'
|
||||
// : `<span style='cursor: pointer;text-decoration: underline;'>${row.reportFilePath
|
||||
// .split('/')
|
||||
// .pop()}</span>`
|
||||
// }
|
||||
// },
|
||||
{
|
||||
title: '操作',
|
||||
minWidth: 80,
|
||||
// fixed: 'right',
|
||||
title: '报告',
|
||||
field: 'reportFilePath',
|
||||
minWidth: '120',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue == null ? '/' : row.cellValue.split('/').pop()
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作', fixed: 'right',
|
||||
width: 150,
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
@@ -196,7 +261,7 @@ const tableStore: any = new TableStore({
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
downloadTheReport(row.lineId)
|
||||
downloadTheReport(row.lineId, row.reportFilePath)
|
||||
},
|
||||
disabled: row => {
|
||||
return row.reportFilePath == null || row.reportFilePath.length == 0
|
||||
@@ -232,22 +297,22 @@ tableStore.table.params.keywords = ''
|
||||
provide('tableStore', tableStore)
|
||||
|
||||
const setTime = () => {
|
||||
const time = getTime(
|
||||
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
prop.timeKey,
|
||||
fullscreen.value
|
||||
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
: prop.timeValue
|
||||
)
|
||||
// const time = getTime(
|
||||
// (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
// prop.timeKey,
|
||||
// fullscreen.value
|
||||
// ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
// : prop.timeValue
|
||||
// )
|
||||
|
||||
if (Array.isArray(time)) {
|
||||
tableStore.table.params.searchBeginTime = time[0]
|
||||
tableStore.table.params.searchEndTime = time[1]
|
||||
TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
} else {
|
||||
console.warn('获取时间失败,time 不是一个有效数组')
|
||||
}
|
||||
// if (Array.isArray(time)) {
|
||||
// tableStore.table.params.searchBeginTime = time[0]
|
||||
// tableStore.table.params.searchEndTime = time[1]
|
||||
// TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
// TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
// } else {
|
||||
// console.warn('获取时间失败,time 不是一个有效数组')
|
||||
// }
|
||||
}
|
||||
|
||||
// 点击行
|
||||
@@ -262,16 +327,43 @@ const cellClickEvent = ({ row, column }: any) => {
|
||||
}
|
||||
|
||||
// 下载报告
|
||||
const downloadTheReport = (lineId: string) => {
|
||||
const downloadTheReport = (lineId: string, name: string) => {
|
||||
getReportUrl({ lineId: lineId }).then((res: any) => {
|
||||
const link = document.createElement('a')
|
||||
link.href = res.data
|
||||
link.download = '治理报告'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
forceDownloadPdf(res.data, name.split('/').pop() || '')
|
||||
})
|
||||
}
|
||||
const forceDownloadPdf = async (pdfUrl, fileName = '文件.pdf') => {
|
||||
try {
|
||||
// 1. 请求 PDF 并转为 Blob(关键:绕开浏览器直接解析)
|
||||
const response = await fetch(pdfUrl, {
|
||||
method: 'GET'
|
||||
// 若需要鉴权,添加请求头(如 token)
|
||||
})
|
||||
|
||||
// 校验响应是否成功
|
||||
if (!response.ok) throw new Error(`请求失败:${response.status}`)
|
||||
|
||||
// 2. 将响应转为 Blob(指定类型为 PDF,确保兼容性)
|
||||
const blob = await response.blob()
|
||||
const pdfBlob = new Blob([blob], { type: 'application/pdf' })
|
||||
|
||||
// 3. 创建临时 URL 并触发下载
|
||||
const blobUrl = URL.createObjectURL(pdfBlob)
|
||||
const a = document.createElement('a')
|
||||
a.href = blobUrl
|
||||
a.download = fileName // 此时 Blob URL 是同源的,download 必生效
|
||||
a.style.display = 'none'
|
||||
document.body.appendChild(a)
|
||||
a.click() // 触发下载
|
||||
|
||||
// 4. 清理资源(避免内存泄漏)
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(blobUrl)
|
||||
} catch (error) {
|
||||
console.error('PDF 下载失败:', error)
|
||||
// ElMessage.error('文件下载失败,请检查网络或文件地址') // 适配 Element Plus
|
||||
}
|
||||
}
|
||||
|
||||
// 上传报告
|
||||
const uploadReportRow = (row: any) => {
|
||||
@@ -288,14 +380,18 @@ const handleDialogClosed = () => {
|
||||
}
|
||||
|
||||
// 处理文件超出限制
|
||||
const handleExceed = (files: any, fileList: any) => {
|
||||
const handleExceed = (files: any) => {
|
||||
ElMessage.warning('只能上传一个文件,请先删除已选择的文件')
|
||||
}
|
||||
const handleRemove = (files: any) => {
|
||||
fileList.value = []
|
||||
}
|
||||
|
||||
// 文件变更处理函数
|
||||
const handleChange = (file: any, fileList: any) => {
|
||||
const handleChange = (file: any) => {
|
||||
// 在这里直接处理文件上传逻辑
|
||||
beforeUpload(file.raw) // 注意使用 file.raw 获取原始文件对象
|
||||
// beforeUpload(file.raw) // 注意使用 file.raw 获取原始文件对象
|
||||
fileList.value = [file] // 只保留最新选择的文件
|
||||
}
|
||||
|
||||
// 处理上传前检查
|
||||
@@ -312,11 +408,7 @@ const beforeUpload = (file: any) => {
|
||||
const isLt10M = file.size / 1024 / 1024 < 10
|
||||
|
||||
if (!isValidType) {
|
||||
ElMessage.error('上传文件只能是 Word 文档(.doc/.docx) 或 PDF 文件(.pdf)!')
|
||||
return false
|
||||
}
|
||||
if (!isLt10M) {
|
||||
ElMessage.error('上传文件大小不能超过 10MB!')
|
||||
ElMessage.error('请上传(.doc/.docx/.pdf)格式文件!')
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -324,10 +416,11 @@ const beforeUpload = (file: any) => {
|
||||
return true
|
||||
}
|
||||
|
||||
const handleUpload = async (options: any) => {
|
||||
const { file } = options
|
||||
const handleUpload = async () => {
|
||||
// return
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
formData.append('file', fileList.value[0]?.raw)
|
||||
formData.append('lineId', currentUploadRow.value?.lineId || currentUploadRow.value?.id || '')
|
||||
|
||||
try {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
v-model="searchForm.index"
|
||||
placeholder="请选择统计指标"
|
||||
@change="onIndexChange($event)"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in indexOptions"
|
||||
@@ -36,6 +37,7 @@
|
||||
style="min-width: 120px !important"
|
||||
placeholder="请选择"
|
||||
v-model="searchForm.valueType"
|
||||
filterable
|
||||
>
|
||||
<el-option value="max" label="最大值"></el-option>
|
||||
<el-option value="min" label="最小值"></el-option>
|
||||
@@ -59,11 +61,12 @@
|
||||
placeholder="请选择谐波次数"
|
||||
style="width: 100px"
|
||||
class="mr20"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="vv in item.countOptions"
|
||||
:key="vv"
|
||||
:label="vv"
|
||||
:label="item.name.includes('间谐波') ? vv - 0.5 : vv"
|
||||
:value="vv"
|
||||
></el-option>
|
||||
</el-select>
|
||||
@@ -79,11 +82,7 @@
|
||||
</TableHeader>
|
||||
</div>
|
||||
<div class="history_chart" :style="pageHeight" v-loading="loading">
|
||||
<MyEchart
|
||||
ref="historyChart"
|
||||
:options="echartsData"
|
||||
v-if="showEchart"
|
||||
/>
|
||||
<MyEchart ref="historyChart" :options="echartsData" v-if="showEchart" />
|
||||
<el-empty :style="pageHeight" v-else description="暂无数据" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -157,28 +156,40 @@ const countOptions: any = ref([])
|
||||
const legendDictList: any = ref([])
|
||||
|
||||
const initCode = (field: string, title: string) => {
|
||||
queryByCode('steady_state_limit_trend').then(res => {
|
||||
queryByCode('gridSide_exceedTheLimit').then(res => {
|
||||
queryCsDictTree(res.data.id).then(item => {
|
||||
//排序
|
||||
indexOptions.value = item.data.sort((a: any, b: any) => {
|
||||
return a.sort - b.sort
|
||||
})
|
||||
const titleMap: Record<string, number> = {
|
||||
flickerOvertime: 0,
|
||||
uaberranceOvertime: 3,
|
||||
ubalanceOvertime: 4,
|
||||
freqDevOvertime: 5
|
||||
}
|
||||
// const titleMap: Record<string, number> = {
|
||||
// flickerOvertime: 0,
|
||||
// uaberranceOvertime: 3,
|
||||
// ubalanceOvertime: 4,
|
||||
// freqDevOvertime: 5
|
||||
// }
|
||||
|
||||
let defaultIndex = 0 // 默认值
|
||||
// let defaultIndex = 0 // 默认值
|
||||
|
||||
if (field in titleMap) {
|
||||
defaultIndex = titleMap[field]
|
||||
} else if (field.includes('uharm')) {
|
||||
defaultIndex = 1
|
||||
} else if (field.includes('iharm')) {
|
||||
defaultIndex = 2
|
||||
}
|
||||
// if (field in titleMap) {
|
||||
// defaultIndex = titleMap[field]
|
||||
// } else if (field.includes('uharm')) {
|
||||
// defaultIndex = 1
|
||||
// } else if (field.includes('iharm')) {
|
||||
// defaultIndex = 2
|
||||
// }
|
||||
let codeKey = field.includes('flickerOvertime')
|
||||
? '闪变'
|
||||
: field.includes('uharm')
|
||||
? '谐波电压'
|
||||
: field.includes('iharm')
|
||||
? '谐波电流'
|
||||
: field.includes('voltageDevOvertime')
|
||||
? '电压偏差'
|
||||
: field.includes('ubalanceOvertime')
|
||||
? '不平衡'
|
||||
: ''
|
||||
let defaultIndex = indexOptions.value.findIndex((item: any) => item.name.includes(codeKey)) || 0
|
||||
|
||||
searchForm.value.index[0] = indexOptions.value[defaultIndex].id
|
||||
})
|
||||
@@ -202,7 +213,7 @@ const initCode = (field: string, title: string) => {
|
||||
if (kk.harmStart && kk.harmEnd) {
|
||||
range(0, 0, 0)
|
||||
|
||||
if (kk.showName == '间谐波电压含有率') {
|
||||
if (kk.showName.includes('间谐波电压')) {
|
||||
countDataCopy.value[index].countOptions = range(kk.harmStart, kk.harmEnd, 1).map(
|
||||
(item: any) => {
|
||||
return item - 0.5
|
||||
@@ -291,7 +302,7 @@ const init = async () => {
|
||||
}
|
||||
lists[index] = {
|
||||
statisticalId: item.index,
|
||||
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
|
||||
frequency: frequencys !== null && frequencys !== undefined ? String(frequencys) : ''
|
||||
}
|
||||
})
|
||||
let obj = {
|
||||
@@ -597,12 +608,12 @@ const formatCountOptions = () => {
|
||||
})
|
||||
|
||||
countData.value.map((item: any, key: any) => {
|
||||
if (item.name == '谐波电流有效值') {
|
||||
item.name = '谐波电流次数'
|
||||
} else if (item.name == '谐波电压含有率') {
|
||||
item.name = '谐波电压次数'
|
||||
} else if (item.name == '间谐波电压含有率') {
|
||||
if (item.name.includes('间谐波电压')) {
|
||||
item.name = '间谐波电压次数'
|
||||
} else if (item.name.includes('谐波电流')) {
|
||||
item.name = '谐波电流次数'
|
||||
} else if (item.name.includes('谐波电压')) {
|
||||
item.name = '谐波电压次数'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
v-model="tableStore.table.params.lineId"
|
||||
placeholder="请选择监测点"
|
||||
style="width: 150px"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.lineId"
|
||||
:label="item.name"
|
||||
:label="item.lineName"
|
||||
:value="item.lineId"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -48,7 +49,7 @@ const loop50 = (key: string) => {
|
||||
list.push({
|
||||
title: i + '次',
|
||||
field: key + i + 'Overtime',
|
||||
width: '80',
|
||||
width: '60',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row[key + i + 'Overtime']}</span>`
|
||||
@@ -84,9 +85,9 @@ const tableStore: any = new TableStore({
|
||||
width: '150'
|
||||
},
|
||||
{
|
||||
title: '闪变越限(%)',
|
||||
title: '长时闪变越限(%)',
|
||||
field: 'flickerOvertime',
|
||||
width: '80',
|
||||
width: '90',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.flickerOvertime}</span>`
|
||||
@@ -100,16 +101,7 @@ const tableStore: any = new TableStore({
|
||||
title: '谐波电流越限(%)',
|
||||
children: loop50('iharm')
|
||||
},
|
||||
{
|
||||
title: '三相不平衡度越限(%)',
|
||||
field: 'ubalanceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
|
||||
}
|
||||
},
|
||||
{
|
||||
{
|
||||
title: '电压偏差越限(%)',
|
||||
field: 'voltageDevOvertime',
|
||||
width: '100',
|
||||
@@ -119,14 +111,24 @@ const tableStore: any = new TableStore({
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '频率偏差越限(%)',
|
||||
field: 'freqDevOvertime',
|
||||
title: '三相不平衡度越限(%)',
|
||||
field: 'ubalanceOvertime',
|
||||
width: '100',
|
||||
render: 'customTemplate',
|
||||
customTemplate: (row: any) => {
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
return `<span style='cursor: pointer;text-decoration: underline;'>${row.ubalanceOvertime}</span>`
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// {
|
||||
// title: '频率偏差越限(%)',
|
||||
// field: 'freqDevOvertime',
|
||||
// width: '100',
|
||||
// render: 'customTemplate',
|
||||
// customTemplate: (row: any) => {
|
||||
// return `<span style='cursor: pointer;text-decoration: underline;'>${row.freqDevOvertime}</span>`
|
||||
// }
|
||||
// }
|
||||
],
|
||||
beforeSearchFun: () => {
|
||||
},
|
||||
@@ -138,9 +140,10 @@ const tableStore: any = new TableStore({
|
||||
provide('tableStore', tableStore)
|
||||
tableStore.table.params.sortBy = ''
|
||||
tableStore.table.params.orderBy = ''
|
||||
const open = async (row: any,searchBeginTime:any,searchEndTime:any) => {
|
||||
const open = async (row: any,searchBeginTime:any,searchEndTime:any,data: any) => {
|
||||
dialogVisible.value = true
|
||||
initCSlineList()
|
||||
// initCSlineList()
|
||||
options.value = data
|
||||
tableStore.table.params.lineId = row.lineId
|
||||
|
||||
nextTick(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
ref="TableHeaderRef"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
v-if="fullscreen" :timeKeyList="prop.timeKey"
|
||||
></TableHeader>
|
||||
<my-echart
|
||||
class="tall"
|
||||
@@ -43,7 +43,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -89,7 +89,7 @@ const initEcharts = () => {
|
||||
|
||||
xAxis: {
|
||||
// name: '(区域)',
|
||||
data: ['闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
|
||||
data: ['长时闪变', '谐波电压', '谐波电流', '电压偏差', '三相不平衡']
|
||||
},
|
||||
|
||||
yAxis: {
|
||||
@@ -150,7 +150,7 @@ const tableStore: any = new TableStore({
|
||||
title: '越限占比(%)',
|
||||
children: [
|
||||
{
|
||||
title: '闪变',
|
||||
title: '长时闪变',
|
||||
field: 'flicker',
|
||||
minWidth: '70',
|
||||
render: 'customTemplate',
|
||||
@@ -217,7 +217,8 @@ const cellClickEvent = ({ row, column }: any) => {
|
||||
OverLimitDetailsRef.value.open(
|
||||
row,
|
||||
tableStore.table.params.searchBeginTime || prop.timeValue?.[0],
|
||||
tableStore.table.params.searchEndTime || prop.timeValue?.[1]
|
||||
tableStore.table.params.searchEndTime || prop.timeValue?.[1],
|
||||
tableStore.table.data
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
ref="TableHeaderRef"
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
|
||||
v-if="fullscreen"
|
||||
:timeKeyList="prop.timeKey"
|
||||
></TableHeader>
|
||||
<Table
|
||||
ref="tableRef"
|
||||
@@ -29,7 +30,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -39,16 +40,16 @@ const headerHeight = ref(57)
|
||||
const TableHeaderRef = ref()
|
||||
|
||||
const dictData = useDictData()
|
||||
const sensitiveUserType = dictData.getBasicData('Sensitive_User_Type')
|
||||
const sensitiveUserType = dictData.getBasicData('Interference_Source')
|
||||
|
||||
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
|
||||
headerHeight.value = height
|
||||
|
||||
if (datePickerValue && datePickerValue.timeValue) {
|
||||
// 更新时间参数
|
||||
tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
}
|
||||
// if (datePickerValue && datePickerValue.timeValue) {
|
||||
// // 更新时间参数
|
||||
// tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
|
||||
// tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
|
||||
// }
|
||||
}
|
||||
|
||||
// 计算是否全屏展示
|
||||
@@ -94,12 +95,18 @@ const tableStore: any = new TableStore({
|
||||
{
|
||||
title: '是否监测',
|
||||
field: 'isMonitor',
|
||||
minWidth: '80'
|
||||
minWidth: '80',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '是否治理',
|
||||
field: 'isGovern',
|
||||
minWidth: '80'
|
||||
minWidth: '80',
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue || '/'
|
||||
}
|
||||
}
|
||||
],
|
||||
beforeSearchFun: () => {
|
||||
@@ -123,22 +130,22 @@ const cellClickEvent = ({ row, column }: any) => {
|
||||
}
|
||||
|
||||
const setTime = () => {
|
||||
const time = getTime(
|
||||
(TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
prop.timeKey,
|
||||
fullscreen.value
|
||||
? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
: prop.timeValue
|
||||
)
|
||||
// const time = getTime(
|
||||
// (TableHeaderRef.value?.datePickerRef.interval || prop.interval) ?? 0,
|
||||
// prop.timeKey,
|
||||
// fullscreen.value
|
||||
// ? [tableStore.table.params.searchBeginTime, tableStore.table.params.searchEndTime]
|
||||
// : prop.timeValue
|
||||
// )
|
||||
|
||||
if (Array.isArray(time)) {
|
||||
tableStore.table.params.searchBeginTime = time[0]
|
||||
tableStore.table.params.searchEndTime = time[1]
|
||||
TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
} else {
|
||||
console.warn('获取时间失败,time 不是一个有效数组')
|
||||
}
|
||||
// if (Array.isArray(time)) {
|
||||
// tableStore.table.params.searchBeginTime = time[0]
|
||||
// tableStore.table.params.searchEndTime = time[1]
|
||||
// TableHeaderRef.value?.setInterval(time[2] - 0)
|
||||
// TableHeaderRef.value?.setTimeInterval([time[0], time[1]])
|
||||
// } else {
|
||||
// console.warn('获取时间失败,time 不是一个有效数组')
|
||||
// }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -132,7 +132,7 @@ const tableStore: any = new TableStore({
|
||||
},
|
||||
{
|
||||
title: '波形',
|
||||
width: '100',
|
||||
minWidth: '100',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
@@ -166,8 +166,8 @@ const tableStore: any = new TableStore({
|
||||
// ...row,
|
||||
// duration: row.persistTime // 将 persistTime 值赋给 duration
|
||||
// }
|
||||
boxoList.value.featureAmplitude =
|
||||
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
|
||||
boxoList.value.featureAmplitude = (row.amplitude - 0) / 100
|
||||
// row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
|
||||
boxoList.value.systemType = 'YPT'
|
||||
wp.value = res.data
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
:timeKeyList="['3']"
|
||||
:timeKeyList="prop.timeKey"
|
||||
></TableHeader>
|
||||
<el-calendar
|
||||
v-model="value"
|
||||
@@ -74,7 +74,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--暂态事件概率分布 -->
|
||||
<TableHeader ref="TableHeaderRef" :showReset="false" @selectChange="selectChange" datePicker v-if="fullscreen"></TableHeader>
|
||||
<TableHeader
|
||||
ref="TableHeaderRef"
|
||||
:timeKeyList="prop.timeKey"
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
></TableHeader>
|
||||
<my-echart
|
||||
class="tall"
|
||||
:options="echartList"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
<my-echart
|
||||
<!-- <my-echart
|
||||
class="mt10"
|
||||
:options="echartList1"
|
||||
:style="{
|
||||
width: prop.width,
|
||||
height: `calc(${prop.height} / 2 - ${headerHeight / 2}px + ${fullscreen ? 0 : 28}px )`
|
||||
}"
|
||||
/>
|
||||
/> -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@@ -33,7 +40,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -70,7 +77,6 @@ const echartList = ref({})
|
||||
|
||||
const echartList1 = ref({})
|
||||
|
||||
|
||||
const processDataForChart = (rawData: any[]) => {
|
||||
// 将后端返回的扁平数据转换为 ECharts 需要的三维坐标格式 [x, y, z]
|
||||
const chartData = rawData.map(item => [item.x, item.y, item.z])
|
||||
@@ -84,7 +90,7 @@ const tableStore: any = new TableStore({
|
||||
showPage: false,
|
||||
column: [],
|
||||
beforeSearchFun: () => {
|
||||
setTime()
|
||||
setTime()
|
||||
},
|
||||
loadCallback: () => {
|
||||
const processedData = processDataForChart(tableStore.table.data.innerList || [])
|
||||
@@ -177,15 +183,14 @@ const tableStore: any = new TableStore({
|
||||
type: 'category',
|
||||
name: '特征幅值',
|
||||
data: xLabels,
|
||||
nameGap: 40,
|
||||
|
||||
nameGap: 40
|
||||
},
|
||||
yAxis3D: {
|
||||
type: 'category',
|
||||
name: '持续时间',
|
||||
data: yLabels,
|
||||
nameGap: 40,
|
||||
|
||||
nameGap: 40,
|
||||
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
@@ -197,24 +202,26 @@ const tableStore: any = new TableStore({
|
||||
type: 'value',
|
||||
minInterval: 10,
|
||||
name: '暂态事件次数',
|
||||
nameGap: 30,
|
||||
nameGap: 30
|
||||
},
|
||||
grid3D: {
|
||||
viewControl: {
|
||||
projection: 'perspective',
|
||||
distance: 250
|
||||
},
|
||||
|
||||
boxWidth: 200,
|
||||
boxDepth: 80,
|
||||
light: {
|
||||
main: {
|
||||
intensity: 1.2
|
||||
projection: 'perspective',
|
||||
distance: 260,
|
||||
rotateSensitivity: 10,
|
||||
zoomSensitivity: 2
|
||||
},
|
||||
ambient: {
|
||||
intensity: 0.3
|
||||
boxWidth: 150,
|
||||
boxDepth: 100,
|
||||
boxHeight: 100,
|
||||
light: {
|
||||
main: {
|
||||
intensity: 1.2
|
||||
},
|
||||
ambient: {
|
||||
intensity: 0.4
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@@ -227,9 +234,7 @@ const tableStore: any = new TableStore({
|
||||
fontSize: 16,
|
||||
borderWidth: 1
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -296,7 +301,7 @@ watch(
|
||||
watch(
|
||||
() => prop.timeValue,
|
||||
(newVal, oldVal) => {
|
||||
tableStore.index()
|
||||
tableStore.index()
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<TableHeader datePicker showExport :showReset="false" ref="tableHeaderRef" @selectChange="selectChange">
|
||||
<template v-slot:select>
|
||||
<el-form-item label="监测点">
|
||||
<el-select v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称">
|
||||
<el-select filterable v-model="tableStore.table.params.lineId" placeholder="请选择监测点名称">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="item.lineId"
|
||||
:label="item.name"
|
||||
:value="item.lineId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -58,9 +58,9 @@ const boxoList: any = ref({})
|
||||
const tableHeaderRef = ref()
|
||||
|
||||
const options = ref()
|
||||
const heightRef = ref(mainHeight(168, 2.1).height)
|
||||
const heightRef = ref(mainHeight(168, 2.2).height)
|
||||
const selectChange = (flag: boolean, h: any) => {
|
||||
heightRef.value = mainHeight(h, 2.1).height
|
||||
heightRef.value = mainHeight(h, 2.2).height
|
||||
}
|
||||
|
||||
const getSimpleLineList = async () => {
|
||||
@@ -68,7 +68,6 @@ const getSimpleLineList = async () => {
|
||||
options.value = res.data
|
||||
}
|
||||
|
||||
|
||||
const tableStore: any = new TableStore({
|
||||
url: '/cs-harmonic-boot/event/pageEvent',
|
||||
method: 'POST',
|
||||
@@ -86,27 +85,27 @@ const tableStore: any = new TableStore({
|
||||
{
|
||||
title: '暂态时间',
|
||||
field: 'startTime',
|
||||
width: '180'
|
||||
minWidth: '180'
|
||||
},
|
||||
{
|
||||
title: '测点名称',
|
||||
field: 'lineName',
|
||||
width: '150'
|
||||
minWidth: '150'
|
||||
},
|
||||
{
|
||||
title: '暂态类型',
|
||||
field: 'tag',
|
||||
width: '100'
|
||||
minWidth: '100'
|
||||
},
|
||||
{
|
||||
title: '特征幅值(%)',
|
||||
field: 'amplitude',
|
||||
width: '100'
|
||||
minWidth: '100'
|
||||
},
|
||||
{
|
||||
title: '暂降深度(%)',
|
||||
field: 'depth',
|
||||
width: '100',
|
||||
minWidth: '100',
|
||||
formatter: (row: any) => {
|
||||
// 当暂态类型不是电压暂升时,计算暂降深度 = 100 - 特征幅值
|
||||
if (row.row.tag !== '电压暂升') {
|
||||
@@ -124,12 +123,12 @@ const tableStore: any = new TableStore({
|
||||
{
|
||||
title: '持续时间(S)',
|
||||
field: 'persistTime',
|
||||
width: '100'
|
||||
minWidth: '100'
|
||||
},
|
||||
{
|
||||
title: '严重度',
|
||||
field: 'severity',
|
||||
width: '80'
|
||||
minWidth: '80'
|
||||
},
|
||||
{
|
||||
title: '波形',
|
||||
@@ -167,8 +166,9 @@ const tableStore: any = new TableStore({
|
||||
// ...row,
|
||||
// duration: row.persistTime // 将 persistTime 值赋给 duration
|
||||
// }
|
||||
boxoList.value.featureAmplitude =
|
||||
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
|
||||
// boxoList.value.featureAmplitude =
|
||||
// row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
|
||||
boxoList.value.featureAmplitude = (row.amplitude - 0) / 100
|
||||
boxoList.value.systemType = 'YPT'
|
||||
wp.value = res.data
|
||||
}
|
||||
@@ -192,7 +192,7 @@ const tableStore: any = new TableStore({
|
||||
icon: 'el-icon-DataLine',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return !(!row.wavePath && row.evtParamTm < 20)
|
||||
return !!row.wavePath
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
datePicker
|
||||
v-if="fullscreen"
|
||||
v-if="fullscreen" :timeKeyList="prop.timeKey"
|
||||
></TableHeader>
|
||||
<my-echart
|
||||
class="tall"
|
||||
@@ -41,7 +41,7 @@ const prop = defineProps({
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -50,6 +50,7 @@ const TableHeaderRef = ref()
|
||||
|
||||
const headerHeight = ref(57)
|
||||
|
||||
|
||||
const selectChange = (showSelect: any, height: any, datePickerValue?: any) => {
|
||||
headerHeight.value = height
|
||||
|
||||
|
||||
@@ -4,14 +4,19 @@
|
||||
<TableHeader
|
||||
datePicker
|
||||
ref="TableHeaderRef"
|
||||
:timeKeyList="['4', '5']"
|
||||
:timeKeyList="prop.timeKey"
|
||||
:showReset="false"
|
||||
@selectChange="selectChange"
|
||||
v-if="fullscreen"
|
||||
>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="监测对象">
|
||||
<el-select v-model="tableStore.table.params.sensitiveUserId" placeholder="请选择监测对象" clearable>
|
||||
<el-select
|
||||
filterable
|
||||
v-model="tableStore.table.params.sensitiveUserId"
|
||||
placeholder="请选择监测对象"
|
||||
clearable
|
||||
>
|
||||
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -76,6 +81,7 @@
|
||||
height: `calc(${prop.height} - ${headerHeight}px + ${fullscreen ? 0 : 56}px)`
|
||||
}"
|
||||
/>
|
||||
<!-- <el-empty description="暂无数据" /> -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@@ -87,13 +93,14 @@ import { useConfig } from '@/stores/config'
|
||||
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
|
||||
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
|
||||
import { getTime } from '@/utils/formatTime'
|
||||
|
||||
import { yMethod, exportCSV } from '@/utils/echartMethod'
|
||||
import { max } from 'lodash'
|
||||
const prop = defineProps({
|
||||
w: { type: [String, Number] },
|
||||
h: { type: [String, Number] },
|
||||
width: { type: [String, Number] },
|
||||
height: { type: [String, Number] },
|
||||
timeKey: { type: [String, Number] },
|
||||
timeKey: { type: Array as () => string[] },
|
||||
timeValue: { type: Object },
|
||||
interval: { type: Number }
|
||||
})
|
||||
@@ -122,14 +129,16 @@ const echartList = ref()
|
||||
const headerHeight = ref(57)
|
||||
|
||||
// 监测对象
|
||||
const idList = ref()
|
||||
const idList = ref([])
|
||||
|
||||
// 监测对象
|
||||
const initListByIds = () => {
|
||||
getListByIds({}).then((res: any) => {
|
||||
if (res.data.length > 0) {
|
||||
if (res.data?.length > 0) {
|
||||
idList.value = res.data
|
||||
initCode()
|
||||
} else {
|
||||
tableStore.index()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -174,7 +183,7 @@ const setEchart = () => {
|
||||
if (!beforeGroupedByPhase[phase]) {
|
||||
beforeGroupedByPhase[phase] = []
|
||||
}
|
||||
beforeGroupedByPhase[phase].push([item.time, item.statisticalData])
|
||||
beforeGroupedByPhase[phase].push([item.time, item.statisticalData, item.unit, 'solid'])
|
||||
})
|
||||
|
||||
// 处理治理后数据
|
||||
@@ -183,7 +192,7 @@ const setEchart = () => {
|
||||
if (!afterGroupedByPhase[phase]) {
|
||||
afterGroupedByPhase[phase] = []
|
||||
}
|
||||
afterGroupedByPhase[phase].push([item.time, item.statisticalData])
|
||||
afterGroupedByPhase[phase].push([item.time, item.statisticalData, item.unit, 'dotted'])
|
||||
})
|
||||
|
||||
// 构建系列数据
|
||||
@@ -254,6 +263,11 @@ const setEchart = () => {
|
||||
titleText = afterData[0].anotherName
|
||||
}
|
||||
|
||||
// statisticalData
|
||||
// chartsListBefore.value.map((item: any) => item.statisticalData)
|
||||
// chartsListAfter.value = tableStore.table.data.after
|
||||
|
||||
|
||||
// 构建图例数据
|
||||
const legendData = series.map((item: any, index: number) => {
|
||||
let color = config.layout.elementUiPrimary[0]
|
||||
@@ -283,11 +297,45 @@ const setEchart = () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let [min, max] = yMethod(
|
||||
[...chartsListBefore.value.map((item: any) => item.statisticalData),
|
||||
...chartsListAfter.value.map((item: any) => item.statisticalData)]
|
||||
)
|
||||
echartList.value = {
|
||||
title: {
|
||||
text: titleText
|
||||
},
|
||||
tooltip: {
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
color: '#fff',
|
||||
fontSize: 16
|
||||
}
|
||||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontStyle: 'normal',
|
||||
opacity: 0.35,
|
||||
fontSize: 14
|
||||
},
|
||||
backgroundColor: 'rgba(0,0,0,0.55)',
|
||||
borderWidth: 0,
|
||||
formatter(params: any) {
|
||||
const xname = params[0].value[0]
|
||||
let str = `${xname}<br>`
|
||||
params.forEach((el: any, index: any) => {
|
||||
let marker = ''
|
||||
|
||||
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
|
||||
|
||||
str += `${marker}${el.seriesName.split('(')[0]}:${
|
||||
el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == null ? '' : el.value[2]) : '-'
|
||||
}<br>`
|
||||
})
|
||||
return str
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: legendData,
|
||||
icon: 'rect',
|
||||
@@ -311,7 +359,9 @@ const setEchart = () => {
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
name: beforeData.length > 0 ? beforeData[0].unit : afterData.length > 0 ? afterData[0].unit : ''
|
||||
name: beforeData.length > 0 ? beforeData[0].unit : afterData.length > 0 ? afterData[0].unit : '',
|
||||
max: max,
|
||||
min: min,
|
||||
},
|
||||
grid: {
|
||||
left: '10px',
|
||||
@@ -363,8 +413,8 @@ const tableStore: any = new TableStore({
|
||||
}
|
||||
})
|
||||
|
||||
tableStore.table.params.indicator = '1'
|
||||
tableStore.table.params.exceedingTheLimit = '1'
|
||||
tableStore.table.params.indicator = ''
|
||||
tableStore.table.params.exceedingTheLimit = ''
|
||||
tableStore.table.params.dataLevel = 'Primary'
|
||||
tableStore.table.params.valueType = 'avg'
|
||||
provide('tableStore', tableStore)
|
||||
@@ -400,7 +450,7 @@ const shouldShowHarmonicCount = () => {
|
||||
|
||||
return (
|
||||
currentIndicator &&
|
||||
(currentIndicator.name.includes('电压谐波含有率') || currentIndicator.name.includes('电流谐波含有率'))
|
||||
(currentIndicator.name.includes('幅值') || currentIndicator.name.includes('含有率'))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -409,9 +459,9 @@ const getHarmonicTypeName = () => {
|
||||
const currentIndicator = indicatorList.value.find((item: any) => item.id === tableStore.table.params.indicator)
|
||||
|
||||
if (currentIndicator) {
|
||||
if (currentIndicator.name.includes('电压谐波含有率')) {
|
||||
if (currentIndicator.name.includes('电压')) {
|
||||
return '电压'
|
||||
} else if (currentIndicator.name.includes('电流谐波含有率')) {
|
||||
} else if (currentIndicator.name.includes('电流')) {
|
||||
return '电流'
|
||||
}
|
||||
}
|
||||
@@ -449,7 +499,6 @@ watch(
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
// :deep(.el-select) {
|
||||
|
||||
@@ -252,50 +252,52 @@ self.onmessage = function (e) {
|
||||
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'
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div v-loading="loading" style="position: relative; height: 100%">
|
||||
<div id="boxr">
|
||||
<div id="rmsp" :style="`height:${vh};overflow: hidden;`">
|
||||
<div class="bx" id="rms"></div>
|
||||
<div id="rmsp" :style="`height:${vh};overflow: hidden;min-height: 200px;`">
|
||||
<div class="bx" id="rms" style="min-height: 200px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,7 +109,7 @@ const myChartess5 = ref<echarts.ECharts | null>(null)
|
||||
|
||||
const vh = computed(() => {
|
||||
if (props.parentHeight == 999) {
|
||||
return '310px'
|
||||
return `calc((60vh - 150px) / 2 )`
|
||||
} else if (props.parentHeight != 0) {
|
||||
return mainHeight(props.parentHeight, 2).height
|
||||
}
|
||||
@@ -594,13 +594,13 @@ const initWave = (
|
||||
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
|
||||
const rmsId = 'rms' + step
|
||||
const newDivRms = $(
|
||||
`<div style="height:${vh.value};overflow: hidden;"><div class='bx' id='${rmsId}'></div></div>`
|
||||
`<div style="height:${vh.value};overflow: hidden;min-height: 200px;"><div class='bx' id='${rmsId}'></div></div>`
|
||||
)
|
||||
newDivRms.insertAfter($('#rmsp'))
|
||||
$(`#${rmsId}`).css('height', picHeight).css('width', vw.value)
|
||||
$(`#${rmsId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px')
|
||||
}
|
||||
} else {
|
||||
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 残余电压:${(
|
||||
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降(骤升)幅值:${(
|
||||
Number(eventValue.value) * 1
|
||||
).toFixed(0)}% 持续时间:${persistTime.value}s`
|
||||
}
|
||||
@@ -749,7 +749,7 @@ const initWave = (
|
||||
y: -10
|
||||
},
|
||||
max: rmscm[0]?.[1] * 1.06 || 0,
|
||||
min: rmscu[0]?.[1] - rmscu[0]?.[1] * 0.04 || 0,
|
||||
min: rmscu[0]?.[1] - rmscu[0]?.[1] * 0.2 || 0,
|
||||
boundaryGap: [0, '100%'],
|
||||
showLastLabel: true,
|
||||
opposite: false,
|
||||
@@ -768,7 +768,7 @@ const initWave = (
|
||||
fontSize: '12px',
|
||||
color: props.DColor ? '#000' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
return Math.floor(value * 1000) / 1000
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
@@ -837,7 +837,7 @@ const initWave = (
|
||||
data: rmscu
|
||||
},
|
||||
{
|
||||
name: '最小残余电压',
|
||||
name: '最小暂降(骤升)幅值',
|
||||
type: 'scatter',
|
||||
symbol: 'image://' + url2,
|
||||
itemStyle: { width: 45, height: 45 },
|
||||
@@ -1092,7 +1092,8 @@ const drawPics = (
|
||||
fontSize: '12px',
|
||||
color: props.DColor ? '#000' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
// return (value - 0).toFixed(2)
|
||||
return Math.floor(value * 1000) / 1000
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
|
||||
@@ -125,50 +125,52 @@ self.addEventListener('message', function (e) {
|
||||
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'
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div v-loading="loading" class="boxbx" style="position: relative; height: 100%">
|
||||
<div id="boxsj">
|
||||
<div id="shushi" :style="`height:${vh};overflow: hidden;`">
|
||||
<div class="bx" id="wave"></div>
|
||||
<div id="shushi" :style="`height:${vh};overflow: hidden;min-height: 200px;`">
|
||||
<div class="bx" id="wave" style="min-height: 200px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,7 +88,7 @@ const myChartess5 = ref<echarts.ECharts | null>(null)
|
||||
|
||||
const vh = computed(() => {
|
||||
if (props.parentHeight == 999) {
|
||||
return '310px'
|
||||
return `calc((60vh - 150px) / 2 )`
|
||||
} else if (props.parentHeight != 0) {
|
||||
return mainHeight(props.parentHeight, 2).height
|
||||
}
|
||||
@@ -327,14 +327,14 @@ const initWave = (
|
||||
|
||||
for (let step = waveDatas.length - 1; step > 0 && step < waveDatas.length; step--) {
|
||||
const waveId = 'wave' + step
|
||||
const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;">
|
||||
const newDivShunshi = $(`<div style="height:${vh.value};overflow: hidden;min-height: 200px;">
|
||||
<div class='bx1' id='${waveId}'></div>
|
||||
</div>`)
|
||||
newDivShunshi.insertAfter($('#shushi'))
|
||||
$(`#${waveId}`).css('height', picHeight).css('width', vw.value)
|
||||
$(`#${waveId}`).css('height', picHeight).css('width', vw.value).css('min-height', '200px')
|
||||
}
|
||||
} else {
|
||||
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 残余电压:${(
|
||||
titleText = `变电站名称:${subName.value} 监测点名称:${lineName.value} 发生时刻:${time} 暂降(骤升)幅值:${(
|
||||
Number(eventValue.value) * 1
|
||||
).toFixed(0)}% 持续时间:${persistTime.value}s`
|
||||
}
|
||||
@@ -499,7 +499,8 @@ const initWave = (
|
||||
fontSize: '12px',
|
||||
color: props.DColor ? '#000' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
// return (value - 0).toFixed(2)
|
||||
return Math.floor(value * 1000) / 1000
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
@@ -591,7 +592,7 @@ const initWave = (
|
||||
|
||||
const drawPics = (
|
||||
waveDataTemp: WaveData,
|
||||
picHeight: string,
|
||||
picHeight: any,
|
||||
step: number,
|
||||
show: boolean,
|
||||
myChartes1: echarts.ECharts,
|
||||
@@ -806,7 +807,8 @@ const drawPics = (
|
||||
fontSize: '12px',
|
||||
color: props.DColor ? '#000' : echartsColor.WordColor,
|
||||
formatter: function (value: number) {
|
||||
return (value - 0).toFixed(2)
|
||||
// return (value - 0).toFixed(2)
|
||||
return Math.floor(value * 1000) / 1000
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
value-format="YYYY-MM-DD"
|
||||
:shortcuts="shortcuts"
|
||||
/>
|
||||
|
||||
<el-button :disabled="backDisabled" type="primary" :icon="DArrowLeft" @click="preClick"></el-button>
|
||||
<el-button type="primary" :icon="VideoPause" @click="nowTime">当前</el-button>
|
||||
<el-button :disabled="preDisabled" type="primary" :icon="DArrowRight" @click="next"></el-button>
|
||||
@@ -35,7 +36,7 @@ interface Props {
|
||||
theCurrentTime?: boolean
|
||||
initialInterval?: number
|
||||
initialTimeValue?: any
|
||||
timeKeyList?: string[] //日期下拉
|
||||
timeKeyList?: string[] //日期下拉
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -97,11 +98,8 @@ const filteredTimeOptions = computed(() => {
|
||||
if (!props.timeKeyList || props.timeKeyList.length === 0) {
|
||||
return timeOptions.value
|
||||
}
|
||||
|
||||
return timeOptions.value.filter((option: any) =>
|
||||
props.timeKeyList.includes(option.value.toString())
|
||||
)
|
||||
|
||||
return timeOptions.value.filter((option: any) => props.timeKeyList.includes(option.value.toString()))
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@@ -128,9 +126,13 @@ const checkInitialButtonStatus = () => {
|
||||
const endTime = timeValue.value[1]
|
||||
const currentDate = window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd')
|
||||
|
||||
// 如果结束时间小于当前日期,则不禁用"下一个"按钮
|
||||
if (new Date(endTime + ' 00:00:00').getTime() < new Date(currentDate + ' 00:00:00').getTime()) {
|
||||
preDisabled.value = false
|
||||
// 只有当 props.nextFlag 为 false 时才应用限制
|
||||
if (!props.nextFlag) {
|
||||
// 如果结束时间早于当前日期,则按钮可用(preDisabled = false)
|
||||
// 如果结束时间晚于或等于当前日期,则按钮禁用(preDisabled = true)
|
||||
const endDateTime = new Date(endTime).getTime()
|
||||
const currentDateTime = new Date(currentDate).getTime()
|
||||
preDisabled.value = endDateTime >= currentDateTime
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,30 +181,15 @@ const timeChange = (e: number) => {
|
||||
timeFlag.value = 1
|
||||
}
|
||||
|
||||
// 检查按钮状态
|
||||
checkButtonStatus()
|
||||
nextTick(() => {
|
||||
// 检查按钮状态
|
||||
checkInitialButtonStatus()
|
||||
})
|
||||
|
||||
// 触发 change 事件
|
||||
emitChange()
|
||||
}
|
||||
|
||||
// 添加按钮状态检查方法
|
||||
const checkButtonStatus = () => {
|
||||
if (timeValue.value && timeValue.value.length >= 2) {
|
||||
const endTime = timeValue.value[1]
|
||||
const currentDate = window.XEUtils.toDateString(new Date(), 'yyyy-MM-dd')
|
||||
|
||||
// 如果结束时间大于等于当前日期,且 nextFlag 为 false,则禁用"下一个"按钮
|
||||
if (!props.nextFlag) {
|
||||
if (new Date(endTime + ' 00:00:00').getTime() >= new Date(currentDate + ' 00:00:00').getTime()) {
|
||||
preDisabled.value = true
|
||||
} else {
|
||||
preDisabled.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 当前
|
||||
const nowTime = () => {
|
||||
// console.log(interval.value, '000000000')
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<el-image
|
||||
:hide-on-click-modal="true"
|
||||
:preview-teleported="true"
|
||||
:preview-src-list="[fieldValue]"
|
||||
:src="fieldValue.length > 100 ? fieldValue : getUrl(fieldValue)"
|
||||
:preview-src-list="[imgList[fieldValue]]"
|
||||
:src="fieldValue.length > 100 ? fieldValue : getUrl(fieldValue) ? imgList[fieldValue] : ''"
|
||||
></el-image>
|
||||
</div>
|
||||
|
||||
@@ -226,10 +226,12 @@ const handlerCommand = (item: OptButton) => {
|
||||
break
|
||||
}
|
||||
}
|
||||
const imgList: any = ref({})
|
||||
const getUrl = (url: string) => {
|
||||
getFileUrl({ filePath: url }).then(res => {
|
||||
return res.data
|
||||
imgList.value[url] = res.data
|
||||
})
|
||||
return true
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div ref="tableHeader" class="cn-table-header">
|
||||
<div class="table-header ba-scroll-style" :key="num">
|
||||
<el-form
|
||||
style="flex: 1; height: 34px; margin-right: 20px; display: flex; flex-wrap: wrap"
|
||||
style="flex: 1; height: 34px; margin-right: 0px; display: flex; flex-wrap: wrap"
|
||||
ref="headerForm"
|
||||
@submit.prevent=""
|
||||
@keyup.enter="onComSearch"
|
||||
@@ -29,12 +29,27 @@
|
||||
<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" type="primary" :icon="Search">查询</el-button>
|
||||
<el-button @click="onResetForm" v-if="showSearch && showReset" :icon="RefreshLeft">重置</el-button>
|
||||
<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>
|
||||
<el-button
|
||||
@click="onExport"
|
||||
v-if="showExport"
|
||||
:loading="tableStore.table.loading"
|
||||
:loading="tableStore.table.exportLoading"
|
||||
type="primary"
|
||||
icon="el-icon-Download"
|
||||
>
|
||||
@@ -90,7 +105,7 @@ interface Props {
|
||||
showReset?: boolean //是否显示重置
|
||||
showExport?: boolean //导出控制
|
||||
timeCacheFlag?: boolean //是否取缓存时间
|
||||
timeKeyList?: Array<string> //日期下拉列表
|
||||
timeKeyList?: string[] //日期下拉列表
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -102,7 +117,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
showReset: true,
|
||||
showExport: false,
|
||||
timeCacheFlag: true,
|
||||
timeKeyList: () => [] // 修改为箭头函数返回空数组
|
||||
timeKeyList: () => ['1', '2', '3', '4', '5'] // 修改为箭头函数返回空数组
|
||||
})
|
||||
|
||||
// 处理 DatePicker 值变化事件
|
||||
|
||||
189
src/components/tree/allocation.vue
Normal file
189
src/components/tree/allocation.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<div :style="{ width: menuCollapse ? '40px' : props.width }" style="transition: all 0.3s; overflow: hidden">
|
||||
<div class="mt10 mr10" style="display: flex; justify-content: end">
|
||||
<el-button type="primary" icon="el-icon-Select" @click="save" :loading="loading">保存</el-button>
|
||||
</div>
|
||||
<Icon
|
||||
v-show="menuCollapse"
|
||||
@click="onMenuCollapse"
|
||||
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''"
|
||||
size="18"
|
||||
class="fold ml10 mt20 menu-collapse"
|
||||
style="cursor: pointer"
|
||||
/>
|
||||
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }">
|
||||
<div style="display: flex; align-items: center" class="mb10">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable>
|
||||
<template #prefix>
|
||||
<Icon name="el-icon-Search" style="font-size: 16px" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tooltip placement="bottom" :hide-after="0" v-if="props.showPush">
|
||||
<template #content>
|
||||
<span>台账推送</span>
|
||||
</template>
|
||||
|
||||
<Icon
|
||||
name="el-icon-Promotion"
|
||||
size="20"
|
||||
class="fold ml10 menu-collapse"
|
||||
style="cursor: pointer"
|
||||
:style="{ color: config.getColorVal('elementUiPrimary') }"
|
||||
@click="onAdd"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<!-- <Icon @click='onMenuCollapse' :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" v-else
|
||||
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse'
|
||||
style='cursor: pointer' v-if='props.canExpand' /> -->
|
||||
</div>
|
||||
|
||||
<el-tree
|
||||
:style="{ height: 'calc(100vh - 267px)' }"
|
||||
style="overflow: auto"
|
||||
ref="treeRef"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
:default-expand-all="false"
|
||||
@check-change="checkTreeNodeChange"
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<Icon
|
||||
:name="data.icon"
|
||||
style="font-size: 16px"
|
||||
:style="{ color: data.color }"
|
||||
v-if="data.icon"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import useCurrentInstance from '@/utils/useCurrentInstance'
|
||||
import { ElTree } from 'element-plus'
|
||||
import { emit } from 'process'
|
||||
import { ref, watch } from 'vue'
|
||||
import { t } from 'vxe-table'
|
||||
import { useConfig } from '@/stores/config'
|
||||
|
||||
defineOptions({
|
||||
name: 'govern/tree'
|
||||
})
|
||||
|
||||
interface Props {
|
||||
width?: string
|
||||
canExpand?: boolean
|
||||
showPush?: boolean
|
||||
}
|
||||
const loading = ref(false)
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
width: '280px',
|
||||
canExpand: true,
|
||||
showPush: false
|
||||
})
|
||||
const config = useConfig()
|
||||
const { proxy } = useCurrentInstance()
|
||||
const menuCollapse = ref(false)
|
||||
const filterText = ref('')
|
||||
const defaultProps = {
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
}
|
||||
const emit = defineEmits(['checkTreeNodeChange', 'onAdd', 'checkChange'])
|
||||
watch(filterText, val => {
|
||||
treeRef.value!.filter(val)
|
||||
})
|
||||
const onMenuCollapse = () => {
|
||||
menuCollapse.value = !menuCollapse.value
|
||||
proxy.eventBus.emit('cnTreeCollapse', menuCollapse)
|
||||
}
|
||||
const save = () => {
|
||||
loading.value = true
|
||||
emit('checkChange')
|
||||
}
|
||||
const filterNode = (value: string, data: any, node: any) => {
|
||||
console.log(value, data, node, 'filterNode')
|
||||
if (!value) return true
|
||||
// return data.name.includes(value)
|
||||
if (data.name) {
|
||||
return chooseNode(value, data, node)
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤父节点 / 子节点 (如果输入的参数是父节点且能匹配,则返回该节点以及其下的所有子节点;如果参数是子节点,则返回该节点的父节点。name是中文字符,enName是英文字符.
|
||||
const chooseNode = (value: string, data: any, node: any) => {
|
||||
if (data.name.indexOf(value) !== -1) {
|
||||
return true
|
||||
}
|
||||
const level = node.level
|
||||
// 如果传入的节点本身就是一级节点就不用校验了
|
||||
if (level === 1) {
|
||||
return false
|
||||
}
|
||||
// 先取当前节点的父节点
|
||||
let parentData = node.parent
|
||||
// 遍历当前节点的父节点
|
||||
let index = 0
|
||||
while (index < level - 1) {
|
||||
// 如果匹配到直接返回,此处name值是中文字符,enName是英文字符。判断匹配中英文过滤
|
||||
if (parentData.data.name.indexOf(value) !== -1) {
|
||||
return true
|
||||
}
|
||||
// 否则的话再往上一层做匹配
|
||||
parentData = parentData.parent
|
||||
index++
|
||||
}
|
||||
// 没匹配到返回false
|
||||
return false
|
||||
}
|
||||
const checkTreeNodeChange = () => {
|
||||
// console.log(treeRef.value?.getCheckedNodes(), "ikkkkkiisiiisis");
|
||||
emit('checkTreeNodeChange', treeRef.value?.getCheckedNodes())
|
||||
}
|
||||
|
||||
const onAdd = () => {
|
||||
emit('onAdd')
|
||||
}
|
||||
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
defineExpose({ treeRef, loading })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cn-tree {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-tree) {
|
||||
border: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
|
||||
background-color: var(--el-color-primary-light-7);
|
||||
}
|
||||
|
||||
.menu-collapse {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -14,7 +14,7 @@
|
||||
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse'
|
||||
style='cursor: pointer' v-if='props.canExpand' /> -->
|
||||
</div>
|
||||
<el-tree :style="{ height: 'calc(100vh - 110px)' }"
|
||||
<el-tree :style="{ height: 'calc(100vh - 230px)' }"
|
||||
style=' overflow: auto;' ref='treeRef' :props='defaultProps' highlight-current :default-expand-all="false"
|
||||
@check-change="checkTreeNodeChange" :filter-node-method='filterNode' node-key='id' v-bind='$attrs'>
|
||||
<template #default='{ node, data }'>
|
||||
@@ -32,7 +32,6 @@
|
||||
<script lang='ts' setup>
|
||||
import useCurrentInstance from '@/utils/useCurrentInstance'
|
||||
import { ElTree } from 'element-plus'
|
||||
import { emit } from 'process';
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
|
||||
@@ -1,27 +1,48 @@
|
||||
<!-- 设备管理使用折叠面板渲染多个tree -->
|
||||
<template>
|
||||
<div :style="{ width: menuCollapse ? '40px' : props.width }" style="display: flex; overflow: hidden">
|
||||
<Icon v-show="menuCollapse" @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 mt20 menu-collapse"
|
||||
style="cursor: pointer" />
|
||||
<Icon
|
||||
v-show="menuCollapse"
|
||||
@click="onMenuCollapse"
|
||||
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''"
|
||||
size="18"
|
||||
class="fold ml10 mt20 menu-collapse"
|
||||
style="cursor: pointer"
|
||||
/>
|
||||
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1 }">
|
||||
<div style="display: flex; align-items: center" class="mb10">
|
||||
<!-- <el-form-item> -->
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="filterText" autocomplete="off"
|
||||
placeholder="请输入内容" clearable>
|
||||
<el-input
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
v-model.trim="filterText"
|
||||
autocomplete="off"
|
||||
placeholder="请输入内容"
|
||||
clearable
|
||||
>
|
||||
<template #prefix>
|
||||
<Icon name="el-icon-Search" style="font-size: 16px" />
|
||||
</template>
|
||||
</el-input>
|
||||
<!-- </el-form-item> -->
|
||||
<Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 menu-collapse"
|
||||
style="cursor: pointer" v-if="props.canExpand" />
|
||||
<Icon
|
||||
@click="onMenuCollapse"
|
||||
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''"
|
||||
size="18"
|
||||
class="fold ml10 menu-collapse"
|
||||
style="cursor: pointer"
|
||||
v-if="props.canExpand"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-collapse :accordion="true" v-model.trim="activeName" style="flex: 1; height: 100%"
|
||||
@change="changeDevice">
|
||||
|
||||
<el-collapse
|
||||
:accordion="true"
|
||||
v-model.trim="activeName"
|
||||
style="flex: 1; height: 100%"
|
||||
@change="changeDevice"
|
||||
>
|
||||
<el-collapse-item title="治理设备" name="0" v-if="zlDeviceData.length != 0">
|
||||
<el-select v-model.trim="process" clearable placeholder="请选择状态" class="mb10">
|
||||
<el-option label="功能调试" value="2"></el-option>
|
||||
@@ -29,13 +50,30 @@
|
||||
<el-option label="正式投运" value="4"></el-option>
|
||||
</el-select>
|
||||
<el-tree
|
||||
:style="{ height: bxsDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 278px)' }"
|
||||
ref="treeRef1" :props="defaultProps" highlight-current :filter-node-method="filterNode"
|
||||
node-key="id" :default-expand-all="false" v-bind="$attrs" :data="zlDevList" style="overflow: auto">
|
||||
:style="{
|
||||
height:
|
||||
bxsDeviceData.length != 0
|
||||
? `calc(100vh - 380px - ${props.height}px)`
|
||||
: 'calc(100vh - 278px)'
|
||||
}"
|
||||
ref="treeRef1"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
:default-expand-all="false"
|
||||
v-bind="$attrs"
|
||||
:data="zlDevList"
|
||||
style="overflow: auto"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
|
||||
v-if="data.icon" />
|
||||
<Icon
|
||||
:name="data.icon"
|
||||
style="font-size: 16px"
|
||||
:style="{ color: data.color }"
|
||||
v-if="data.icon"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -43,29 +81,61 @@
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length != 0">
|
||||
<el-tree
|
||||
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 280px)' : 'calc(100vh - 238px)' }"
|
||||
ref="treeRef2" :props="defaultProps" highlight-current :default-expand-all="false"
|
||||
:filter-node-method="filterNode" node-key="id" :data="bxsDeviceData" v-bind="$attrs"
|
||||
style="overflow: auto">
|
||||
:style="{
|
||||
height:
|
||||
zlDeviceData.length != 0
|
||||
? `calc(100vh - 340px - ${props.height}px)`
|
||||
: 'calc(100vh - 238px)'
|
||||
}"
|
||||
ref="treeRef2"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
:default-expand-all="false"
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
:data="bxsDeviceData"
|
||||
v-bind="$attrs"
|
||||
style="overflow: auto"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
|
||||
v-if="data.icon" />
|
||||
<Icon
|
||||
:name="data.icon"
|
||||
style="font-size: 16px"
|
||||
:style="{ color: data.color }"
|
||||
v-if="data.icon"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="在线设备" name="2" v-if="frontDeviceData.length != 0">
|
||||
<el-collapse-item title="监测设备" name="2" v-if="frontDeviceData.length != 0">
|
||||
<el-tree
|
||||
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 280px)' : 'calc(100vh - 238px)' }"
|
||||
ref="treeRef3" :props="defaultProps" highlight-current :default-expand-all="false"
|
||||
:filter-node-method="filterNode" node-key="id" :data="frontDeviceData" v-bind="$attrs"
|
||||
style="overflow: auto">
|
||||
:style="{
|
||||
height:
|
||||
zlDeviceData.length != 0
|
||||
? `calc(100vh - 340px - ${props.height}px)`
|
||||
: 'calc(100vh - 238px)'
|
||||
}"
|
||||
ref="treeRef3"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
:default-expand-all="false"
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
:data="frontDeviceData"
|
||||
v-bind="$attrs"
|
||||
style="overflow: auto"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
|
||||
v-if="data.icon" />
|
||||
<Icon
|
||||
:name="data.icon"
|
||||
style="font-size: 16px"
|
||||
:style="{ color: data.color }"
|
||||
v-if="data.icon"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -90,13 +160,15 @@ interface Props {
|
||||
canExpand?: boolean
|
||||
type?: string
|
||||
data?: any
|
||||
height?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
width: '280px',
|
||||
canExpand: true,
|
||||
type: '',
|
||||
data: []
|
||||
data: [],
|
||||
height: 0
|
||||
})
|
||||
const { proxy } = useCurrentInstance()
|
||||
const menuCollapse = ref(false)
|
||||
@@ -124,19 +196,18 @@ watch(
|
||||
item.children.map((vv: any) => {
|
||||
zlDeviceData.value.push(vv)
|
||||
})
|
||||
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
|
||||
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
|
||||
} else if (item.name == '便携式设备') {
|
||||
bxsDeviceData.value = []
|
||||
item.children.map((vv: any) => {
|
||||
bxsDeviceData.value.push(vv)
|
||||
})
|
||||
}else if (item.name == '在线设备') {
|
||||
} else if (item.name == '监测设备') {
|
||||
frontDeviceData.value = []
|
||||
|
||||
item.children.map((vv: any) => {
|
||||
frontDeviceData.value.push(vv)
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -157,8 +228,9 @@ watch(filterText, val => {
|
||||
}
|
||||
})
|
||||
watch(process, val => {
|
||||
if (val == '') {
|
||||
if (val == '' || val == undefined) {
|
||||
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
|
||||
console.log('🚀 ~ zlDevList.value:', zlDeviceData.value)
|
||||
} else {
|
||||
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
|
||||
}
|
||||
@@ -176,7 +248,7 @@ function filterProcess(nodes: any) {
|
||||
const children = node.children ? filterProcess(node.children) : []
|
||||
|
||||
// 如果当前节点的process=4,或者有子节点满足条件,则保留当前节点
|
||||
if ( node.process == process.value || children.length > 0) {
|
||||
if (node.process == process.value || children.length > 0) {
|
||||
return {
|
||||
...node,
|
||||
children: children
|
||||
@@ -228,7 +300,6 @@ const chooseNode = (value: string, data: any, node: any) => {
|
||||
}
|
||||
|
||||
const changeDevice = (val: any) => {
|
||||
console.log('changeDevice', val)
|
||||
let arr1: any = []
|
||||
|
||||
//zlDeviceData
|
||||
@@ -259,22 +330,30 @@ const changeDevice = (val: any) => {
|
||||
arr2.map((item: any) => {
|
||||
item.checked = false
|
||||
})
|
||||
treeRef1.value && treeRef1.value.setCurrentKey(arr1[0]?.id)
|
||||
|
||||
emit('changeDeviceType', activeName.value, arr1[0])
|
||||
setTimeout(() => {
|
||||
treeRef1.value?.setCurrentKey(arr1[0]?.id)
|
||||
}, 100)
|
||||
}
|
||||
if (val == '1') {
|
||||
arr1.map((item: any) => {
|
||||
item.checked = false
|
||||
})
|
||||
treeRef2.value && treeRef2.value.setCurrentKey(arr2[0]?.id)
|
||||
emit('changeDeviceType', activeName.value, arr2[0])
|
||||
setTimeout(() => {
|
||||
treeRef2.value?.setCurrentKey(arr2[0]?.id)
|
||||
}, 100)
|
||||
}
|
||||
if (val == '2') {
|
||||
arr3.map((item: any) => {
|
||||
item.checked = false
|
||||
})
|
||||
treeRef3.value && treeRef3.value.setCurrentKey(arr3[0]?.id)
|
||||
|
||||
emit('changeDeviceType', activeName.value, arr3[0])
|
||||
setTimeout(() => {
|
||||
treeRef3.value?.setCurrentKey(arr3[0]?.id)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
//治理
|
||||
@@ -283,7 +362,7 @@ const treeRef1 = ref<InstanceType<typeof ElTree>>()
|
||||
const treeRef2 = ref<InstanceType<typeof ElTree>>()
|
||||
//前置
|
||||
const treeRef3 = ref<InstanceType<typeof ElTree>>()
|
||||
defineExpose({ treeRef1, treeRef2 })
|
||||
defineExpose({ treeRef1, treeRef2, treeRef3 })
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
if (zlDeviceData.value.length != 0) {
|
||||
@@ -293,6 +372,9 @@ onMounted(() => {
|
||||
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length != 0) {
|
||||
activeName.value = '1'
|
||||
}
|
||||
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length === 0) {
|
||||
activeName.value = '2'
|
||||
}
|
||||
if (!zlDeviceData.value && !bxsDeviceData.value) {
|
||||
activeName.value = ''
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Tree ref="treRef" :width="width" :data="tree" default-expand-all @changePointType="changePointType" @onAdd="onAdd"/>
|
||||
<Tree ref="treRef" :width="width" :showPush="props.showPush" :data="tree" default-expand-all @changePointType="changePointType" @onAdd="onAdd"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -7,14 +7,16 @@ import { ref, nextTick, onMounted, defineProps } from 'vue'
|
||||
import Tree from '../index.vue'
|
||||
import { getLineTree,getCldTree } from '@/api/cs-device-boot/csLedger'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { getTemplateByDept } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { useDictData } from '@/stores/dictData'
|
||||
|
||||
interface Props {
|
||||
template?: boolean
|
||||
showPush?: boolean
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
template: false
|
||||
template: false,
|
||||
showPush: false
|
||||
})
|
||||
defineOptions({
|
||||
name: 'govern/deviceTree'
|
||||
@@ -37,8 +39,8 @@ const info = (selectedNodeId?: string) => {
|
||||
let rootData = null;
|
||||
if (Array.isArray(res.data)) {
|
||||
// 旧的数据结构 - 数组
|
||||
rootData = res.data.find((item: any) => item.name == '在线设备');
|
||||
} else if (res.data && res.data.name == '在线设备') {
|
||||
rootData = res.data.find((item: any) => item.name == '监测设备');
|
||||
} else if (res.data && res.data.name == '监测设备') {
|
||||
// 新的数据结构 - 单个对象
|
||||
rootData = res.data;
|
||||
}
|
||||
@@ -95,13 +97,16 @@ const info = (selectedNodeId?: string) => {
|
||||
tree.value = []
|
||||
}
|
||||
nextTick(() => {
|
||||
|
||||
if (arr1.length) {
|
||||
// 安全检查 treRef 和 treeRef1 是否存在
|
||||
if (treRef.value && treRef.value.treeRef1 && treRef.value.treeRef1.setCurrentKey) {
|
||||
// 安全检查 treRef 和 treeRef 是否存在
|
||||
console.log("🚀 ~ info ~ treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey:", treRef.value && treRef.value.treeRef1 && treRef.value.treeRef1.setCurrentKey)
|
||||
|
||||
if (treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey) {
|
||||
// 如果传入了要选中的节点ID,则选中该节点,否则选中第一个节点
|
||||
console.log('selectedNodeId:', selectedNodeId);
|
||||
if (selectedNodeId) {
|
||||
treRef.value.treeRef1.setCurrentKey(selectedNodeId);
|
||||
treRef.value.treeRef.setCurrentKey(selectedNodeId);
|
||||
// 查找对应的节点数据并触发事件
|
||||
let selectedNode = null;
|
||||
const findNode = (nodes: any[]) => {
|
||||
@@ -127,7 +132,7 @@ const info = (selectedNodeId?: string) => {
|
||||
}
|
||||
} else {
|
||||
// 初始化选中第一个节点
|
||||
treRef.value.treeRef1.setCurrentKey(arr1[0].id);
|
||||
treRef.value.treeRef.setCurrentKey(arr1[0].id);
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr1[0]
|
||||
@@ -153,7 +158,7 @@ const onAdd = () => {
|
||||
emit('onAdd')
|
||||
}
|
||||
if (props.template) {
|
||||
getTemplateByDept({ id: dictData.state.area[0].id })
|
||||
querySysExcel({ id: dictData.state.area[0]?.id })
|
||||
.then((res: any) => {
|
||||
emit('Policy', res.data)
|
||||
info()
|
||||
|
||||
106
src/components/tree/govern/cloudDeviceEntryTreeZL.vue
Normal file
106
src/components/tree/govern/cloudDeviceEntryTreeZL.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<Tree
|
||||
ref="treRef"
|
||||
:width="width"
|
||||
:showPush="props.showPush"
|
||||
:data="tree"
|
||||
default-expand-all
|
||||
@changePointType="changePointType"
|
||||
@onAdd="onAdd"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, onMounted, defineProps } from 'vue'
|
||||
import Tree from '../index.vue'
|
||||
import { getLineTree, objTree } from '@/api/cs-device-boot/csLedger'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { useDictData } from '@/stores/dictData'
|
||||
|
||||
interface Props {
|
||||
template?: boolean
|
||||
showPush?: boolean
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
template: false,
|
||||
showPush: false
|
||||
})
|
||||
defineOptions({
|
||||
name: 'govern/deviceTree'
|
||||
})
|
||||
|
||||
const emit = defineEmits(['init', 'checkChange', 'pointTypeChange', 'Policy', 'onAdd'])
|
||||
const config = useConfig()
|
||||
const tree = ref()
|
||||
const dictData = useDictData()
|
||||
const treRef = ref()
|
||||
const width = ref('')
|
||||
|
||||
const info = (selectedNodeId?: string) => {
|
||||
tree.value = []
|
||||
let arr1: any[] = []
|
||||
objTree().then(res => {
|
||||
try {
|
||||
res.data.map((item: any) => {
|
||||
item.icon = 'el-icon-HomeFilled'
|
||||
item.level = 1
|
||||
item.color = config.getColorVal('elementUiPrimary')
|
||||
item.children.forEach((item: any) => {
|
||||
item.icon = 'el-icon-List'
|
||||
item.level = 2
|
||||
item.color = config.getColorVal('elementUiPrimary')
|
||||
item.children.forEach((item2: any) => {
|
||||
arr1.push(item2)
|
||||
item2.icon = 'el-icon-Platform'
|
||||
item2.level = 3
|
||||
item2.color = config.getColorVal('elementUiPrimary')
|
||||
})
|
||||
})
|
||||
})
|
||||
tree.value = res.data
|
||||
nextTick(() => {
|
||||
if (arr1.length) {
|
||||
//初始化选中
|
||||
treRef.value.treeRef.setCurrentKey(arr1[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', arr1[0])
|
||||
return
|
||||
} else {
|
||||
emit('init')
|
||||
return
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error in processing getCldTree response:', error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const changePointType = (val: any, obj: any) => {
|
||||
emit('pointTypeChange', val, obj)
|
||||
}
|
||||
|
||||
const onAdd = () => {
|
||||
emit('onAdd')
|
||||
}
|
||||
if (props.template) {
|
||||
querySysExcel({ id: dictData.state.area[0]?.id })
|
||||
.then((res: any) => {
|
||||
emit('Policy', res.data)
|
||||
info()
|
||||
})
|
||||
.catch(err => {
|
||||
info()
|
||||
})
|
||||
} else {
|
||||
info()
|
||||
}
|
||||
|
||||
// 暴露 info 方法给父组件调用
|
||||
defineExpose({
|
||||
info
|
||||
})
|
||||
|
||||
onMounted(() => {})
|
||||
</script>
|
||||
179
src/components/tree/govern/csLedgerLineTree.vue
Normal file
179
src/components/tree/govern/csLedgerLineTree.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<Tree ref="treRef" :width="width" :showPush="props.showPush" :data="tree" default-expand-all @changePointType="changePointType" @onAdd="onAdd"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, onMounted, defineProps } from 'vue'
|
||||
import Tree from '../index.vue'
|
||||
import { getLineTree,lineTree } from '@/api/cs-device-boot/csLedger'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { useDictData } from '@/stores/dictData'
|
||||
|
||||
interface Props {
|
||||
template?: boolean
|
||||
showPush?: boolean
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
template: false,
|
||||
showPush: false
|
||||
})
|
||||
defineOptions({
|
||||
name: 'govern/deviceTree'
|
||||
})
|
||||
|
||||
const emit = defineEmits(['init', 'checkChange', 'pointTypeChange', 'Policy','onAdd'])
|
||||
const config = useConfig()
|
||||
const tree = ref()
|
||||
const dictData = useDictData()
|
||||
const treRef = ref()
|
||||
const width = ref('')
|
||||
|
||||
|
||||
const info = (selectedNodeId?: string) => {
|
||||
tree.value = []
|
||||
let arr1: any[] = []
|
||||
lineTree().then(res => {
|
||||
try {
|
||||
// 检查响应数据结构
|
||||
let rootData = null;
|
||||
if (Array.isArray(res.data)) {
|
||||
// 旧的数据结构 - 数组
|
||||
rootData = res.data.find((item: any) => item.name == '监测设备');
|
||||
} else if (res.data && res.data.name == '监测设备') {
|
||||
// 新的数据结构 - 单个对象
|
||||
rootData = res.data;
|
||||
}
|
||||
|
||||
// 治理设备
|
||||
if (rootData) {
|
||||
rootData.icon = 'el-icon-Menu'
|
||||
rootData.level = 0
|
||||
rootData.color = config.getColorVal('elementUiPrimary')
|
||||
// 确保根节点的 children 是数组
|
||||
if (!Array.isArray(rootData.children)) {
|
||||
rootData.children = []
|
||||
}
|
||||
rootData.children.forEach((item: any) => {
|
||||
item.icon = 'el-icon-HomeFilled'
|
||||
item.level = 1
|
||||
item.color = config.getColorVal('elementUiPrimary')
|
||||
// 确保 children 是数组
|
||||
if (!Array.isArray(item.children)) {
|
||||
item.children = []
|
||||
}
|
||||
item.children.forEach((item2: any) => {
|
||||
item2.icon = 'el-icon-List'
|
||||
item2.level = 2
|
||||
item2.color = config.getColorVal('elementUiPrimary')
|
||||
|
||||
// 确保 children 是数组
|
||||
if (!Array.isArray(item2.children)) {
|
||||
item2.children = []
|
||||
}
|
||||
item2.children.forEach((item3: any) => {
|
||||
item3.icon = 'el-icon-Platform'
|
||||
item3.level = 3
|
||||
item3.color =
|
||||
item3.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
|
||||
|
||||
// 确保 children 是数组
|
||||
if (!Array.isArray(item3.children)) {
|
||||
item3.children = []
|
||||
}
|
||||
|
||||
item3.children.forEach((item4: any) => {
|
||||
item4.icon = 'el-icon-Platform'
|
||||
item4.level = 4
|
||||
item4.color =
|
||||
item4.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
|
||||
arr1.push(item4)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
tree.value = [rootData] // 确保 tree.value 是数组
|
||||
} else {
|
||||
tree.value = []
|
||||
}
|
||||
nextTick(() => {
|
||||
|
||||
if (arr1.length) {
|
||||
// 安全检查 treRef 和 treeRef 是否存在
|
||||
console.log("🚀 ~ info ~ treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey:", treRef.value && treRef.value.treeRef1 && treRef.value.treeRef1.setCurrentKey)
|
||||
|
||||
if (treRef.value && treRef.value.treeRef && treRef.value.treeRef.setCurrentKey) {
|
||||
// 如果传入了要选中的节点ID,则选中该节点,否则选中第一个节点
|
||||
console.log('selectedNodeId:', selectedNodeId);
|
||||
if (selectedNodeId) {
|
||||
treRef.value.treeRef.setCurrentKey(selectedNodeId);
|
||||
// 查找对应的节点数据并触发事件
|
||||
let selectedNode = null;
|
||||
const findNode = (nodes: any[]) => {
|
||||
for (const node of nodes) {
|
||||
if (node.id === selectedNodeId) {
|
||||
selectedNode = node;
|
||||
return true;
|
||||
}
|
||||
if (node.children && findNode(node.children)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
findNode(tree.value);
|
||||
|
||||
if (selectedNode) {
|
||||
emit('init', {
|
||||
level: selectedNode.level,
|
||||
...selectedNode
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 初始化选中第一个节点
|
||||
treRef.value.treeRef.setCurrentKey(arr1[0].id);
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr1[0]
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error in processing getCldTree response:', error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const changePointType = (val: any, obj: any) => {
|
||||
emit('pointTypeChange', val, obj)
|
||||
}
|
||||
|
||||
const onAdd = () => {
|
||||
emit('onAdd')
|
||||
}
|
||||
if (props.template) {
|
||||
querySysExcel({ id: dictData.state.area[0]?.id })
|
||||
.then((res: any) => {
|
||||
emit('Policy', res.data)
|
||||
info()
|
||||
})
|
||||
.catch(err => {
|
||||
info()
|
||||
})
|
||||
} else {
|
||||
info()
|
||||
}
|
||||
|
||||
// 暴露 info 方法给父组件调用
|
||||
defineExpose({
|
||||
info
|
||||
})
|
||||
|
||||
onMounted(() => {})
|
||||
</script>
|
||||
@@ -5,15 +5,17 @@
|
||||
:default-checked-keys="defaultCheckedKeys"
|
||||
:show-checkbox="props.showCheckbox"
|
||||
:data="tree"
|
||||
:height="props.height"
|
||||
@changeDeviceType="changeDeviceType"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, defineEmits } from 'vue'
|
||||
import { ref, nextTick } from 'vue'
|
||||
import Tree from '../device.vue'
|
||||
import { getDeviceTree } from '@/api/cs-device-boot/csLedger'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { throttle } from 'lodash'
|
||||
defineOptions({
|
||||
name: 'govern/deviceTree'
|
||||
})
|
||||
@@ -21,10 +23,12 @@ const props = withDefaults(
|
||||
defineProps<{
|
||||
showCheckbox?: boolean
|
||||
defaultCheckedKeys?: any
|
||||
height?: number
|
||||
}>(),
|
||||
{
|
||||
showCheckbox: false,
|
||||
defaultCheckedKeys: []
|
||||
defaultCheckedKeys: [],
|
||||
height: 0
|
||||
}
|
||||
)
|
||||
const emit = defineEmits(['init', 'checkChange', 'deviceTypeChange'])
|
||||
@@ -32,7 +36,6 @@ const config = useConfig()
|
||||
const tree = ref()
|
||||
const treRef = ref()
|
||||
const changeDeviceType = (val: any, obj: any) => {
|
||||
console.log("🚀 ~ changeDeviceType ~ val:", val,obj)
|
||||
emit('deviceTypeChange', val, obj)
|
||||
}
|
||||
getDeviceTree().then(res => {
|
||||
@@ -49,8 +52,9 @@ getDeviceTree().then(res => {
|
||||
item2.icon = 'el-icon-List'
|
||||
item2.color = config.getColorVal('elementUiPrimary')
|
||||
item2.children.forEach((item3: any) => {
|
||||
item3.pName = '治理设备'
|
||||
item3.icon = 'el-icon-Platform'
|
||||
item3.level = 2
|
||||
item3.level = 2
|
||||
item3.color = config.getColorVal('elementUiPrimary')
|
||||
if (item3.comFlag === 1) {
|
||||
item3.color = '#e26257 !important'
|
||||
@@ -65,14 +69,15 @@ getDeviceTree().then(res => {
|
||||
item.color = config.getColorVal('elementUiPrimary')
|
||||
item.color = '#e26257 !important'
|
||||
item.color = item.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
|
||||
|
||||
// item.disabled =true
|
||||
item.pName = '便携式设备'
|
||||
if (item.type == 'device') {
|
||||
arr2.push(item)
|
||||
}
|
||||
item.children.forEach((item2: any) => {
|
||||
item2.icon = 'el-icon-Platform'
|
||||
item2.color = item2.comFlag === 2 ? config.getColorVal('elementUiPrimary') : '#e26257 !important'
|
||||
|
||||
item2.pName = '便携式设备'
|
||||
// item2.children.forEach((item3: any) => {
|
||||
// item3.icon = 'el-icon-Platform'
|
||||
// item3.color = config.getColorVal('elementUiPrimary')
|
||||
@@ -83,14 +88,15 @@ getDeviceTree().then(res => {
|
||||
// })
|
||||
})
|
||||
})
|
||||
}else if (item.name == '在线设备') {
|
||||
item.children.forEach((item: any) => {
|
||||
} else if (item.name == '监测设备') {
|
||||
item.children.forEach((item: any) => {
|
||||
item.icon = 'el-icon-HomeFilled'
|
||||
item.color = config.getColorVal('elementUiPrimary')
|
||||
item.children.forEach((item2: any) => {
|
||||
item2.icon = 'el-icon-List'
|
||||
item2.color = config.getColorVal('elementUiPrimary')
|
||||
item2.children.forEach((item3: any) => {
|
||||
item3.pName = '监测设备'
|
||||
item3.icon = 'el-icon-Platform'
|
||||
item3.color = config.getColorVal('elementUiPrimary')
|
||||
if (item3.comFlag === 1) {
|
||||
@@ -102,45 +108,59 @@ getDeviceTree().then(res => {
|
||||
})
|
||||
}
|
||||
})
|
||||
console.log("🚀 ~ file: deviceTree.vue ~ line 18 ~ getDeviceTree ~ tree:", arr,arr2,arr3)
|
||||
tree.value = res.data
|
||||
|
||||
|
||||
nextTick(() => {
|
||||
if (arr.length) {
|
||||
treRef.value.treeRef1.setCurrentKey(arr[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr[0]
|
||||
})
|
||||
return
|
||||
}
|
||||
if (arr2.length) {
|
||||
treRef.value.treeRef2.setCurrentKey(arr2[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr2[0]
|
||||
})
|
||||
return
|
||||
}
|
||||
console.log("🚀 ~ file: deviceTree.vue ~ line 33 ~ getDeviceTree ~ tree:", arr3.length)
|
||||
if (arr3.length) {
|
||||
console.log("🚀 ~ file: deviceTree.vue ~ line 33 ~ getDeviceTree ~ tree:", arr3)
|
||||
treRef.value.treeRef3.setCurrentKey(arr3[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr3[0]
|
||||
})
|
||||
return
|
||||
}
|
||||
else {
|
||||
emit('init')
|
||||
return
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (arr.length > 0) {
|
||||
treRef.value.treeRef1.setCurrentKey(arr[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr[0]
|
||||
})
|
||||
return
|
||||
} else if (arr2.length > 0) {
|
||||
treRef.value.treeRef2.setCurrentKey(arr2[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr2[0]
|
||||
})
|
||||
return
|
||||
} else if (arr3.length > 0) {
|
||||
console.log('🚀 ~ arr3:', arr3)
|
||||
|
||||
treRef.value.treeRef3.setCurrentKey(arr3[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr3[0]
|
||||
})
|
||||
return
|
||||
} else {
|
||||
emit('init')
|
||||
return
|
||||
}
|
||||
}, 500)
|
||||
})
|
||||
})
|
||||
|
||||
throttle(
|
||||
(data: any, checked: any, indeterminate: any) => {
|
||||
emit('checkChange', {
|
||||
data,
|
||||
checked,
|
||||
indeterminate
|
||||
})
|
||||
},
|
||||
300,
|
||||
{
|
||||
leading: true, // 首次触发立即执行(可选,默认 true)
|
||||
trailing: false // 节流结束后是否执行最后一次(可选,默认 true,根据需求调整)
|
||||
}
|
||||
)
|
||||
|
||||
const handleCheckChange = (data: any, checked: any, indeterminate: any) => {
|
||||
emit('checkChange', {
|
||||
data,
|
||||
@@ -148,4 +168,7 @@ const handleCheckChange = (data: any, checked: any, indeterminate: any) => {
|
||||
indeterminate
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
treRef
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ref, nextTick, onMounted, defineProps } from 'vue'
|
||||
import Tree from '../point.vue'
|
||||
import { getLineTree } from '@/api/cs-device-boot/csLedger'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { getTemplateByDept } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { useDictData } from '@/stores/dictData'
|
||||
// const props = defineProps(['template'])
|
||||
interface Props {
|
||||
@@ -71,7 +71,7 @@ const info = () => {
|
||||
arr2.push(item2)
|
||||
})
|
||||
})
|
||||
} else if (item.name == '在线设备') {
|
||||
} else if (item.name == '监测设备') {
|
||||
item.children.forEach((item: any) => {
|
||||
item.icon = 'el-icon-HomeFilled'
|
||||
item.level = 1
|
||||
@@ -99,38 +99,38 @@ const info = () => {
|
||||
})
|
||||
tree.value = res.data
|
||||
nextTick(() => {
|
||||
if (arr1.length) {
|
||||
//初始化选中
|
||||
treRef.value.treeRef1.setCurrentKey(arr1[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr1[0]
|
||||
})
|
||||
return
|
||||
}
|
||||
if (arr2.length) {
|
||||
//初始化选中
|
||||
treRef.value.treeRef2.setCurrentKey(arr2[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr2[0]
|
||||
})
|
||||
return
|
||||
}
|
||||
if(arr3.length){
|
||||
treRef.value.treeRef3.setCurrentKey(arr3[0].id)
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr3[0]
|
||||
})
|
||||
return
|
||||
}
|
||||
else {
|
||||
emit('init')
|
||||
return
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (arr1.length > 0) {
|
||||
//初始化选中
|
||||
treRef.value?.treeRef1.setCurrentKey(arr1[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr1[0]
|
||||
})
|
||||
return
|
||||
} else if (arr2.length > 0) {
|
||||
//初始化选中
|
||||
treRef.value?.treeRef2.setCurrentKey(arr2[0].id)
|
||||
// 注册父组件事件
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr2[0]
|
||||
})
|
||||
return
|
||||
} else if (arr3.length > 0) {
|
||||
|
||||
treRef.value?.treeRef3?.setCurrentKey(arr3[0].id)
|
||||
emit('init', {
|
||||
level: 2,
|
||||
...arr3[0]
|
||||
})
|
||||
return
|
||||
} else {
|
||||
emit('init')
|
||||
return
|
||||
}
|
||||
}, 500)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -138,7 +138,7 @@ const changePointType = (val: any, obj: any) => {
|
||||
emit('pointTypeChange', val, obj)
|
||||
}
|
||||
if (props.template) {
|
||||
getTemplateByDept({ id: dictData.state.area[0].id })
|
||||
querySysExcel({ id: dictData.state.area[0]?.id })
|
||||
.then((res: any) => {
|
||||
emit('Policy', res.data)
|
||||
info()
|
||||
|
||||
@@ -53,7 +53,7 @@ import { getSchemeTree, getTestRecordInfo } from '@/api/cs-device-boot/planData'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import useCurrentInstance from '@/utils/useCurrentInstance'
|
||||
import { ElTree } from 'element-plus'
|
||||
import { getTemplateByDept } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { querySysExcel } from '@/api/harmonic-boot/luckyexcel'
|
||||
import { useDictData } from '@/stores/dictData'
|
||||
defineOptions({
|
||||
name: 'govern/schemeTree'
|
||||
@@ -157,7 +157,7 @@ const clickNode = (e: anyObj) => {
|
||||
}
|
||||
|
||||
if (props.template) {
|
||||
getTemplateByDept({ id: dictData.state.area[0].id })
|
||||
querySysExcel({ id: dictData.state.area[0]?.id })
|
||||
.then((res: any) => {
|
||||
emit('Policy', res.data)
|
||||
getTreeList()
|
||||
|
||||
@@ -33,7 +33,7 @@ const info = () => {
|
||||
getLineTree().then(res => {
|
||||
//治理设备
|
||||
res.data.map((item: any) => {
|
||||
if (item.name == '在线设备') {
|
||||
if (item.name == '监测设备') {
|
||||
item.children.forEach((item: any) => {
|
||||
item.icon = 'el-icon-HomeFilled'
|
||||
item.level = 1
|
||||
@@ -59,7 +59,7 @@ const info = () => {
|
||||
})
|
||||
}
|
||||
})
|
||||
tree.value = res.data.filter(item => item.name == '在线设备')
|
||||
tree.value = res.data.filter(item => item.name == '监测设备')
|
||||
nextTick(() => {
|
||||
if (arr3.length) {
|
||||
//初始化选中
|
||||
@@ -87,7 +87,7 @@ const handleCheckedNodesChange = (nodes: any[]) => {
|
||||
|
||||
|
||||
if (props.template) {
|
||||
getTemplateByDept({ id: dictData.state.area[0].id })
|
||||
getTemplateByDept({ id: dictData.state.area[0]?.id })
|
||||
.then((res: any) => {
|
||||
emit('Policy', res.data)
|
||||
info()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Icon name='el-icon-Search' style='font-size: 16px' />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tooltip placement="bottom" :hide-after="0">
|
||||
<el-tooltip placement="bottom" :hide-after="0" v-if="props.showPush">
|
||||
<template #content>
|
||||
<span>台账推送</span>
|
||||
</template>
|
||||
@@ -23,12 +23,12 @@
|
||||
:style="{ color: config.getColorVal('elementUiPrimary') }"
|
||||
@click="onAdd" />
|
||||
</el-tooltip>
|
||||
<!-- <Icon @click='onMenuCollapse' :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
<!-- <Icon @click='onMenuCollapse' :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'" v-else
|
||||
:class="menuCollapse ? 'unfold' : ''" size='18' class='fold ml10 menu-collapse'
|
||||
style='cursor: pointer' v-if='props.canExpand' /> -->
|
||||
</div>
|
||||
|
||||
<el-tree :style="{ height: 'calc(100vh - 200px)' }"
|
||||
<el-tree :style="{ height: 'calc(100vh - 190px)' }"
|
||||
style=' overflow: auto;' ref='treeRef' :props='defaultProps' highlight-current :default-expand-all="false"
|
||||
@check-change="checkTreeNodeChange" :filter-node-method='filterNode' node-key='id' v-bind='$attrs'>
|
||||
<template #default='{ node, data }'>
|
||||
@@ -58,11 +58,13 @@ defineOptions({
|
||||
interface Props {
|
||||
width?: string
|
||||
canExpand?: boolean
|
||||
showPush?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
width: '280px',
|
||||
canExpand: true
|
||||
canExpand: true,
|
||||
showPush: false
|
||||
})
|
||||
const config = useConfig()
|
||||
const { proxy } = useCurrentInstance()
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<!-- 设备监控使用折叠面板渲染多个tree -->
|
||||
<template>
|
||||
<div :style="{ width: menuCollapse ? '40px' : props.width }" style="display: flex; overflow: hidden">
|
||||
<Icon v-show="menuCollapse" @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 mt20 menu-collapse" style="cursor: pointer"
|
||||
v-if="route.path != '/admin/govern/reportCore/statistics/index'" />
|
||||
<Icon
|
||||
v-show="menuCollapse"
|
||||
@click="onMenuCollapse"
|
||||
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''"
|
||||
size="18"
|
||||
class="fold ml10 mt20 menu-collapse"
|
||||
style="cursor: pointer"
|
||||
v-if="route.path != '/admin/govern/reportCore/statistics/index'"
|
||||
/>
|
||||
<div class="cn-tree" :style="{ opacity: menuCollapse ? 0 : 1, display: menuCollapse ? 'none' : '' }">
|
||||
<div style="display: flex; align-items: center" class="mb10">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="filterText" placeholder="请输入内容" clearable>
|
||||
@@ -11,13 +18,23 @@
|
||||
<Icon name="el-icon-Search" style="font-size: 16px" />
|
||||
</template>
|
||||
</el-input>
|
||||
<Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 menu-collapse"
|
||||
<Icon
|
||||
@click="onMenuCollapse"
|
||||
:name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
|
||||
:class="menuCollapse ? 'unfold' : ''"
|
||||
size="18"
|
||||
class="fold ml10 menu-collapse"
|
||||
style="cursor: pointer"
|
||||
v-if="props.canExpand && route.path != '/admin/govern/reportCore/statistics/index'" />
|
||||
v-if="props.canExpand && route.path != '/admin/govern/reportCore/statistics/index'"
|
||||
/>
|
||||
</div>
|
||||
<el-collapse :accordion="true" v-model.trim="activeName" style="flex: 1; height: 100%"
|
||||
@change="changeDevice">
|
||||
|
||||
<el-collapse
|
||||
:accordion="true"
|
||||
v-model.trim="activeName"
|
||||
style="flex: 1; height: 100%"
|
||||
@change="changeDevice"
|
||||
>
|
||||
<el-collapse-item title="治理设备" name="0" v-if="zlDeviceData.length != 0">
|
||||
<el-select v-model.trim="process" clearable placeholder="请选择状态" class="mb10">
|
||||
<el-option label="功能调试" value="2"></el-option>
|
||||
@@ -26,14 +43,25 @@
|
||||
</el-select>
|
||||
|
||||
<el-tree
|
||||
:style="{ height: bxsDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 278px)' }"
|
||||
ref="treeRef1" :props="defaultProps" highlight-current :filter-node-method="filterNode"
|
||||
node-key="id" v-bind="$attrs" :data="zlDevList" style="overflow: auto"
|
||||
:default-expand-all="false">
|
||||
:style="{ height: bxsDeviceData.length != 0 ? 'calc(100vh - 380px)' : 'calc(100vh - 278px)' }"
|
||||
ref="treeRef1"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
v-bind="$attrs"
|
||||
:data="zlDevList"
|
||||
style="overflow: auto"
|
||||
:default-expand-all="false"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
|
||||
v-if="data.icon" />
|
||||
<Icon
|
||||
:name="data.icon"
|
||||
style="font-size: 16px"
|
||||
:style="{ color: data.color }"
|
||||
v-if="data.icon"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -41,29 +69,51 @@
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="便携式设备" name="1" v-if="bxsDeviceData.length != 0">
|
||||
<el-tree
|
||||
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 330px)' : 'calc(100vh - 238px)' }"
|
||||
ref="treeRef2" :props="defaultProps" highlight-current :default-expand-all="false"
|
||||
:filter-node-method="filterNode" node-key="id" :data="bxsDeviceData" v-bind="$attrs"
|
||||
style="overflow: auto" >
|
||||
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 238px)' }"
|
||||
ref="treeRef2"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
:default-expand-all="false"
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
:data="bxsDeviceData"
|
||||
v-bind="$attrs"
|
||||
style="overflow: auto"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
|
||||
v-if="data.icon" />
|
||||
<Icon
|
||||
:name="data.icon"
|
||||
style="font-size: 16px"
|
||||
:style="{ color: data.color }"
|
||||
v-if="data.icon"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="在线设备" name="2" v-if="yqfDeviceData.length != 0">
|
||||
<el-collapse-item title="监测设备" name="2" v-if="yqfDeviceData.length != 0">
|
||||
<el-tree
|
||||
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 330px)' : 'calc(100vh - 238px)' }"
|
||||
ref="treeRef3" :props="defaultProps" highlight-current :default-expand-all="false"
|
||||
:filter-node-method="filterNode" node-key="id" :data="yqfDeviceData" v-bind="$attrs"
|
||||
style="overflow: auto">
|
||||
:style="{ height: zlDeviceData.length != 0 ? 'calc(100vh - 340px)' : 'calc(100vh - 238px)' }"
|
||||
ref="treeRef3"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
:default-expand-all="false"
|
||||
:filter-node-method="filterNode"
|
||||
node-key="id"
|
||||
:data="yqfDeviceData"
|
||||
v-bind="$attrs"
|
||||
style="overflow: auto"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<Icon :name="data.icon" style="font-size: 16px" :style="{ color: data.color }"
|
||||
v-if="data.icon" />
|
||||
<Icon
|
||||
:name="data.icon"
|
||||
style="font-size: 16px"
|
||||
:style="{ color: data.color }"
|
||||
v-if="data.icon"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -112,33 +162,31 @@ const zlDeviceData = ref<any>([])
|
||||
const zlDevList = ref<any>([])
|
||||
//便携式设备数据
|
||||
const bxsDeviceData = ref<any>([])
|
||||
//在线设备数据
|
||||
//监测设备数据
|
||||
const yqfDeviceData = ref<any>([])
|
||||
watch(
|
||||
() => props.data,
|
||||
(val, oldVal) => {
|
||||
if (val && val.length != 0) {
|
||||
|
||||
val.map((item: any) => {
|
||||
if (item.name == '治理设备') {
|
||||
zlDeviceData.value = []
|
||||
item.children.map((vv: any) => {
|
||||
zlDeviceData.value.push(vv)
|
||||
})
|
||||
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
|
||||
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
|
||||
} else if (item.name == '便携式设备') {
|
||||
bxsDeviceData.value = []
|
||||
item.children.map((vv: any) => {
|
||||
bxsDeviceData.value.push(vv)
|
||||
})
|
||||
}else if (item.name == '在线设备') {
|
||||
} else if (item.name == '监测设备') {
|
||||
yqfDeviceData.value = []
|
||||
item.children.map((vv: any) => {
|
||||
yqfDeviceData.value.push(vv)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -157,12 +205,10 @@ watch(filterText, val => {
|
||||
}
|
||||
})
|
||||
watch(process, val => {
|
||||
|
||||
if (val == '') {
|
||||
if (val == '' || val == undefined) {
|
||||
zlDevList.value = JSON.parse(JSON.stringify(zlDeviceData.value))
|
||||
} else {
|
||||
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
|
||||
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -170,9 +216,8 @@ watch(process, val => {
|
||||
}, 0)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const changeDevice = (val: any) => {
|
||||
console.log('🚀 ~ changeDevice ~ val:', val)
|
||||
|
||||
let arr1: any = []
|
||||
//zlDeviceData
|
||||
@@ -206,22 +251,28 @@ const changeDevice = (val: any) => {
|
||||
arr2.map((item: any) => {
|
||||
item.checked = false
|
||||
})
|
||||
treeRef1?.value && treeRef1.value.setCurrentKey(arr1[0]?.id)
|
||||
emit('changePointType', activeName.value, arr1[0])
|
||||
setTimeout(() => {
|
||||
treeRef1.value?.setCurrentKey(arr1[0]?.id)
|
||||
}, 100)
|
||||
}
|
||||
if (val == '1') {
|
||||
arr1.map((item: any) => {
|
||||
item.checked = false
|
||||
})
|
||||
treeRef2?.value && treeRef2.value.setCurrentKey(arr2[0]?.id)
|
||||
emit('changePointType', activeName.value, arr2[0])
|
||||
setTimeout(() => {
|
||||
treeRef2.value?.setCurrentKey(arr2[0]?.id)
|
||||
}, 100)
|
||||
}
|
||||
if (val == '2') {
|
||||
arr3.map((item: any) => {
|
||||
item.checked = false
|
||||
})
|
||||
treeRef3?.value && treeRef3.value.setCurrentKey(arr3[0]?.id)
|
||||
emit('changePointType', activeName.value, arr3[0])
|
||||
setTimeout(() => {
|
||||
treeRef3.value?.setCurrentKey(arr3[0]?.id)
|
||||
}, 100)
|
||||
}
|
||||
// if(activeName.value){
|
||||
// emit('changePointType', activeName.value)
|
||||
@@ -263,8 +314,7 @@ function filterProcess(nodes: any) {
|
||||
// 1. 如果有满足条件的子节点则保留
|
||||
// 2. 如果本身 process 值匹配则保留
|
||||
// 3. 如果是叶子节点也保留(监测点通常没有子节点)
|
||||
if (children.length > 0 || node.process == process.value ||
|
||||
(!node.children || node.children.length === 0)) {
|
||||
if (children.length > 0 || node.process == process.value || !node.children || node.children.length === 0) {
|
||||
return {
|
||||
...node,
|
||||
children: children
|
||||
@@ -276,7 +326,6 @@ function filterProcess(nodes: any) {
|
||||
.filter(Boolean) // 移除null节点
|
||||
}
|
||||
|
||||
|
||||
// function filterProcess(nodes: any) {
|
||||
// if (process.value == '') {
|
||||
// return nodes
|
||||
@@ -332,11 +381,9 @@ const treeRef1 = ref<InstanceType<typeof ElTree>>()
|
||||
const treeRef2 = ref<InstanceType<typeof ElTree>>()
|
||||
//在线
|
||||
const treeRef3 = ref<InstanceType<typeof ElTree>>()
|
||||
defineExpose({ treeRef1, treeRef2 })
|
||||
defineExpose({ treeRef1, treeRef2, treeRef3 })
|
||||
onMounted(() => {
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
if (zlDeviceData.value.length != 0) {
|
||||
zlDevList.value = filterProcess(JSON.parse(JSON.stringify(zlDeviceData.value)))
|
||||
activeName.value = '0'
|
||||
@@ -344,8 +391,11 @@ onMounted(() => {
|
||||
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length != 0) {
|
||||
activeName.value = '1'
|
||||
}
|
||||
if (zlDeviceData.value.length === 0 && bxsDeviceData.value.length === 0) {
|
||||
activeName.value = '2'
|
||||
}
|
||||
if (!zlDeviceData.value && !bxsDeviceData.value) {
|
||||
activeName.value = ''
|
||||
activeName.value = '2'
|
||||
}
|
||||
nextTick(() => {
|
||||
changeDevice(activeName.value)
|
||||
|
||||
@@ -218,11 +218,12 @@ const updateNodeCheckStatus = (currentCount: number) => {
|
||||
const allNodes = treeRef.value.store._getAllNodes()
|
||||
allNodes.forEach((node: any) => {
|
||||
if (node.level === 3) { // 监测点层级
|
||||
|
||||
// 如果已达到最大数量且该节点未被选中,则禁用勾选
|
||||
if (isMaxSelected && !node.checked) {
|
||||
node.disabled = true
|
||||
if (isMaxSelected && !node.data.checked) {
|
||||
node.data.disabled = true
|
||||
} else {
|
||||
node.disabled = false
|
||||
node.data.disabled = false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,359 +1,359 @@
|
||||
<template>
|
||||
<div class="layout-config-drawer">
|
||||
<el-drawer :model-value="configStore.layout.showDrawer" title="布局配置" size="310px" @close="onCloseDrawer">
|
||||
<el-scrollbar class="layout-mode-style-scrollbar">
|
||||
<el-form ref="formRef" :model="configStore.layout">
|
||||
<div class="layout-mode-styles-box">
|
||||
<el-divider border-style="dashed">全局</el-divider>
|
||||
<div class="layout-config-global">
|
||||
<el-form-item label="后台页面切换动画">
|
||||
<el-select @change="onCommitState($event, 'mainAnimation')"
|
||||
:model-value="configStore.layout.mainAnimation"
|
||||
:placeholder="'layouts.Please select an animation name'">
|
||||
<el-option label="slide-right" value="slide-right"></el-option>
|
||||
<el-option label="slide-left" value="slide-left"></el-option>
|
||||
<el-option label="el-fade-in-linear" value="el-fade-in-linear"></el-option>
|
||||
<el-option label="el-fade-in" value="el-fade-in"></el-option>
|
||||
<el-option label="el-zoom-in-center" value="el-zoom-in-center"></el-option>
|
||||
<el-option label="el-zoom-in-top" value="el-zoom-in-top"></el-option>
|
||||
<el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="组件主题色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'elementUiPrimary')"
|
||||
:model-value="configStore.getColorVal('elementUiPrimary')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表格标题栏背景颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'tableHeaderBackground')"
|
||||
:model-value="configStore.getColorVal('tableHeaderBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表格标题栏文字颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'tableHeaderColor')"
|
||||
:model-value="configStore.getColorVal('tableHeaderColor')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表格激活栏颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'tableCurrent')"
|
||||
:model-value="configStore.getColorVal('tableCurrent')" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-divider border-style="dashed">侧边栏</el-divider>
|
||||
<div class="layout-config-aside">
|
||||
<el-form-item label="侧边菜单栏背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuBackground')"
|
||||
:model-value="configStore.getColorVal('menuBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单文字颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuColor')"
|
||||
:model-value="configStore.getColorVal('menuColor')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单激活项背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuActiveBackground')"
|
||||
:model-value="configStore.getColorVal('menuActiveBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单激活项文字色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuActiveColor')"
|
||||
:model-value="configStore.getColorVal('menuActiveColor')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示侧边菜单顶栏(LOGO栏)">
|
||||
<el-switch @change="onCommitState($event, 'menuShowTopBar')"
|
||||
:model-value="configStore.layout.menuShowTopBar"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单顶栏背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuTopBarBackground')"
|
||||
:model-value="configStore.getColorVal('menuTopBarBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单宽度(展开时)">
|
||||
<el-input maxlength="32" show-word-limit @input="onCommitState($event, 'menuWidth')"
|
||||
type="number" :step="10" :model-value="configStore.layout.menuWidth">
|
||||
<template #append>px</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="侧边菜单默认图标">-->
|
||||
<!-- <IconSelector-->
|
||||
<!-- @change="onCommitMenuDefaultIcon($event, 'menuDefaultIcon')"-->
|
||||
<!-- :model-value="configStore.layout.menuDefaultIcon"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="侧边菜单水平折叠">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- @change="onCommitState($event, 'menuCollapse')"-->
|
||||
<!-- :model-value="configStore.layout.menuCollapse"-->
|
||||
<!-- ></el-switch>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="侧边菜单手风琴">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- @change="onCommitState($event, 'menuUniqueOpened')"-->
|
||||
<!-- :model-value="configStore.layout.menuUniqueOpened"-->
|
||||
<!-- ></el-switch>-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
|
||||
<el-divider border-style="dashed">顶栏</el-divider>
|
||||
<div class="layout-config-aside">
|
||||
<el-form-item label="顶栏背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'headerBarBackground')"
|
||||
:model-value="configStore.getColorVal('headerBarBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="顶栏文字色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'headerBarTabColor')"
|
||||
:model-value="configStore.getColorVal('headerBarTabColor')" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="顶栏悬停时背景色">-->
|
||||
<!-- <el-color-picker-->
|
||||
<!-- @change="onCommitColorState($event, 'headerBarHoverBackground')"-->
|
||||
<!-- :model-value="configStore.getColorVal('headerBarHoverBackground')"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="顶栏菜单激活项背景色">-->
|
||||
<!-- <el-color-picker-->
|
||||
<!-- @change="onCommitColorState($event, 'headerBarTabActiveBackground')"-->
|
||||
<!-- :model-value="configStore.getColorVal('headerBarTabActiveBackground')"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="顶栏菜单激活项文字色">-->
|
||||
<!-- <el-color-picker-->
|
||||
<!-- @change="onCommitColorState($event, 'headerBarTabActiveColor')"-->
|
||||
<!-- :model-value="configStore.getColorVal('headerBarTabActiveColor')"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
<el-popconfirm @confirm="restoreDefault" title="确定要恢复全部配置到默认值吗?">
|
||||
<template #reference>
|
||||
<div class="ba-center">
|
||||
<el-button class="w80" type="info">恢复默认</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
import { useRouter } from 'vue-router'
|
||||
import IconSelector from '@/components/baInput/components/iconSelector.vue'
|
||||
import { STORE_CONFIG } from '@/stores/constant/cacheKey'
|
||||
import { Local, Session } from '@/utils/storage'
|
||||
import type { Layout } from '@/stores/interface'
|
||||
|
||||
const configStore = useConfig()
|
||||
const navTabs = useNavTabs()
|
||||
const router = useRouter()
|
||||
|
||||
const onCommitState = (value: any, name: any) => {
|
||||
configStore.setLayout(name, value)
|
||||
}
|
||||
|
||||
const onCommitColorState = (value: string | null, name: keyof Layout) => {
|
||||
if (value === null) return
|
||||
const colors = configStore.layout[name] as string[]
|
||||
if (configStore.layout.isDark) {
|
||||
colors[1] = value
|
||||
} else {
|
||||
colors[0] = value
|
||||
}
|
||||
configStore.setLayout(name, colors)
|
||||
}
|
||||
|
||||
const setLayoutMode = (mode: string) => {
|
||||
configStore.setLayoutMode(mode)
|
||||
}
|
||||
|
||||
// 修改默认菜单图标
|
||||
const onCommitMenuDefaultIcon = (value: any, name: any) => {
|
||||
configStore.setLayout(name, value)
|
||||
|
||||
const menus = navTabs.state.tabsViewRoutes
|
||||
navTabs.setTabsViewRoutes([])
|
||||
setTimeout(() => {
|
||||
navTabs.setTabsViewRoutes(menus)
|
||||
}, 200)
|
||||
}
|
||||
|
||||
const onCloseDrawer = () => {
|
||||
configStore.setLayout('showDrawer', false)
|
||||
}
|
||||
|
||||
const restoreDefault = () => {
|
||||
Local.remove(STORE_CONFIG)
|
||||
router.go(0)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-config-drawer :deep(.el-input__inner) {
|
||||
padding: 0 0 0 6px;
|
||||
}
|
||||
|
||||
.layout-config-drawer :deep(.el-input-group__append) {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.layout-config-drawer :deep(.el-drawer__header) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.layout-config-drawer :deep(.el-drawer__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.layout-mode-styles-box {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.layout-mode-box-style-row {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.layout-mode-style {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: var(--el-border-radius-small);
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
.layout-mode-style-name {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--el-color-primary-light-5);
|
||||
border-radius: 50%;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border: 1px solid var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
.layout-mode-style-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-aside {
|
||||
width: 18%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 68%;
|
||||
height: 90%;
|
||||
margin-left: 4%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 85%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
margin-top: 5%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.classic {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-aside {
|
||||
width: 18%;
|
||||
height: 100%;
|
||||
background-color: var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 82%;
|
||||
height: 100%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.streamline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.double {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-aside {
|
||||
width: 18%;
|
||||
height: 100%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 82%;
|
||||
height: 100%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.w80 {
|
||||
width: 90%;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="layout-config-drawer">
|
||||
<el-drawer :model-value="configStore.layout.showDrawer" title="布局配置" size="310px" @close="onCloseDrawer">
|
||||
<el-scrollbar class="layout-mode-style-scrollbar">
|
||||
<el-form ref="formRef" :model="configStore.layout">
|
||||
<div class="layout-mode-styles-box">
|
||||
<el-divider border-style="dashed">全局</el-divider>
|
||||
<div class="layout-config-global">
|
||||
<el-form-item label="后台页面切换动画">
|
||||
<el-select @change="onCommitState($event, 'mainAnimation')"
|
||||
:model-value="configStore.layout.mainAnimation"
|
||||
:placeholder="'layouts.Please select an animation name'">
|
||||
<el-option label="slide-right" value="slide-right"></el-option>
|
||||
<el-option label="slide-left" value="slide-left"></el-option>
|
||||
<el-option label="el-fade-in-linear" value="el-fade-in-linear"></el-option>
|
||||
<el-option label="el-fade-in" value="el-fade-in"></el-option>
|
||||
<el-option label="el-zoom-in-center" value="el-zoom-in-center"></el-option>
|
||||
<el-option label="el-zoom-in-top" value="el-zoom-in-top"></el-option>
|
||||
<el-option label="el-zoom-in-bottom" value="el-zoom-in-bottom"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="组件主题色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'elementUiPrimary')"
|
||||
:model-value="configStore.getColorVal('elementUiPrimary')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表格标题栏背景颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'tableHeaderBackground')"
|
||||
:model-value="configStore.getColorVal('tableHeaderBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表格标题栏文字颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'tableHeaderColor')"
|
||||
:model-value="configStore.getColorVal('tableHeaderColor')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表格激活栏颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'tableCurrent')"
|
||||
:model-value="configStore.getColorVal('tableCurrent')" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-divider border-style="dashed">侧边栏</el-divider>
|
||||
<div class="layout-config-aside">
|
||||
<el-form-item label="侧边菜单栏背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuBackground')"
|
||||
:model-value="configStore.getColorVal('menuBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单文字颜色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuColor')"
|
||||
:model-value="configStore.getColorVal('menuColor')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单激活项背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuActiveBackground')"
|
||||
:model-value="configStore.getColorVal('menuActiveBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单激活项文字色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuActiveColor')"
|
||||
:model-value="configStore.getColorVal('menuActiveColor')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示侧边菜单顶栏(LOGO栏)">
|
||||
<el-switch @change="onCommitState($event, 'menuShowTopBar')"
|
||||
:model-value="configStore.layout.menuShowTopBar"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单顶栏背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'menuTopBarBackground')"
|
||||
:model-value="configStore.getColorVal('menuTopBarBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="侧边菜单宽度(展开时)">
|
||||
<el-input maxlength="32" show-word-limit @input="onCommitState($event, 'menuWidth')"
|
||||
type="number" :step="10" :model-value="configStore.layout.menuWidth">
|
||||
<template #append>px</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="侧边菜单默认图标">-->
|
||||
<!-- <IconSelector-->
|
||||
<!-- @change="onCommitMenuDefaultIcon($event, 'menuDefaultIcon')"-->
|
||||
<!-- :model-value="configStore.layout.menuDefaultIcon"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="侧边菜单水平折叠">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- @change="onCommitState($event, 'menuCollapse')"-->
|
||||
<!-- :model-value="configStore.layout.menuCollapse"-->
|
||||
<!-- ></el-switch>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="侧边菜单手风琴">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- @change="onCommitState($event, 'menuUniqueOpened')"-->
|
||||
<!-- :model-value="configStore.layout.menuUniqueOpened"-->
|
||||
<!-- ></el-switch>-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
|
||||
<el-divider border-style="dashed">顶栏</el-divider>
|
||||
<div class="layout-config-aside">
|
||||
<el-form-item label="顶栏背景色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'headerBarBackground')"
|
||||
:model-value="configStore.getColorVal('headerBarBackground')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="顶栏文字色">
|
||||
<el-color-picker @change="onCommitColorState($event, 'headerBarTabColor')"
|
||||
:model-value="configStore.getColorVal('headerBarTabColor')" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="顶栏悬停时背景色">-->
|
||||
<!-- <el-color-picker-->
|
||||
<!-- @change="onCommitColorState($event, 'headerBarHoverBackground')"-->
|
||||
<!-- :model-value="configStore.getColorVal('headerBarHoverBackground')"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="顶栏菜单激活项背景色">-->
|
||||
<!-- <el-color-picker-->
|
||||
<!-- @change="onCommitColorState($event, 'headerBarTabActiveBackground')"-->
|
||||
<!-- :model-value="configStore.getColorVal('headerBarTabActiveBackground')"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item label="顶栏菜单激活项文字色">-->
|
||||
<!-- <el-color-picker-->
|
||||
<!-- @change="onCommitColorState($event, 'headerBarTabActiveColor')"-->
|
||||
<!-- :model-value="configStore.getColorVal('headerBarTabActiveColor')"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
</div>
|
||||
<el-popconfirm @confirm="restoreDefault" title="确定要恢复全部配置到默认值吗?">
|
||||
<template #reference>
|
||||
<div class="ba-center">
|
||||
<el-button class="w80" type="info">恢复默认</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
import { useRouter } from 'vue-router'
|
||||
import IconSelector from '@/components/baInput/components/iconSelector.vue'
|
||||
import { STORE_CONFIG } from '@/stores/constant/cacheKey'
|
||||
import { Local, Session } from '@/utils/storage'
|
||||
import type { Layout } from '@/stores/interface'
|
||||
|
||||
const configStore = useConfig()
|
||||
const navTabs = useNavTabs()
|
||||
const router = useRouter()
|
||||
|
||||
const onCommitState = (value: any, name: any) => {
|
||||
configStore.setLayout(name, value)
|
||||
}
|
||||
|
||||
const onCommitColorState = (value: string | null, name: keyof Layout) => {
|
||||
if (value === null) return
|
||||
const colors = configStore.layout[name] as string[]
|
||||
if (configStore.layout.isDark) {
|
||||
colors[1] = value
|
||||
} else {
|
||||
colors[0] = value
|
||||
}
|
||||
configStore.setLayout(name, colors)
|
||||
}
|
||||
|
||||
const setLayoutMode = (mode: string) => {
|
||||
configStore.setLayoutMode(mode)
|
||||
}
|
||||
|
||||
// 修改默认菜单图标
|
||||
const onCommitMenuDefaultIcon = (value: any, name: any) => {
|
||||
configStore.setLayout(name, value)
|
||||
|
||||
const menus = navTabs.state.tabsViewRoutes
|
||||
navTabs.setTabsViewRoutes([])
|
||||
setTimeout(() => {
|
||||
navTabs.setTabsViewRoutes(menus)
|
||||
}, 200)
|
||||
}
|
||||
|
||||
const onCloseDrawer = () => {
|
||||
configStore.setLayout('showDrawer', false)
|
||||
}
|
||||
|
||||
const restoreDefault = () => {
|
||||
Local.remove(STORE_CONFIG)
|
||||
router.go(0)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-config-drawer :deep(.el-input__inner) {
|
||||
padding: 0 0 0 6px;
|
||||
}
|
||||
|
||||
.layout-config-drawer :deep(.el-input-group__append) {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.layout-config-drawer :deep(.el-drawer__header) {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.layout-config-drawer :deep(.el-drawer__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.layout-mode-styles-box {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.layout-mode-box-style-row {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.layout-mode-style {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: var(--el-border-radius-small);
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
border: 1px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
.layout-mode-style-name {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--el-color-primary-light-5);
|
||||
border-radius: 50%;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border: 1px solid var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
.layout-mode-style-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-aside {
|
||||
width: 18%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 68%;
|
||||
height: 90%;
|
||||
margin-left: 4%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 85%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
margin-top: 5%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.classic {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-aside {
|
||||
width: 18%;
|
||||
height: 100%;
|
||||
background-color: var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 82%;
|
||||
height: 100%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.streamline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.double {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.layout-mode-style-aside {
|
||||
width: 18%;
|
||||
height: 100%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container-box {
|
||||
width: 82%;
|
||||
height: 100%;
|
||||
|
||||
.layout-mode-style-header {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.layout-mode-style-container {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.w80 {
|
||||
width: 90%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,81 +1,81 @@
|
||||
<template>
|
||||
<el-scrollbar ref="verticalMenusRef" class="vertical-menus-scrollbar">
|
||||
<el-menu
|
||||
class="layouts-menu-vertical"
|
||||
:collapse-transition="false"
|
||||
:unique-opened="config.layout.menuUniqueOpened"
|
||||
:default-active="state.defaultActive"
|
||||
:collapse="config.layout.menuCollapse"
|
||||
>
|
||||
<MenuTree :menus="navTabs.state.tabsViewRoutes" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import MenuTree from '@/layouts/admin/components/menus/menuTree.vue'
|
||||
import { useRoute, onBeforeRouteUpdate, type RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import type { ScrollbarInstance } from 'element-plus'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
|
||||
const config = useConfig()
|
||||
const navTabs = useNavTabs()
|
||||
const route = useRoute()
|
||||
|
||||
const verticalMenusRef = ref<ScrollbarInstance>()
|
||||
|
||||
const state = reactive({
|
||||
defaultActive: '',
|
||||
})
|
||||
|
||||
const verticalMenusScrollbarHeight = computed(() => {
|
||||
let menuTopBarHeight = 0
|
||||
if (config.layout.menuShowTopBar) {
|
||||
menuTopBarHeight = 50
|
||||
}
|
||||
if (config.layout.layoutMode == 'Default') {
|
||||
return 'calc(100vh - ' + (32 + menuTopBarHeight) + 'px)'
|
||||
} else {
|
||||
return 'calc(100vh - ' + menuTopBarHeight + 'px)'
|
||||
}
|
||||
})
|
||||
|
||||
// 激活当前路由的菜单
|
||||
const currentRouteActive = (currentRoute: RouteLocationNormalizedLoaded) => {
|
||||
state.defaultActive = currentRoute.path
|
||||
}
|
||||
|
||||
// 滚动条滚动到激活菜单所在位置
|
||||
const verticalMenusScroll = () => {
|
||||
nextTick(() => {
|
||||
let activeMenu: HTMLElement | null = document.querySelector('.el-menu.layouts-menu-vertical li.is-active')
|
||||
if (!activeMenu) return false
|
||||
verticalMenusRef.value?.setScrollTop(activeMenu.offsetTop)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
currentRouteActive(route)
|
||||
verticalMenusScroll()
|
||||
})
|
||||
|
||||
onBeforeRouteUpdate((to) => {
|
||||
currentRouteActive(to)
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.vertical-menus-scrollbar {
|
||||
height: v-bind(verticalMenusScrollbarHeight);
|
||||
background-color: v-bind('config.getColorVal("menuBackground")');
|
||||
}
|
||||
.layouts-menu-vertical {
|
||||
border: 0;
|
||||
padding-bottom: 30px;
|
||||
--el-menu-bg-color: v-bind('config.getColorVal("menuBackground")');
|
||||
--el-menu-text-color: v-bind('config.getColorVal("menuColor")');
|
||||
--el-menu-active-color: v-bind('config.getColorVal("menuActiveColor")');
|
||||
--el-menu-hover-color: v-bind('config.getColorVal("menuActiveBackground")');
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<el-scrollbar ref="verticalMenusRef" class="vertical-menus-scrollbar">
|
||||
<el-menu
|
||||
class="layouts-menu-vertical"
|
||||
:collapse-transition="false"
|
||||
:unique-opened="config.layout.menuUniqueOpened"
|
||||
:default-active="state.defaultActive"
|
||||
:collapse="config.layout.menuCollapse"
|
||||
>
|
||||
<MenuTree :menus="navTabs.state.tabsViewRoutes" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import MenuTree from '@/layouts/admin/components/menus/menuTree.vue'
|
||||
import { useRoute, onBeforeRouteUpdate, type RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import type { ScrollbarInstance } from 'element-plus'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
|
||||
const config = useConfig()
|
||||
const navTabs = useNavTabs()
|
||||
const route = useRoute()
|
||||
|
||||
const verticalMenusRef = ref<ScrollbarInstance>()
|
||||
|
||||
const state = reactive({
|
||||
defaultActive: '',
|
||||
})
|
||||
|
||||
const verticalMenusScrollbarHeight = computed(() => {
|
||||
let menuTopBarHeight = 0
|
||||
if (config.layout.menuShowTopBar) {
|
||||
menuTopBarHeight = 50
|
||||
}
|
||||
if (config.layout.layoutMode == 'Default') {
|
||||
return 'calc(100vh - ' + (32 + menuTopBarHeight) + 'px)'
|
||||
} else {
|
||||
return 'calc(100vh - ' + menuTopBarHeight + 'px)'
|
||||
}
|
||||
})
|
||||
|
||||
// 激活当前路由的菜单
|
||||
const currentRouteActive = (currentRoute: RouteLocationNormalizedLoaded) => {
|
||||
state.defaultActive = currentRoute.path
|
||||
}
|
||||
|
||||
// 滚动条滚动到激活菜单所在位置
|
||||
const verticalMenusScroll = () => {
|
||||
nextTick(() => {
|
||||
let activeMenu: HTMLElement | null = document.querySelector('.el-menu.layouts-menu-vertical li.is-active')
|
||||
if (!activeMenu) return false
|
||||
verticalMenusRef.value?.setScrollTop(activeMenu.offsetTop)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
currentRouteActive(route)
|
||||
verticalMenusScroll()
|
||||
})
|
||||
|
||||
onBeforeRouteUpdate((to) => {
|
||||
currentRouteActive(to)
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.vertical-menus-scrollbar {
|
||||
height: v-bind(verticalMenusScrollbarHeight);
|
||||
background-color: v-bind('config.getColorVal("menuBackground")');
|
||||
}
|
||||
.layouts-menu-vertical {
|
||||
border: 0;
|
||||
padding-bottom: 30px;
|
||||
--el-menu-bg-color: v-bind('config.getColorVal("menuBackground")');
|
||||
--el-menu-text-color: v-bind('config.getColorVal("menuColor")');
|
||||
--el-menu-active-color: v-bind('config.getColorVal("menuActiveColor")');
|
||||
--el-menu-hover-color: v-bind('config.getColorVal("menuActiveBackground")');
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,242 +1,246 @@
|
||||
<template>
|
||||
<div class="nav-tabs" ref="tabScrollbarRef">
|
||||
<div
|
||||
v-for="(item, idx) in navTabs.state.tabsView"
|
||||
@click="onTab(item)"
|
||||
@contextmenu.prevent="onContextmenu(item, $event)"
|
||||
class="ba-nav-tab"
|
||||
:class="navTabs.state.activeIndex == idx ? 'active' : ''"
|
||||
:ref="tabsRefs.set"
|
||||
:key="idx"
|
||||
>
|
||||
{{ item.meta.title }}
|
||||
<transition @after-leave="selectNavTab(tabsRefs[navTabs.state.activeIndex])" name="el-fade-in">
|
||||
<Icon
|
||||
v-if="navTabs.state.tabsView.length > 1"
|
||||
class="close-icon"
|
||||
@click.stop="closeTab(item)"
|
||||
size="15"
|
||||
name="el-icon-Close"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
<!-- <div :style='activeBoxStyle' class='nav-tabs-active-box'></div>-->
|
||||
</div>
|
||||
<Contextmenu ref="contextmenuRef" :items="state.contextmenuItems" @contextmenuItemClick="onContextmenuItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import { useRoute, useRouter, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
import { useTemplateRefsList } from '@vueuse/core'
|
||||
import type { ContextMenuItem, ContextmenuItemClickEmitArg } from '@/components/contextmenu/interface'
|
||||
import useCurrentInstance from '@/utils/useCurrentInstance'
|
||||
import Contextmenu from '@/components/contextmenu/index.vue'
|
||||
import horizontalScroll from '@/utils/horizontalScroll'
|
||||
import { getFirstRoute, routePush } from '@/utils/router'
|
||||
import { adminBaseRoutePath } from '@/router/static'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const config = useConfig()
|
||||
const navTabs = useNavTabs()
|
||||
|
||||
const { proxy } = useCurrentInstance()
|
||||
const tabScrollbarRef = ref()
|
||||
const tabsRefs = useTemplateRefsList<HTMLDivElement>()
|
||||
|
||||
const contextmenuRef = ref()
|
||||
|
||||
const state: {
|
||||
contextmenuItems: ContextMenuItem[]
|
||||
} = reactive({
|
||||
contextmenuItems: [
|
||||
{ name: 'refresh', label: '重新加载', icon: 'fa fa-refresh' },
|
||||
{ name: 'close', label: '关闭标签', icon: 'fa fa-times' },
|
||||
{ name: 'fullScreen', label: '当前标签全屏', icon: 'el-icon-FullScreen' },
|
||||
{ name: 'closeOther', label: '关闭其他标签', icon: 'fa fa-minus' },
|
||||
{ name: 'closeAll', label: '关闭全部标签', icon: 'fa fa-stop' }
|
||||
]
|
||||
})
|
||||
|
||||
const activeBoxStyle = reactive({
|
||||
width: '0',
|
||||
transform: 'translateX(0px)'
|
||||
})
|
||||
|
||||
const onTab = (menu: RouteLocationNormalized) => {
|
||||
router.push(menu)
|
||||
}
|
||||
|
||||
const onContextmenu = (menu: RouteLocationNormalized, el: MouseEvent) => {
|
||||
// 禁用刷新
|
||||
state.contextmenuItems[0].disabled = route.path !== menu.path
|
||||
// 禁用关闭其他和关闭全部
|
||||
state.contextmenuItems[4].disabled = state.contextmenuItems[3].disabled =
|
||||
navTabs.state.tabsView.length == 1 ? true : false
|
||||
|
||||
const { clientX, clientY } = el
|
||||
contextmenuRef.value.onShowContextmenu(menu, {
|
||||
x: clientX,
|
||||
y: clientY
|
||||
})
|
||||
}
|
||||
|
||||
// tab 激活状态切换
|
||||
const selectNavTab = function (dom: HTMLDivElement) {
|
||||
if (!dom) {
|
||||
return false
|
||||
}
|
||||
activeBoxStyle.width = dom.clientWidth + 'px'
|
||||
activeBoxStyle.transform = `translateX(${dom.offsetLeft}px)`
|
||||
|
||||
let scrollLeft = dom.offsetLeft + dom.clientWidth - tabScrollbarRef.value.clientWidth
|
||||
if (dom.offsetLeft < tabScrollbarRef.value.scrollLeft) {
|
||||
tabScrollbarRef.value.scrollTo(dom.offsetLeft, 0)
|
||||
} else if (scrollLeft > tabScrollbarRef.value.scrollLeft) {
|
||||
tabScrollbarRef.value.scrollTo(scrollLeft, 0)
|
||||
}
|
||||
}
|
||||
|
||||
const toLastTab = () => {
|
||||
const lastTab = navTabs.state.tabsView.slice(-1)[0]
|
||||
if (lastTab) {
|
||||
router.push(lastTab)
|
||||
} else {
|
||||
router.push(adminBaseRoutePath)
|
||||
}
|
||||
}
|
||||
|
||||
const closeTab = (route: RouteLocationNormalized) => {
|
||||
navTabs.closeTab(route)
|
||||
proxy.eventBus.emit('onTabViewClose', route)
|
||||
if (navTabs.state.activeRoute?.path === route.path) {
|
||||
toLastTab()
|
||||
} else {
|
||||
navTabs.setActiveRoute(navTabs.state.activeRoute!)
|
||||
nextTick(() => {
|
||||
selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
|
||||
})
|
||||
}
|
||||
|
||||
contextmenuRef.value.onHideContextmenu()
|
||||
}
|
||||
|
||||
const closeOtherTab = (menu: RouteLocationNormalized) => {
|
||||
navTabs.closeTabs(menu)
|
||||
navTabs.setActiveRoute(menu)
|
||||
if (navTabs.state.activeRoute?.path !== route.path) {
|
||||
router.push(menu!.path)
|
||||
}
|
||||
}
|
||||
|
||||
const closeAllTab = (menu: RouteLocationNormalized) => {
|
||||
let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
|
||||
if (firstRoute && firstRoute.path == menu.path) {
|
||||
return closeOtherTab(menu)
|
||||
}
|
||||
if (firstRoute && firstRoute.path == navTabs.state.activeRoute?.path) {
|
||||
return closeOtherTab(navTabs.state.activeRoute)
|
||||
}
|
||||
navTabs.closeTabs(false)
|
||||
if (firstRoute) routePush(firstRoute.path)
|
||||
}
|
||||
|
||||
const onContextmenuItem = async (item: ContextmenuItemClickEmitArg) => {
|
||||
const { name, menu } = item
|
||||
if (!menu) return
|
||||
switch (name) {
|
||||
case 'refresh':
|
||||
proxy.eventBus.emit('onTabViewRefresh', menu)
|
||||
break
|
||||
case 'close':
|
||||
closeTab(menu)
|
||||
break
|
||||
case 'closeOther':
|
||||
closeOtherTab(menu)
|
||||
break
|
||||
case 'closeAll':
|
||||
closeAllTab(menu)
|
||||
break
|
||||
case 'fullScreen':
|
||||
if (route.path !== menu?.path) {
|
||||
router.push(menu?.path as string)
|
||||
}
|
||||
navTabs.setFullScreen(true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const updateTab = function (newRoute: RouteLocationNormalized) {
|
||||
// 添加tab
|
||||
navTabs.addTab(newRoute)
|
||||
// 激活当前tab
|
||||
navTabs.setActiveRoute(newRoute)
|
||||
|
||||
nextTick(() => {
|
||||
selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate(async to => {
|
||||
|
||||
|
||||
updateTab(to)
|
||||
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
updateTab(router.currentRoute.value)
|
||||
new horizontalScroll(tabScrollbarRef.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dark {
|
||||
.close-icon {
|
||||
color: v-bind('config.getColorVal("headerBarTabColor")') !important;
|
||||
}
|
||||
|
||||
.ba-nav-tab.active {
|
||||
.close-icon {
|
||||
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
margin-right: var(--ba-main-space);
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
//
|
||||
//&::-webkit-scrollbar-thumb {
|
||||
// background: #eaeaea;
|
||||
// border-radius: var(--el-border-radius-base);
|
||||
// box-shadow: none;
|
||||
// -webkit-box-shadow: none;
|
||||
//}
|
||||
//
|
||||
//&::-webkit-scrollbar-track {
|
||||
// background: v-bind('config.layout.layoutMode == "Default" ? "none":config.getColorVal("headerBarBackground")');
|
||||
//}
|
||||
//
|
||||
//&:hover {
|
||||
// &::-webkit-scrollbar-thumb:hover {
|
||||
// background: #c8c9cc;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
.ba-nav-tab {
|
||||
white-space: nowrap;
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="nav-tabs" ref="tabScrollbarRef">
|
||||
|
||||
<div
|
||||
v-for="(item, idx) in navTabs.state.tabsView"
|
||||
@click="onTab(item)"
|
||||
@contextmenu.prevent="onContextmenu(item, $event)"
|
||||
class="ba-nav-tab"
|
||||
:class="navTabs.state.activeIndex == idx ? 'active' : ''"
|
||||
:ref="tabsRefs.set"
|
||||
:key="idx"
|
||||
>
|
||||
{{ item.meta.title }}
|
||||
<transition @after-leave="selectNavTab(tabsRefs[navTabs.state.activeIndex])" name="el-fade-in">
|
||||
<Icon
|
||||
v-if="navTabs.state.tabsView.length > 1"
|
||||
class="close-icon"
|
||||
@click.stop="closeTab(item)"
|
||||
size="15"
|
||||
name="el-icon-Close"
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
<!-- <div :style='activeBoxStyle' class='nav-tabs-active-box'></div>-->
|
||||
</div>
|
||||
<Contextmenu ref="contextmenuRef" :items="state.contextmenuItems" @contextmenuItemClick="onContextmenuItem" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import { useRoute, useRouter, onBeforeRouteUpdate, type RouteLocationNormalized } from 'vue-router'
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
import { useTemplateRefsList } from '@vueuse/core'
|
||||
import type { ContextMenuItem, ContextmenuItemClickEmitArg } from '@/components/contextmenu/interface'
|
||||
import useCurrentInstance from '@/utils/useCurrentInstance'
|
||||
import Contextmenu from '@/components/contextmenu/index.vue'
|
||||
import horizontalScroll from '@/utils/horizontalScroll'
|
||||
import { getFirstRoute, routePush } from '@/utils/router'
|
||||
import { adminBaseRoutePath } from '@/router/static'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const config = useConfig()
|
||||
const navTabs = useNavTabs()
|
||||
|
||||
const { proxy } = useCurrentInstance()
|
||||
const tabScrollbarRef = ref()
|
||||
const tabsRefs = useTemplateRefsList<HTMLDivElement>()
|
||||
|
||||
const contextmenuRef = ref()
|
||||
|
||||
const state: {
|
||||
contextmenuItems: ContextMenuItem[]
|
||||
} = reactive({
|
||||
contextmenuItems: [
|
||||
{ name: 'refresh', label: '重新加载', icon: 'fa fa-refresh' },
|
||||
{ name: 'close', label: '关闭标签', icon: 'fa fa-times' },
|
||||
{ name: 'fullScreen', label: '当前标签全屏', icon: 'el-icon-FullScreen' },
|
||||
{ name: 'closeOther', label: '关闭其他标签', icon: 'fa fa-minus' },
|
||||
{ name: 'closeAll', label: '关闭全部标签', icon: 'fa fa-stop' }
|
||||
]
|
||||
})
|
||||
|
||||
const activeBoxStyle = reactive({
|
||||
width: '0',
|
||||
transform: 'translateX(0px)'
|
||||
})
|
||||
|
||||
const onTab = (menu: RouteLocationNormalized) => {
|
||||
router.push(menu)
|
||||
}
|
||||
|
||||
const onContextmenu = (menu: RouteLocationNormalized, el: MouseEvent) => {
|
||||
|
||||
// 禁用刷新
|
||||
state.contextmenuItems[0].disabled = route.path !== menu.path
|
||||
|
||||
|
||||
// 禁用关闭其他和关闭全部
|
||||
state.contextmenuItems[4].disabled = state.contextmenuItems[3].disabled =
|
||||
navTabs.state.tabsView.length == 1 ? true : false
|
||||
|
||||
const { clientX, clientY } = el
|
||||
contextmenuRef.value.onShowContextmenu(menu, {
|
||||
x: clientX,
|
||||
y: clientY
|
||||
})
|
||||
}
|
||||
|
||||
// tab 激活状态切换
|
||||
const selectNavTab = function (dom: HTMLDivElement) {
|
||||
if (!dom) {
|
||||
return false
|
||||
}
|
||||
activeBoxStyle.width = dom.clientWidth + 'px'
|
||||
activeBoxStyle.transform = `translateX(${dom.offsetLeft}px)`
|
||||
|
||||
let scrollLeft = dom.offsetLeft + dom.clientWidth - tabScrollbarRef.value.clientWidth
|
||||
if (dom.offsetLeft < tabScrollbarRef.value.scrollLeft) {
|
||||
tabScrollbarRef.value.scrollTo(dom.offsetLeft, 0)
|
||||
} else if (scrollLeft > tabScrollbarRef.value.scrollLeft) {
|
||||
tabScrollbarRef.value.scrollTo(scrollLeft, 0)
|
||||
}
|
||||
}
|
||||
|
||||
const toLastTab = () => {
|
||||
const lastTab = navTabs.state.tabsView.slice(-1)[0]
|
||||
if (lastTab) {
|
||||
router.push(lastTab)
|
||||
} else {
|
||||
router.push(adminBaseRoutePath)
|
||||
}
|
||||
}
|
||||
|
||||
const closeTab = (route: RouteLocationNormalized) => {
|
||||
navTabs.closeTab(route)
|
||||
proxy.eventBus.emit('onTabViewClose', route)
|
||||
if (navTabs.state.activeRoute?.path === route.path) {
|
||||
toLastTab()
|
||||
} else {
|
||||
navTabs.setActiveRoute(navTabs.state.activeRoute!)
|
||||
nextTick(() => {
|
||||
selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
|
||||
})
|
||||
}
|
||||
|
||||
contextmenuRef.value.onHideContextmenu()
|
||||
}
|
||||
|
||||
const closeOtherTab = (menu: RouteLocationNormalized) => {
|
||||
navTabs.closeTabs(menu)
|
||||
navTabs.setActiveRoute(menu)
|
||||
if (navTabs.state.activeRoute?.path !== route.path) {
|
||||
router.push(menu!.path)
|
||||
}
|
||||
}
|
||||
|
||||
const closeAllTab = (menu: RouteLocationNormalized) => {
|
||||
let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
|
||||
if (firstRoute && firstRoute.path == menu.path) {
|
||||
return closeOtherTab(menu)
|
||||
}
|
||||
if (firstRoute && firstRoute.path == navTabs.state.activeRoute?.path) {
|
||||
return closeOtherTab(navTabs.state.activeRoute)
|
||||
}
|
||||
navTabs.closeTabs(false)
|
||||
if (firstRoute) routePush(firstRoute.path)
|
||||
}
|
||||
|
||||
const onContextmenuItem = async (item: ContextmenuItemClickEmitArg) => {
|
||||
const { name, menu } = item
|
||||
if (!menu) return
|
||||
switch (name) {
|
||||
case 'refresh':
|
||||
proxy.eventBus.emit('onTabViewRefresh', menu)
|
||||
break
|
||||
case 'close':
|
||||
closeTab(menu)
|
||||
break
|
||||
case 'closeOther':
|
||||
closeOtherTab(menu)
|
||||
break
|
||||
case 'closeAll':
|
||||
closeAllTab(menu)
|
||||
break
|
||||
case 'fullScreen':
|
||||
if (route.path !== menu?.path) {
|
||||
router.push(menu?.path as string)
|
||||
}
|
||||
navTabs.setFullScreen(true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const updateTab = function (newRoute: RouteLocationNormalized) {
|
||||
// 添加tab
|
||||
navTabs.addTab(newRoute)
|
||||
// 激活当前tab
|
||||
navTabs.setActiveRoute(newRoute)
|
||||
|
||||
nextTick(() => {
|
||||
selectNavTab(tabsRefs.value[navTabs.state.activeIndex])
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate(async to => {
|
||||
|
||||
|
||||
updateTab(to)
|
||||
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
updateTab(router.currentRoute.value)
|
||||
new horizontalScroll(tabScrollbarRef.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dark {
|
||||
.close-icon {
|
||||
color: v-bind('config.getColorVal("headerBarTabColor")') !important;
|
||||
}
|
||||
|
||||
.ba-nav-tab.active {
|
||||
.close-icon {
|
||||
color: v-bind('config.getColorVal("headerBarTabActiveColor")') !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
margin-right: var(--ba-main-space);
|
||||
// scrollbar-width: none;
|
||||
// overflow-x: auto;
|
||||
&::-webkit-scrollbar {
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
//
|
||||
//&::-webkit-scrollbar-thumb {
|
||||
// background: #eaeaea;
|
||||
// border-radius: var(--el-border-radius-base);
|
||||
// box-shadow: none;
|
||||
// -webkit-box-shadow: none;
|
||||
//}
|
||||
//
|
||||
//&::-webkit-scrollbar-track {
|
||||
// background: v-bind('config.layout.layoutMode == "Default" ? "none":config.getColorVal("headerBarBackground")');
|
||||
//}
|
||||
//
|
||||
//&:hover {
|
||||
// &::-webkit-scrollbar-thumb:hover {
|
||||
// background: #c8c9cc;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
.ba-nav-tab {
|
||||
white-space: nowrap;
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,238 +1,241 @@
|
||||
<template>
|
||||
<div class="nav-menus" :class="configStore.layout.layoutMode">
|
||||
<div @click="savePng" class="nav-menu-item">
|
||||
<Icon
|
||||
:color="configStore.getColorVal('headerBarTabColor')"
|
||||
class="nav-menu-icon"
|
||||
name="el-icon-Camera"
|
||||
size="18"
|
||||
/>
|
||||
</div>
|
||||
<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-dropdown style="height: 100%" @command="handleCommand">
|
||||
<div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''">
|
||||
<el-avatar :size="25" fit="fill">
|
||||
<img src="@/assets/avatar.png" alt="" />
|
||||
</el-avatar>
|
||||
<div class="admin-name">{{ adminInfo.nickname }}</div>
|
||||
</div>
|
||||
<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-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 /> -->
|
||||
</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 { routePush } from '@/utils/router'
|
||||
import { fullUrl } from '@/utils/common'
|
||||
import html2canvas from 'html2canvas'
|
||||
import PopupPwd from './popup/password.vue'
|
||||
import AdminInfo from './popup/adminInfo.vue'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
|
||||
|
||||
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 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
|
||||
}
|
||||
screenfull.toggle()
|
||||
screenfull.onchange(() => {
|
||||
state.isFullScreen = screenfull.isFullscreen
|
||||
})
|
||||
}
|
||||
|
||||
const handleCommand = (key: string) => {
|
||||
// console.log(key)
|
||||
switch (key) {
|
||||
case 'adminInfo':
|
||||
popupAdminInfo.value.open()
|
||||
break
|
||||
case 'changePwd':
|
||||
popupPwd.value.open()
|
||||
break
|
||||
case 'layout':
|
||||
navTabs.closeTabs()
|
||||
window.localStorage.clear()
|
||||
adminInfo.reset()
|
||||
router.push({ name: 'login' })
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nav-menus.Default {
|
||||
border-radius: var(--el-border-radius-base);
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
}
|
||||
|
||||
.nav-menus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
margin-left: auto;
|
||||
background-color: v-bind('configStore.getColorVal("headerBarBackground")');
|
||||
|
||||
.nav-menu-item {
|
||||
height: 100%;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
.nav-menu-icon {
|
||||
box-sizing: content-box;
|
||||
color: v-bind('configStore.getColorVal("headerBarTabColor")');
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.icon {
|
||||
animation: twinkle 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-info {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 0 10px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: v-bind('configStore.getColorVal("headerBarTabColor")');
|
||||
|
||||
&:hover {
|
||||
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")');
|
||||
}
|
||||
}
|
||||
|
||||
.admin-name {
|
||||
padding-left: 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-menu-item:hover,
|
||||
.admin-info:hover,
|
||||
.nav-menu-item.hover,
|
||||
.admin-info.hover {
|
||||
background: v-bind('configStore.getColorVal("headerBarHoverBackground")');
|
||||
|
||||
.nav-menu-icon {
|
||||
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")') !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@keyframes twinkle {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="nav-menus" :class="configStore.layout.layoutMode">
|
||||
<!-- <div @click="savePng" class="nav-menu-item">
|
||||
<Icon
|
||||
:color="configStore.getColorVal('headerBarTabColor')"
|
||||
class="nav-menu-icon"
|
||||
name="el-icon-Camera"
|
||||
size="18"
|
||||
/>
|
||||
</div>
|
||||
<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-dropdown style="height: 100%" @command="handleCommand">
|
||||
<div class="admin-info" :class="state.currentNavMenu == 'adminInfo' ? 'hover' : ''">
|
||||
<el-avatar :size="25" fit="fill">
|
||||
<img src="@/assets/avatar.png" alt="" />
|
||||
</el-avatar>
|
||||
<div class="admin-name">{{ adminInfo.nickname }}</div>
|
||||
</div>
|
||||
<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-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 /> -->
|
||||
</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 { routePush } from '@/utils/router'
|
||||
import { fullUrl } from '@/utils/common'
|
||||
import html2canvas from 'html2canvas'
|
||||
import PopupPwd from './popup/password.vue'
|
||||
import AdminInfo from './popup/adminInfo.vue'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
|
||||
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 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
|
||||
}
|
||||
screenfull.toggle()
|
||||
screenfull.onchange(() => {
|
||||
state.isFullScreen = screenfull.isFullscreen
|
||||
})
|
||||
}
|
||||
|
||||
const handleCommand = async (key: string) => {
|
||||
// console.log(key)
|
||||
switch (key) {
|
||||
case 'adminInfo':
|
||||
popupAdminInfo.value.open()
|
||||
break
|
||||
case 'changePwd':
|
||||
popupPwd.value.open()
|
||||
break
|
||||
case 'layout':
|
||||
await window.location.reload()
|
||||
setTimeout(() => {
|
||||
navTabs.closeTabs()
|
||||
window.localStorage.clear()
|
||||
adminInfo.reset()
|
||||
router.push({ name: 'login' })
|
||||
}, 0)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nav-menus.Default {
|
||||
border-radius: var(--el-border-radius-base);
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
}
|
||||
|
||||
.nav-menus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
margin-left: auto;
|
||||
background-color: v-bind('configStore.getColorVal("headerBarBackground")');
|
||||
|
||||
.nav-menu-item {
|
||||
height: 100%;
|
||||
width: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
.nav-menu-icon {
|
||||
box-sizing: content-box;
|
||||
color: v-bind('configStore.getColorVal("headerBarTabColor")');
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.icon {
|
||||
animation: twinkle 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-info {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 0 10px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: v-bind('configStore.getColorVal("headerBarTabColor")');
|
||||
|
||||
&:hover {
|
||||
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")');
|
||||
}
|
||||
}
|
||||
|
||||
.admin-name {
|
||||
padding-left: 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-menu-item:hover,
|
||||
.admin-info:hover,
|
||||
.nav-menu-item.hover,
|
||||
.admin-info.hover {
|
||||
background: v-bind('configStore.getColorVal("headerBarHoverBackground")');
|
||||
|
||||
.nav-menu-icon {
|
||||
color: v-bind('configStore.getColorVal("headerBarTabActiveColor")') !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@keyframes twinkle {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,109 +1,123 @@
|
||||
<template>
|
||||
<el-dialog draggable width="500px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-scrollbar>
|
||||
<el-form :inline="false" :model="form" label-width="120px" :rules="rules" class="form-one" ref="formRef">
|
||||
<el-form-item label="校验密码:" prop="password">
|
||||
<el-input v-model.trim="form.password" type="password" placeholder="请输入校验密码" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码:" prop="newPwd">
|
||||
<el-input v-model.trim="form.newPwd" type="password" placeholder="请输入新密码" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码:" prop="confirmPwd">
|
||||
<el-input v-model.trim="form.confirmPwd" type="password" placeholder="请输入确认密码" show-password />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { passwordConfirm, updatePassword } from '@/api/user-boot/user'
|
||||
import { validatePwd } from '@/utils/common'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
|
||||
const adminInfo = useAdminInfo()
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('修改密码')
|
||||
const formRef = ref()
|
||||
// 注意不要和表单ref的命名冲突
|
||||
const form = reactive({
|
||||
password: '',
|
||||
newPwd: '',
|
||||
confirmPwd: ''
|
||||
})
|
||||
const rules = {
|
||||
password: [
|
||||
{ required: true, message: '请输入校验密码', trigger: 'blur' },
|
||||
{
|
||||
min: 6,
|
||||
max: 16,
|
||||
message: '长度在 6 到 16 个字符',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
newPwd: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{
|
||||
min: 6,
|
||||
max: 16,
|
||||
message: '长度在 6 到 16 个字符',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{ validator: validatePwd, trigger: 'blur' }
|
||||
],
|
||||
confirmPwd: [
|
||||
{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||
{
|
||||
min: 6,
|
||||
max: 16,
|
||||
message: '长度在 6 到 16 个字符',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
} else if (value !== form.newPwd) {
|
||||
callback(new Error('两次输入密码不一致!'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur',
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
dialogVisible.value = true
|
||||
form.password = ''
|
||||
form.newPwd = ''
|
||||
form.confirmPwd = ''
|
||||
}
|
||||
const submit = () => {
|
||||
formRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
passwordConfirm(form.password).then(res => {
|
||||
updatePassword({
|
||||
id: adminInfo.$state.userIndex,
|
||||
newPassword: form.newPwd
|
||||
}).then((res: any) => {
|
||||
ElMessage.success('密码修改成功')
|
||||
dialogVisible.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<template>
|
||||
<el-dialog draggable width="500px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-scrollbar>
|
||||
<el-form :inline="false" :model="form" label-width="120px" :rules="rules" class="form-one" ref="formRef">
|
||||
<el-form-item label="校验密码:" prop="password">
|
||||
<el-input v-model.trim="form.password" type="password" placeholder="请输入校验密码" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码:" prop="newPwd">
|
||||
<el-input v-model.trim="form.newPwd" type="password" placeholder="请输入新密码" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码:" prop="confirmPwd">
|
||||
<el-input
|
||||
v-model.trim="form.confirmPwd"
|
||||
type="password"
|
||||
placeholder="请输入确认密码"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { passwordConfirm, updatePassword } from '@/api/user-boot/user'
|
||||
import { validatePwd } from '@/utils/common'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
import router from '@/router'
|
||||
import { useNavTabs } from '@/stores/navTabs'
|
||||
const adminInfo = useAdminInfo()
|
||||
const navTabs = useNavTabs()
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('修改密码')
|
||||
const formRef = ref()
|
||||
// 注意不要和表单ref的命名冲突
|
||||
const form = reactive({
|
||||
password: '',
|
||||
newPwd: '',
|
||||
confirmPwd: ''
|
||||
})
|
||||
const rules = {
|
||||
password: [
|
||||
{ required: true, message: '请输入校验密码', trigger: 'blur' },
|
||||
{
|
||||
min: 6,
|
||||
max: 16,
|
||||
message: '长度在 6 到 16 个字符',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
newPwd: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{
|
||||
min: 6,
|
||||
max: 16,
|
||||
message: '长度在 6 到 16 个字符',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{ validator: validatePwd, trigger: 'blur' }
|
||||
],
|
||||
confirmPwd: [
|
||||
{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||
{
|
||||
min: 6,
|
||||
max: 16,
|
||||
message: '长度在 6 到 16 个字符',
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
if (value === '') {
|
||||
callback(new Error('请再次输入密码'))
|
||||
} else if (value !== form.newPwd) {
|
||||
callback(new Error('两次输入密码不一致!'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur',
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
dialogVisible.value = true
|
||||
form.password = ''
|
||||
form.newPwd = ''
|
||||
form.confirmPwd = ''
|
||||
}
|
||||
const submit = () => {
|
||||
formRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
passwordConfirm(form.password).then(res => {
|
||||
updatePassword({
|
||||
id: adminInfo.$state.userIndex,
|
||||
newPassword: form.newPwd
|
||||
}).then(async (res: any) => {
|
||||
ElMessage.success('密码修改成功')
|
||||
dialogVisible.value = false
|
||||
|
||||
setTimeout(() => {
|
||||
navTabs.closeTabs()
|
||||
window.localStorage.clear()
|
||||
adminInfo.reset()
|
||||
router.push({ name: 'login' })
|
||||
}, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import staticRoutes from '@/router/static'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
import NProgress from 'nprogress'
|
||||
import { loading } from '@/utils/loading'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: staticRoutes
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.configure({ showSpinner: false })
|
||||
NProgress.start()
|
||||
if (!window.existLoading) {
|
||||
loading.show()
|
||||
window.existLoading = true
|
||||
}
|
||||
if (to.path == '/login' || to.path == '/404'|| to.path == '/policy'|| to.path == '/agreement') {
|
||||
// 登录或者注册才可以往下进行
|
||||
next()
|
||||
} else {
|
||||
// 获取 token
|
||||
const adminInfo = useAdminInfo()
|
||||
const token = adminInfo.getToken()
|
||||
// token 不存在
|
||||
if (token === null || token === '') {
|
||||
ElMessage.error('您还没有登录,请先登录')
|
||||
next('/login')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
// next()
|
||||
})
|
||||
|
||||
// 路由加载后
|
||||
router.afterEach(() => {
|
||||
if (window.existLoading) {
|
||||
loading.hide()
|
||||
}
|
||||
NProgress.done()
|
||||
})
|
||||
|
||||
export default router
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import staticRoutes from '@/router/static'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
import NProgress from 'nprogress'
|
||||
import { loading } from '@/utils/loading'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: staticRoutes
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.configure({ showSpinner: false })
|
||||
NProgress.start()
|
||||
if (!window.existLoading) {
|
||||
loading.show()
|
||||
window.existLoading = true
|
||||
}
|
||||
if (to.path == '/login' || to.path == '/404'|| to.path == '/policy'|| to.path == '/agreement') {
|
||||
// 登录或者注册才可以往下进行
|
||||
next()
|
||||
} else {
|
||||
// 获取 token
|
||||
const adminInfo = useAdminInfo()
|
||||
const token = adminInfo.getToken()
|
||||
// token 不存在
|
||||
if (token === null || token === '') {
|
||||
// ElMessage.error('您还没有登录,请先登录')
|
||||
next('/login')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
// next()
|
||||
})
|
||||
|
||||
// 路由加载后
|
||||
router.afterEach(() => {
|
||||
if (window.existLoading) {
|
||||
loading.hide()
|
||||
}
|
||||
NProgress.done()
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
@@ -1,108 +1,168 @@
|
||||
import { reactive } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { STORE_TAB_VIEW_CONFIG } from '@/stores/constant/cacheKey'
|
||||
import type { NavTabs } from '@/stores/interface/index'
|
||||
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
||||
import { adminBaseRoutePath } from '@/router/static'
|
||||
|
||||
export const useNavTabs = defineStore(
|
||||
'navTabs',
|
||||
() => {
|
||||
const state: NavTabs = reactive({
|
||||
// 激活tab的index
|
||||
activeIndex: 0,
|
||||
// 激活的tab
|
||||
activeRoute: null,
|
||||
// tab列表
|
||||
tabsView: [],
|
||||
// 当前tab是否全屏
|
||||
tabFullScreen: false,
|
||||
// 从后台加载到的菜单路由列表
|
||||
tabsViewRoutes: [],
|
||||
// 按钮权限节点
|
||||
authNode: new Map(),
|
||||
})
|
||||
|
||||
function addTab(route: RouteLocationNormalized) {
|
||||
if (!route.meta.addtab) return
|
||||
for (const key in state.tabsView) {
|
||||
if (state.tabsView[key].path === route.path) {
|
||||
state.tabsView[key].params = route.params ? route.params : state.tabsView[key].params
|
||||
state.tabsView[key].query = route.query ? route.query : state.tabsView[key].query
|
||||
return
|
||||
}
|
||||
}
|
||||
state.tabsView.push(route)
|
||||
}
|
||||
|
||||
function closeTab(route: RouteLocationNormalized) {
|
||||
state.tabsView.map((v, k) => {
|
||||
if (v.path == route.path) {
|
||||
state.tabsView.splice(k, 1)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭多个标签
|
||||
* @param retainMenu 需要保留的标签,否则关闭全部标签
|
||||
*/
|
||||
const closeTabs = (retainMenu: RouteLocationNormalized | false = false) => {
|
||||
if (retainMenu) {
|
||||
state.tabsView = [retainMenu]
|
||||
} else {
|
||||
state.tabsView = []
|
||||
}
|
||||
}
|
||||
|
||||
const setActiveRoute = (route: RouteLocationNormalized): void => {
|
||||
const currentRouteIndex: number = state.tabsView.findIndex((item: RouteLocationNormalized) => {
|
||||
return item.path === route.path
|
||||
})
|
||||
if (currentRouteIndex === -1) return
|
||||
state.activeRoute = route
|
||||
state.activeIndex = currentRouteIndex
|
||||
}
|
||||
|
||||
const setTabsViewRoutes = (data: RouteRecordRaw[]): void => {
|
||||
state.tabsViewRoutes = encodeRoutesURI(data)
|
||||
}
|
||||
|
||||
const setAuthNode = (key: string, data: string[]) => {
|
||||
state.authNode.set(key, data)
|
||||
}
|
||||
|
||||
const fillAuthNode = (data: Map<string, string[]>) => {
|
||||
state.authNode = data
|
||||
}
|
||||
|
||||
const setFullScreen = (fullScreen: boolean): void => {
|
||||
state.tabFullScreen = fullScreen
|
||||
}
|
||||
|
||||
return { state, addTab, closeTab, closeTabs, setActiveRoute, setTabsViewRoutes, setAuthNode, fillAuthNode, setFullScreen }
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
key: STORE_TAB_VIEW_CONFIG,
|
||||
paths: ['state.tabFullScreen'],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 对iframe的url进行编码
|
||||
*/
|
||||
function encodeRoutesURI(data: RouteRecordRaw[]) {
|
||||
data.forEach((item) => {
|
||||
if (item.meta?.menu_type == 'iframe') {
|
||||
item.path = adminBaseRoutePath + '/iframe/' + encodeURIComponent(item.path)
|
||||
}
|
||||
|
||||
if (item.children && item.children.length) {
|
||||
item.children = encodeRoutesURI(item.children)
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
import { reactive } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { STORE_TAB_VIEW_CONFIG } from '@/stores/constant/cacheKey'
|
||||
import type { NavTabs } from '@/stores/interface/index'
|
||||
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
||||
import { adminBaseRoutePath } from '@/router/static'
|
||||
import { set } from 'lodash'
|
||||
|
||||
export const useNavTabs = defineStore(
|
||||
'navTabs',
|
||||
() => {
|
||||
const state: NavTabs = reactive({
|
||||
// 激活tab的index
|
||||
activeIndex: 0,
|
||||
// 激活的tab
|
||||
activeRoute: null,
|
||||
// tab列表
|
||||
tabsView: [],
|
||||
// 当前tab是否全屏
|
||||
tabFullScreen: false,
|
||||
// 从后台加载到的菜单路由列表
|
||||
tabsViewRoutes: [],
|
||||
// 按钮权限节点
|
||||
authNode: new Map()
|
||||
})
|
||||
|
||||
function addTab(route: RouteLocationNormalized) {
|
||||
if (!route.meta.addtab) return
|
||||
for (const key in state.tabsView) {
|
||||
if (state.tabsView[key].path === route.path) {
|
||||
state.tabsView[key].params = route.params ? route.params : state.tabsView[key].params
|
||||
state.tabsView[key].query = route.query ? route.query : state.tabsView[key].query
|
||||
state.tabsView[key].meta = route.query ? route.meta : state.tabsView[key].meta
|
||||
return
|
||||
}
|
||||
}
|
||||
state.tabsView.push(route)
|
||||
}
|
||||
|
||||
function closeTab(route: RouteLocationNormalized) {
|
||||
state.tabsView.map((v, k) => {
|
||||
if (v.path == route.path) {
|
||||
state.tabsView.splice(k, 1)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭多个标签
|
||||
* @param retainMenu 需要保留的标签,否则关闭全部标签
|
||||
*/
|
||||
const closeTabs = (retainMenu: RouteLocationNormalized | false = false) => {
|
||||
if (retainMenu) {
|
||||
state.tabsView = [retainMenu]
|
||||
} else {
|
||||
state.tabsView = []
|
||||
}
|
||||
}
|
||||
|
||||
const setActiveRoute = (route: RouteLocationNormalized): void => {
|
||||
const currentRouteIndex: number = state.tabsView.findIndex((item: RouteLocationNormalized) => {
|
||||
return item.path === route.path
|
||||
})
|
||||
if (currentRouteIndex === -1) return
|
||||
state.activeRoute = route
|
||||
state.activeIndex = currentRouteIndex
|
||||
}
|
||||
|
||||
const setTabsViewRoutes = (data: RouteRecordRaw[]): void => {
|
||||
state.tabsViewRoutes = encodeRoutesURI(JSON.parse(JSON.stringify(data)))
|
||||
}
|
||||
|
||||
const setAuthNode = (key: string, data: string[]) => {
|
||||
state.authNode.set(key, data)
|
||||
}
|
||||
|
||||
const fillAuthNode = (data: Map<string, string[]>) => {
|
||||
state.authNode = data
|
||||
}
|
||||
|
||||
const setFullScreen = (fullScreen: boolean): void => {
|
||||
state.tabFullScreen = fullScreen
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
// setTimeout(() => {
|
||||
// console.log(123, state.tabsViewRoutes)
|
||||
|
||||
let list = matchAndReturnRouteData(state.tabsViewRoutes, state.tabsView)
|
||||
state.tabsView = []
|
||||
list.forEach(item => {
|
||||
addTab(item)
|
||||
})
|
||||
// }, 1000)
|
||||
}
|
||||
return {
|
||||
state,
|
||||
addTab,
|
||||
closeTab,
|
||||
closeTabs,
|
||||
setActiveRoute,
|
||||
setTabsViewRoutes,
|
||||
setAuthNode,
|
||||
fillAuthNode,
|
||||
setFullScreen,
|
||||
refresh
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
key: STORE_TAB_VIEW_CONFIG,
|
||||
paths: ['state.tabFullScreen']
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 核心逻辑:
|
||||
* 1. 递归遍历树形菜单,筛选出与routeList中name匹配的节点
|
||||
* 2. 将匹配到的节点格式转换为routeList的结构并返回
|
||||
*/
|
||||
function matchAndReturnRouteData(tree, routeList) {
|
||||
// 1. 构建路由name映射(name -> 完整路由对象)
|
||||
const routeMap = new Map()
|
||||
routeList.forEach(route => {
|
||||
if (route.name) {
|
||||
routeMap.set(route.name, route)
|
||||
}
|
||||
})
|
||||
|
||||
// 2. 递归遍历树形菜单,收集匹配的节点
|
||||
const matchedNodes = []
|
||||
function recursion(node) {
|
||||
// 匹配当前节点
|
||||
if (routeMap.has(node.name)) {
|
||||
// 深度克隆路由对象,避免修改原数据
|
||||
const matchedRoute = JSON.parse(JSON.stringify(routeMap.get(node.name)))
|
||||
matchedNodes.push(matchedRoute)
|
||||
}
|
||||
// 递归处理子节点
|
||||
if (node.children && node.children.length) {
|
||||
node.children.forEach(child => recursion(child))
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历所有顶级节点
|
||||
tree.forEach(node => recursion(node))
|
||||
|
||||
// 3. 返回匹配后的第二个数据格式(和routeList结构一致)
|
||||
return matchedNodes
|
||||
}
|
||||
|
||||
/**
|
||||
* 对iframe的url进行编码
|
||||
*/
|
||||
function encodeRoutesURI(data: RouteRecordRaw[]) {
|
||||
data.forEach(item => {
|
||||
if (item.meta?.menu_type == 'iframe') {
|
||||
item.path = adminBaseRoutePath + '/iframe/' + encodeURIComponent(item.path)
|
||||
}
|
||||
|
||||
if (item.children && item.children.length) {
|
||||
item.children = encodeRoutesURI(item.children)
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
58
src/styles/vxeTable.css
Normal file
58
src/styles/vxeTable.css
Normal file
@@ -0,0 +1,58 @@
|
||||
.vxe-header--row {
|
||||
background: var(--vxe-table-header-background-color);
|
||||
color: var(--vxe-table-header-font-color);
|
||||
}
|
||||
|
||||
.is--checked.vxe-checkbox,
|
||||
.is--checked.vxe-checkbox .vxe-checkbox--icon,
|
||||
.is--checked.vxe-custom--checkbox-option,
|
||||
.is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon,
|
||||
.is--checked.vxe-export--panel-column-option,
|
||||
.is--checked.vxe-export--panel-column-option .vxe-checkbox--icon,
|
||||
.is--checked.vxe-table--filter-option,
|
||||
.is--checked.vxe-table--filter-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-checkbox,
|
||||
.is--indeterminate.vxe-checkbox .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-custom--checkbox-option,
|
||||
.is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-export--panel-column-option,
|
||||
.is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-table--filter-option,
|
||||
.is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--checkbox,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,
|
||||
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon {
|
||||
color: var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
.vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-radio:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon {
|
||||
color: var(--el-color-primary-light-5);
|
||||
}
|
||||
|
||||
.vxe-table--render-default .vxe-body--row.row--current,
|
||||
.vxe-table--render-default .vxe-body--row.row--current:hover {
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
}
|
||||
|
||||
.vxe-table--tooltip-wrapper {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
|
||||
.is--disabled {
|
||||
background-color: var(--vxe-input-disabled-color);
|
||||
}
|
||||
|
||||
.vxe-modal--wrapper {
|
||||
z-index: 5000 !important;
|
||||
}
|
||||
1
src/styles/vxeTable.min.css
vendored
Normal file
1
src/styles/vxeTable.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vxe-header--row{background:var(--vxe-table-header-background-color);color:var(--vxe-table-header-font-color)}.is--checked.vxe-checkbox,.is--checked.vxe-checkbox .vxe-checkbox--icon,.is--checked.vxe-custom--checkbox-option,.is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon,.is--checked.vxe-export--panel-column-option,.is--checked.vxe-export--panel-column-option .vxe-checkbox--icon,.is--checked.vxe-table--filter-option,.is--checked.vxe-table--filter-option .vxe-checkbox--icon,.is--indeterminate.vxe-checkbox,.is--indeterminate.vxe-checkbox .vxe-checkbox--icon,.is--indeterminate.vxe-custom--checkbox-option,.is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon,.is--indeterminate.vxe-export--panel-column-option,.is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon,.is--indeterminate.vxe-table--filter-option,.is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon,.vxe-table--render-default .is--checked.vxe-cell--checkbox,.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon{color:var(--el-color-primary-light-3)}.vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,.vxe-radio:not(.is--disabled):hover .vxe-radio--icon,.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon{color:var(--el-color-primary-light-5)}.vxe-table--render-default .vxe-body--row.row--current,.vxe-table--render-default .vxe-body--row.row--current:hover{background-color:var(--el-color-primary-light-8)}.vxe-table--tooltip-wrapper{z-index:10000 !important}.is--disabled{background-color:var(--vxe-input-disabled-color)}.vxe-modal--wrapper{z-index:5000 !important}
|
||||
@@ -1,84 +1,87 @@
|
||||
// .vxe-table--body-wrapper,
|
||||
// .boxbx {
|
||||
// &::-webkit-scrollbar {
|
||||
// width: 10px;
|
||||
// height: 10px;
|
||||
// }
|
||||
// &::-webkit-scrollbar-thumb {
|
||||
// border-radius: 5px;
|
||||
// height: 3px;
|
||||
// background-color: var(--el-color-primary) !important;
|
||||
// border-radius: 30px !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .vxe-table--body-wrapper::-webkit-scrollbar-thumb {
|
||||
// border-radius: 5px;
|
||||
// height: 3px;
|
||||
// background-color: var(--el-color-primary) !important;
|
||||
// border-radius: 30px !important;
|
||||
// }
|
||||
|
||||
.vxe-header--row {
|
||||
background: var(--vxe-table-header-background-color);
|
||||
color: var(--vxe-table-header-font-color);
|
||||
}
|
||||
|
||||
.is--checked.vxe-checkbox,
|
||||
.is--checked.vxe-checkbox .vxe-checkbox--icon,
|
||||
.is--checked.vxe-custom--checkbox-option,
|
||||
.is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon,
|
||||
.is--checked.vxe-export--panel-column-option,
|
||||
.is--checked.vxe-export--panel-column-option .vxe-checkbox--icon,
|
||||
.is--checked.vxe-table--filter-option,
|
||||
.is--checked.vxe-table--filter-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-checkbox,
|
||||
.is--indeterminate.vxe-checkbox .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-custom--checkbox-option,
|
||||
.is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-export--panel-column-option,
|
||||
.is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-table--filter-option,
|
||||
.is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--checkbox,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,
|
||||
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon {
|
||||
color: var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
.vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-radio:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon {
|
||||
color: var(--el-color-primary-light-5);
|
||||
}
|
||||
|
||||
// .vxe-table--render-default .is--disabled.vxe-cell--checkbox .vxe-checkbox--icon{
|
||||
// color: #fff0;
|
||||
// }
|
||||
.vxe-table--tooltip-wrapper {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
.is--disabled {
|
||||
background-color: var(--vxe-input-disabled-color);
|
||||
}
|
||||
|
||||
.vxe-modal--wrapper {
|
||||
z-index: 5000 !important;
|
||||
}
|
||||
.vxe-table--body .vxe-body--row:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
// background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.vxe-table--body .vxe-body--row:nth-child(odd) {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
// .vxe-table--body-wrapper,
|
||||
// .boxbx {
|
||||
// &::-webkit-scrollbar {
|
||||
// width: 10px;
|
||||
// height: 10px;
|
||||
// }
|
||||
// &::-webkit-scrollbar-thumb {
|
||||
// border-radius: 5px;
|
||||
// height: 3px;
|
||||
// background-color: var(--el-color-primary) !important;
|
||||
// border-radius: 30px !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .vxe-table--body-wrapper::-webkit-scrollbar-thumb {
|
||||
// border-radius: 5px;
|
||||
// height: 3px;
|
||||
// background-color: var(--el-color-primary) !important;
|
||||
// border-radius: 30px !important;
|
||||
// }
|
||||
|
||||
.vxe-header--row {
|
||||
background: var(--vxe-table-header-background-color);
|
||||
color: var(--vxe-table-header-font-color);
|
||||
}
|
||||
|
||||
.is--checked.vxe-checkbox,
|
||||
.is--checked.vxe-checkbox .vxe-checkbox--icon,
|
||||
.is--checked.vxe-custom--checkbox-option,
|
||||
.is--checked.vxe-custom--checkbox-option .vxe-checkbox--icon,
|
||||
.is--checked.vxe-export--panel-column-option,
|
||||
.is--checked.vxe-export--panel-column-option .vxe-checkbox--icon,
|
||||
.is--checked.vxe-table--filter-option,
|
||||
.is--checked.vxe-table--filter-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-checkbox,
|
||||
.is--indeterminate.vxe-checkbox .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-custom--checkbox-option,
|
||||
.is--indeterminate.vxe-custom--checkbox-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-export--panel-column-option,
|
||||
.is--indeterminate.vxe-export--panel-column-option .vxe-checkbox--icon,
|
||||
.is--indeterminate.vxe-table--filter-option,
|
||||
.is--indeterminate.vxe-table--filter-option .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--checkbox,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--checkbox .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox,
|
||||
.vxe-table--render-default .is--indeterminate.vxe-cell--checkbox .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .is--checked.vxe-cell--radio .vxe-radio--icon {
|
||||
color: var(--el-color-primary-light-3);
|
||||
}
|
||||
|
||||
.vxe-checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-custom--checkbox-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-table--filter-option:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-table--render-default .vxe-cell--checkbox:not(.is--disabled):hover .vxe-checkbox--icon,
|
||||
.vxe-radio:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-custom--radio-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-export--panel-column-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-table--filter-option:not(.is--disabled):hover .vxe-radio--icon,
|
||||
.vxe-table--render-default .vxe-cell--radio:not(.is--disabled):hover .vxe-radio--icon {
|
||||
color: var(--el-color-primary-light-5);
|
||||
}
|
||||
.vxe-table--render-default .vxe-body--row.row--current,
|
||||
.vxe-table--render-default .vxe-body--row.row--current:hover {
|
||||
background-color: var(--el-color-primary-light-8);
|
||||
}
|
||||
// .vxe-table--render-default .is--disabled.vxe-cell--checkbox .vxe-checkbox--icon{
|
||||
// color: #fff0;
|
||||
// }
|
||||
.vxe-table--tooltip-wrapper {
|
||||
z-index: 10000 !important;
|
||||
}
|
||||
.is--disabled {
|
||||
background-color: var(--vxe-input-disabled-color);
|
||||
}
|
||||
|
||||
.vxe-modal--wrapper {
|
||||
z-index: 5000 !important;
|
||||
}
|
||||
// .vxe-table--body .vxe-body--row:nth-child(even) {
|
||||
// background-color: #f9f9f9;
|
||||
// // background-color: var(--el-color-primary-light-9);
|
||||
// }
|
||||
|
||||
// .vxe-table--body .vxe-body--row:nth-child(odd) {
|
||||
// background-color: #ffffff;
|
||||
// }
|
||||
|
||||
@@ -1,79 +1,79 @@
|
||||
<template>
|
||||
<el-dialog width="600px" v-model.trim='dialogVisible' :title='title'>
|
||||
<el-scrollbar>
|
||||
<el-form :inline='false' :model='form' label-width='auto' class="form-one" :rules='rules' ref='formRef'>
|
||||
<el-form-item label='角色名称'>
|
||||
<el-input maxlength="32" show-word-limit v-model.trim='form.name' placeholder='请输入菜单名称' />
|
||||
</el-form-item>
|
||||
<el-form-item label='角色编码'>
|
||||
<el-input maxlength="32" show-word-limit v-model.trim='form.code' placeholder='请输入菜单名称' />
|
||||
</el-form-item>
|
||||
<el-form-item label='角色描述'>
|
||||
<el-input maxlength="300" show-word-limit v-model.trim='form.remark' :rows='2' type='textarea'
|
||||
placeholder='请输入描述' />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<span class='dialog-footer'>
|
||||
<el-button @click='dialogVisible = false'>取消</el-button>
|
||||
<el-button type='primary' @click='submit'>确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang='ts' setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import TableStore from '@/utils/tableStore' // 若不是列表页面弹框可删除
|
||||
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('')
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
const formRef = ref()
|
||||
// 注意不要和表单ref的命名冲突
|
||||
const form = reactive({
|
||||
code: '',
|
||||
name: '',
|
||||
remark: '',
|
||||
id: ''
|
||||
})
|
||||
const rules = {
|
||||
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '角色编码不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const open = (text: string, data?: anyObj) => {
|
||||
title.value = text
|
||||
dialogVisible.value = true
|
||||
if (data) {
|
||||
// 表单赋值
|
||||
for (let key in form) {
|
||||
form[key] = data[key]
|
||||
}
|
||||
} else {
|
||||
// 在此处恢复默认表单
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
const submit = () => {
|
||||
formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (form.id) {
|
||||
// await update(form)
|
||||
} else {
|
||||
// await create(form)
|
||||
}
|
||||
ElMessage.success('保存成功')
|
||||
tableStore.index()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<template>
|
||||
<el-dialog width="600px" v-model.trim='dialogVisible' :title='title'>
|
||||
<el-scrollbar>
|
||||
<el-form :inline='false' :model='form' label-width='auto' class="form-one" :rules='rules' ref='formRef'>
|
||||
<el-form-item label='角色名称'>
|
||||
<el-input maxlength="32" show-word-limit v-model.trim='form.name' placeholder='请输入菜单名称' />
|
||||
</el-form-item>
|
||||
<el-form-item label='角色编码'>
|
||||
<el-input maxlength="32" show-word-limit v-model.trim='form.code' placeholder='请输入菜单名称' />
|
||||
</el-form-item>
|
||||
<el-form-item label='角色描述'>
|
||||
<el-input maxlength="300" show-word-limit v-model.trim='form.remark' :rows='2' type='textarea'
|
||||
placeholder='请输入描述' />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<span class='dialog-footer'>
|
||||
<el-button @click='dialogVisible = false'>取消</el-button>
|
||||
<el-button type='primary' @click='submit'>确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang='ts' setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import TableStore from '@/utils/tableStore' // 若不是列表页面弹框可删除
|
||||
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('')
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
const formRef = ref()
|
||||
// 注意不要和表单ref的命名冲突
|
||||
const form = reactive({
|
||||
code: '',
|
||||
name: '',
|
||||
remark: '',
|
||||
id: ''
|
||||
})
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '请输入角色编码', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const open = (text: string, data?: anyObj) => {
|
||||
title.value = text
|
||||
dialogVisible.value = true
|
||||
if (data) {
|
||||
// 表单赋值
|
||||
for (let key in form) {
|
||||
form[key] = data[key]
|
||||
}
|
||||
} else {
|
||||
// 在此处恢复默认表单
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
const submit = () => {
|
||||
formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (form.id) {
|
||||
// await update(form)
|
||||
} else {
|
||||
// await create(form)
|
||||
}
|
||||
ElMessage.success('保存成功')
|
||||
tableStore.index()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
@@ -44,7 +44,8 @@ const calculateValue = (o: number, value: number, num: number, isMin: boolean) =
|
||||
}
|
||||
|
||||
if (base === 0.1) {
|
||||
return parseFloat(calculatedValue.toFixed(1))
|
||||
// return parseFloat(calculatedValue.toFixed(1))
|
||||
return Math.ceil(calculatedValue * 10) / 10
|
||||
} else if (isMin) {
|
||||
return Math.floor(calculatedValue / base) * base
|
||||
} else {
|
||||
|
||||
@@ -372,7 +372,7 @@ export function getTimeOfTheMonth(key: any): [string, string] {
|
||||
* @param fullscreen // 全屏是否全屏
|
||||
*/
|
||||
export function getTime(interval: number | 3, timeList: any, externalTime: any) {
|
||||
console.log('🚀 ~ getTime ~ timeList:', timeList)
|
||||
// console.log('🚀 ~ getTime ~ timeList:', timeList)
|
||||
// 1、先匹配时间
|
||||
// 检查 interval 是否在 timeList 中
|
||||
if (timeList && timeList.includes(interval.toString())) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useAdminInfo } from '@/stores/adminInfo'
|
||||
|
||||
window.requests = []
|
||||
window.tokenRefreshing = false
|
||||
let loginExpireTimer:any=null
|
||||
let loginExpireTimer: any = null
|
||||
const pendingMap = new Map()
|
||||
const loadingInstance: LoadingInstance = {
|
||||
target: null,
|
||||
@@ -65,10 +65,18 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
|
||||
if (
|
||||
!(
|
||||
config.url == '/system-boot/file/upload' ||
|
||||
config.url == '/user-boot/sysRoleSystem/getSystemByRoleId' ||
|
||||
config.url == '/system-boot/file/getFileUrl' ||
|
||||
config.url == '/cs-harmonic-boot/realData/getBaseRealData' ||
|
||||
config.url == '/harmonic-boot/grid/getAssessOverview' ||
|
||||
config.url == '/harmonic-boot/gridDiagram/getGridDiagramAreaData' ||
|
||||
config.url == '/cs-device-boot/csline/list' ||
|
||||
config.url == '/cs-harmonic-boot/pqSensitiveUser/getListByIds'
|
||||
config.url == '/cs-harmonic-boot/pqSensitiveUser/getListByIds' ||
|
||||
config.url == '/cs-harmonic-boot/csevent/getEventCoords' ||
|
||||
config.url == '/cs-harmonic-boot/limitRateDetailD/limitTimeProbabilityData' ||
|
||||
config.url == '/cs-harmonic-boot/limitRateDetailD/limitProbabilityData' ||
|
||||
config.url == '/system-boot/dictTree/queryByCode' ||
|
||||
config.url == '/system-boot/dictTree/query'
|
||||
)
|
||||
)
|
||||
removePending(config)
|
||||
@@ -106,11 +114,11 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
|
||||
response => {
|
||||
removePending(response.config)
|
||||
options.loading && closeLoading(options) // 关闭loading
|
||||
if (
|
||||
if (
|
||||
response.data.code === 'A0000' ||
|
||||
response.data.type === 'application/json' ||
|
||||
Array.isArray(response.data) ||
|
||||
response.data.size
|
||||
response.data.size
|
||||
// ||
|
||||
// response.data.type === 'application/octet-stream' ||
|
||||
// response.data.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
@@ -145,7 +153,7 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
|
||||
})
|
||||
}
|
||||
} else if (response.data.code == 'A0024') {
|
||||
// // 登录失效
|
||||
// // 登录失效
|
||||
// 清除上一次的定时器
|
||||
if (loginExpireTimer) {
|
||||
clearTimeout(loginExpireTimer)
|
||||
@@ -174,6 +182,7 @@ function createAxios<Data = any, T = ApiPromise<Data>>(
|
||||
setTimeout(() => {
|
||||
ElMessage.error(response.data.message || '未知错误')
|
||||
}, 6000)
|
||||
} else if (response.config.url == '/cs-harmonic-boot/cspage/getByUserId') {
|
||||
} else {
|
||||
ElMessage.error(response.data.message || '未知错误')
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ export const getMenu = () => {
|
||||
}
|
||||
handlerMenu(res.data)
|
||||
handleAdminRoute(res.data)
|
||||
if (route.params.to) {
|
||||
if (route?.params?.to) {
|
||||
const lastRoute = JSON.parse(route.params.to as string)
|
||||
if (lastRoute.path != adminBaseRoutePath) {
|
||||
let query = !isEmpty(lastRoute.query) ? lastRoute.query : {}
|
||||
|
||||
@@ -45,6 +45,7 @@ export default class TableStore {
|
||||
pageSize: 20
|
||||
},
|
||||
loading: true,
|
||||
exportLoading: false,
|
||||
column: [],
|
||||
loadCallback: null,
|
||||
resetCallback: null,
|
||||
@@ -196,6 +197,7 @@ export default class TableStore {
|
||||
[
|
||||
'export',
|
||||
() => {
|
||||
this.table.exportLoading = true
|
||||
// this.index()
|
||||
let params = { ...this.table.params, pageNum: 1, pageSize: this.table.total }
|
||||
createAxios(
|
||||
@@ -206,11 +208,16 @@ export default class TableStore {
|
||||
},
|
||||
requestPayload(this.method, params, this.paramsPOST)
|
||||
)
|
||||
).then(res => {
|
||||
this.table.allData = filtration(res.data.records || res.data)
|
||||
this.table.exportProcessingData && this.table.exportProcessingData()
|
||||
this.table.allFlag = data.showAllFlag || true
|
||||
})
|
||||
)
|
||||
.then(res => {
|
||||
this.table.allData = filtration(res.data.records || res.data)
|
||||
this.table.exportProcessingData && this.table.exportProcessingData()
|
||||
this.table.allFlag = data.showAllFlag || true
|
||||
this.table.exportLoading = false
|
||||
})
|
||||
.catch(() => {
|
||||
this.table.exportLoading = false
|
||||
})
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
@@ -1,136 +1,136 @@
|
||||
<template>
|
||||
<div class="default-main">
|
||||
<div class="custom-table-header">
|
||||
<div class="title">待审核用户</div>
|
||||
<el-button :icon="Check" type="primary" @click="addRole" class="ml10">审核通过</el-button>
|
||||
</div>
|
||||
<Table ref="tableRef" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Check } from '@element-plus/icons-vue'
|
||||
import { ref, onMounted, provide } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { checkUser } from '@/api/user-boot/user'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/audit'
|
||||
})
|
||||
const tableStore = new TableStore({
|
||||
showPage: false,
|
||||
method: 'POST',
|
||||
url: '/user-boot/user/checkUserList',
|
||||
column: [
|
||||
// { width: '60', type: 'checkbox' },
|
||||
{ title: '名称', field: 'name' },
|
||||
{ title: '登录名', field: 'loginName' },
|
||||
{ title: '角色', field: 'roleName' },
|
||||
// { title: '部门', field: 'deptId' },
|
||||
{ title: '电话', field: 'phoneShow' },
|
||||
{ title: '注册时间', field: 'registerTime', sortable: true },
|
||||
{ title: '类型', field: 'casualUserName' },
|
||||
{ title: '状态', field: 'stateName' },
|
||||
{
|
||||
title: '操作',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '审核通过',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Check',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
checkUser([row.id]).then(res => {
|
||||
tableStore.index()
|
||||
ElMessage.success('操作成功')
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'del',
|
||||
title: '审核不通过',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-Close',
|
||||
render: 'confirmButton',
|
||||
popconfirm: {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonType: 'danger',
|
||||
title: '确定不通过该角色吗?'
|
||||
},
|
||||
click: row => {
|
||||
ElMessage.warning('功能尚未实现')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
tableStore.table.data.forEach((item: any) => {
|
||||
item.deptId = item.deptId || '/'
|
||||
item.phoneShow = item.phone || '/'
|
||||
item.roleName = item.role.length ? item.role : '/'
|
||||
switch (item.casualUser) {
|
||||
case 0:
|
||||
item.casualUserName = '临时用户'
|
||||
break
|
||||
case 1:
|
||||
item.casualUserName = '长期用户'
|
||||
break
|
||||
default:
|
||||
item.casualUserName = '/'
|
||||
break
|
||||
}
|
||||
switch (item.state) {
|
||||
case 0:
|
||||
item.stateName = '注销'
|
||||
break
|
||||
case 1:
|
||||
item.stateName = '正常'
|
||||
break
|
||||
case 2:
|
||||
item.stateName = '锁定'
|
||||
break
|
||||
case 3:
|
||||
item.stateName = '待审核'
|
||||
break
|
||||
case 4:
|
||||
item.stateName = '休眠'
|
||||
break
|
||||
case 5:
|
||||
item.stateName = '密码过期'
|
||||
break
|
||||
default:
|
||||
item.stateName = '/'
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
tableStore.table.params.casualUser = 0
|
||||
tableStore.table.params.searchState = 0
|
||||
tableStore.table.params.searchValue = ''
|
||||
tableStore.table.params.searchBeginTime = ''
|
||||
tableStore.table.params.searchEndTime = ''
|
||||
tableStore.table.params.sortBy = ''
|
||||
tableStore.table.params.orderBy = ''
|
||||
provide('tableStore', tableStore)
|
||||
onMounted(() => {
|
||||
tableStore.index()
|
||||
})
|
||||
|
||||
const addRole = () => {
|
||||
if (!tableStore.table.selection.length) {
|
||||
ElMessage.warning('请选择用户')
|
||||
return
|
||||
}
|
||||
checkUser(tableStore.table.selection.map((item: any) => item.id)).then(res => {
|
||||
tableStore.index()
|
||||
ElMessage.success('操作成功')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="default-main">
|
||||
<div class="custom-table-header">
|
||||
<div class="title">待审核用户</div>
|
||||
<el-button :icon="Check" type="primary" @click="addRole" class="ml10">审核通过</el-button>
|
||||
</div>
|
||||
<Table ref="tableRef" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Check } from '@element-plus/icons-vue'
|
||||
import { ref, onMounted, provide } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { checkUser } from '@/api/user-boot/user'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/audit'
|
||||
})
|
||||
const tableStore = new TableStore({
|
||||
showPage: false,
|
||||
method: 'POST',
|
||||
url: '/user-boot/user/checkUserList',
|
||||
column: [
|
||||
{ width: '60', type: 'checkbox' },
|
||||
{ title: '名称', field: 'name' },
|
||||
{ title: '登录名', field: 'loginName' },
|
||||
{ title: '角色', field: 'roleName' },
|
||||
// { title: '部门', field: 'deptId' },
|
||||
{ title: '电话', field: 'phoneShow' },
|
||||
{ title: '注册时间', field: 'registerTime', sortable: true },
|
||||
{ title: '类型', field: 'casualUserName' },
|
||||
{ title: '状态', field: 'stateName' },
|
||||
{
|
||||
title: '操作', fixed: 'right',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '审核通过',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Check',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
checkUser([row.id]).then(res => {
|
||||
tableStore.index()
|
||||
ElMessage.success('操作成功')
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'del',
|
||||
title: '审核不通过',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-Close',
|
||||
render: 'confirmButton',
|
||||
popconfirm: {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonType: 'danger',
|
||||
title: '确定不通过该角色吗?'
|
||||
},
|
||||
click: row => {
|
||||
ElMessage.warning('功能尚未实现')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
tableStore.table.data.forEach((item: any) => {
|
||||
item.deptId = item.deptId || '/'
|
||||
item.phoneShow = item.phone || '/'
|
||||
item.roleName = item.role.length ? item.role : '/'
|
||||
switch (item.casualUser) {
|
||||
case 0:
|
||||
item.casualUserName = '临时用户'
|
||||
break
|
||||
case 1:
|
||||
item.casualUserName = '长期用户'
|
||||
break
|
||||
default:
|
||||
item.casualUserName = '/'
|
||||
break
|
||||
}
|
||||
switch (item.state) {
|
||||
case 0:
|
||||
item.stateName = '注销'
|
||||
break
|
||||
case 1:
|
||||
item.stateName = '正常'
|
||||
break
|
||||
case 2:
|
||||
item.stateName = '锁定'
|
||||
break
|
||||
case 3:
|
||||
item.stateName = '待审核'
|
||||
break
|
||||
case 4:
|
||||
item.stateName = '休眠'
|
||||
break
|
||||
case 5:
|
||||
item.stateName = '密码过期'
|
||||
break
|
||||
default:
|
||||
item.stateName = '/'
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
tableStore.table.params.casualUser = 0
|
||||
tableStore.table.params.searchState = 0
|
||||
tableStore.table.params.searchValue = ''
|
||||
tableStore.table.params.searchBeginTime = ''
|
||||
tableStore.table.params.searchEndTime = ''
|
||||
tableStore.table.params.sortBy = ''
|
||||
tableStore.table.params.orderBy = ''
|
||||
provide('tableStore', tableStore)
|
||||
onMounted(() => {
|
||||
tableStore.index()
|
||||
})
|
||||
|
||||
const addRole = () => {
|
||||
if (!tableStore.table.selection.length) {
|
||||
ElMessage.warning('请选择用户')
|
||||
return
|
||||
}
|
||||
checkUser(tableStore.table.selection.map((item: any) => item.id)).then(res => {
|
||||
tableStore.index()
|
||||
ElMessage.success('操作成功')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,116 +1,136 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="custom-table-header">
|
||||
<div class="title">接口权限列表</div>
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
|
||||
style="width: 240px" placeholder="请输入菜单名称" class="ml10" clearable @input="search" />
|
||||
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10" :disabled="!props.id">新增</el-button>
|
||||
</div>
|
||||
<Table ref="tableRef" />
|
||||
<popupApi ref="popupRef" @init="tableStore.index()"></popupApi>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { ref, Ref, inject, provide, watch } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import popupApi from './popupApi.vue'
|
||||
import { deleteMenu } from '@/api/user-boot/function'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/menu/api'
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
}>()
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const tableRef = ref()
|
||||
const popupRef = ref()
|
||||
const apiList = ref([])
|
||||
const tableStore = new TableStore({
|
||||
showPage: false,
|
||||
url: '/user-boot/function/getButtonById',
|
||||
publicHeight: 60,
|
||||
column: [
|
||||
{ title: '普通接口/接口名称', field: 'name' },
|
||||
{
|
||||
title: '接口类型',
|
||||
field: 'type',
|
||||
formatter: row => {
|
||||
return row.cellValue == 1 ? '普通接口' : '公用接口'
|
||||
}
|
||||
},
|
||||
{ title: 'URL接口路径', field: 'path' },
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
popupRef.value.open('编辑接口权限', row)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'del',
|
||||
title: '删除',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-Delete',
|
||||
render: 'confirmButton',
|
||||
popconfirm: {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonType: 'danger',
|
||||
title: '确定删除该菜单吗?'
|
||||
},
|
||||
click: row => {
|
||||
deleteMenu(row.id).then(() => {
|
||||
tableStore.index()
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
apiList.value = tableStore.table.data
|
||||
search()
|
||||
}
|
||||
})
|
||||
tableStore.table.loading = false
|
||||
watch(
|
||||
() => props.id,
|
||||
() => {
|
||||
tableStore.table.params.id = props.id
|
||||
tableStore.index()
|
||||
}
|
||||
)
|
||||
provide('tableStore', tableStore)
|
||||
|
||||
const addMenu = () => {
|
||||
console.log(popupRef)
|
||||
popupRef.value.open('新增接口权限', {
|
||||
pid: props.id
|
||||
})
|
||||
}
|
||||
const search = () => {
|
||||
tableStore.table.data = apiList.value.filter(
|
||||
(item: any) =>
|
||||
!tableStore.table.params.searchValue ||
|
||||
item.name.indexOf(tableStore.table.params.searchValue) !== -1 ||
|
||||
item.path.indexOf(tableStore.table.params.searchValue) !== -1
|
||||
)
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="custom-table-header">
|
||||
<div class="title">接口权限列表</div>
|
||||
<el-input
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
v-model.trim="tableStore.table.params.searchValue"
|
||||
style="width: 240px"
|
||||
placeholder="请输入菜单名称"
|
||||
class="ml10"
|
||||
clearable
|
||||
@input="search"
|
||||
/>
|
||||
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10" :disabled="!props.id">新增</el-button>
|
||||
</div>
|
||||
<Table ref="tableRef" />
|
||||
<popupApi ref="popupRef" @init="tableStore.index()"></popupApi>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { ref, Ref, inject, provide, watch } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import popupApi from './popupApi.vue'
|
||||
import { deleteMenu } from '@/api/user-boot/function'
|
||||
import { ElMessage } from 'element-plus'
|
||||
defineOptions({
|
||||
name: 'auth/menu/api'
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
}>()
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const tableRef = ref()
|
||||
const popupRef = ref()
|
||||
const apiList = ref([])
|
||||
const tableStore = new TableStore({
|
||||
showPage: false,
|
||||
url: '/user-boot/function/getButtonById',
|
||||
publicHeight: 60,
|
||||
column: [
|
||||
{
|
||||
field: 'index',
|
||||
title: '序号',
|
||||
width: '80',
|
||||
formatter: (row: any) => {
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{ title: '普通接口/接口名称', field: 'name', minWidth: '180' },
|
||||
{
|
||||
title: '接口类型',
|
||||
field: 'type',
|
||||
minWidth: '140',
|
||||
formatter: row => {
|
||||
return row.cellValue == 1 ? '普通接口' : '公用接口'
|
||||
}
|
||||
},
|
||||
{ title: 'URL接口路径', field: 'path', minWidth: '200' },
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
width: '140',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
popupRef.value.open('编辑接口权限', row)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'del',
|
||||
title: '删除',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-Delete',
|
||||
render: 'confirmButton',
|
||||
popconfirm: {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonType: 'danger',
|
||||
title: '确定删除该菜单吗?'
|
||||
},
|
||||
click: row => {
|
||||
deleteMenu(row.id).then(() => {
|
||||
ElMessage.success('删除成功!')
|
||||
|
||||
tableStore.index()
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
apiList.value = tableStore.table.data
|
||||
search()
|
||||
}
|
||||
})
|
||||
tableStore.table.loading = false
|
||||
watch(
|
||||
() => props.id,
|
||||
() => {
|
||||
tableStore.table.params.id = props.id
|
||||
tableStore.index()
|
||||
}
|
||||
)
|
||||
provide('tableStore', tableStore)
|
||||
|
||||
const addMenu = () => {
|
||||
console.log(popupRef)
|
||||
popupRef.value.open('新增接口权限', {
|
||||
pid: props.id
|
||||
})
|
||||
}
|
||||
const search = () => {
|
||||
tableStore.table.data = apiList.value.filter(
|
||||
(item: any) =>
|
||||
!tableStore.table.params.searchValue ||
|
||||
item.name.indexOf(tableStore.table.params.searchValue) !== -1 ||
|
||||
item.path.indexOf(tableStore.table.params.searchValue) !== -1
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,140 +1,142 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="custom-table-header">
|
||||
<div class="title">菜单列表</div>
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
|
||||
style="width: 310px" placeholder="请输入菜单名称" class="ml10" clearable @input="search" />
|
||||
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增</el-button>
|
||||
</div>
|
||||
<Table @currentChange="currentChange" />
|
||||
<popupMenu ref="popupRef" @init="emits('init')"></popupMenu>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { ref, nextTick, inject, provide, watch } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import popupMenu from './popupMenu.vue'
|
||||
import { delMenu } from '@/api/systerm'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/menu/menu'
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
(e: 'select', row: any): void
|
||||
}>()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
menuData: treeData[]
|
||||
}>(),
|
||||
{
|
||||
menuData: () => {
|
||||
return []
|
||||
}
|
||||
}
|
||||
)
|
||||
const popupRef = ref()
|
||||
const tableStore = new TableStore({
|
||||
showPage: false,
|
||||
url: '/user-boot/function/functionTree',
|
||||
publicHeight: 60,
|
||||
column: [
|
||||
{ title: '菜单名称', field: 'title', align: 'left', treeNode: true },
|
||||
{
|
||||
title: '图标',
|
||||
field: 'icon',
|
||||
align: 'center',
|
||||
width: '60',
|
||||
render: 'icon'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
text: '新增',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Plus',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
popupRef.value.open('新增菜单', { pid: row.id })
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
text: '编辑',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
popupRef.value.open('编辑菜单', row)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'del',
|
||||
text: '删除',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-Delete',
|
||||
render: 'confirmButton',
|
||||
popconfirm: {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonType: 'danger',
|
||||
title: '确定删除该菜单吗?'
|
||||
},
|
||||
click: row => {
|
||||
delMenu(row.id).then(() => {
|
||||
emits('init')
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
tableStore.table.loading = false
|
||||
watch(
|
||||
() => props.menuData,
|
||||
() => {
|
||||
search()
|
||||
}
|
||||
)
|
||||
provide('tableStore', tableStore)
|
||||
|
||||
const addMenu = () => {
|
||||
popupRef.value.open('新增菜单', {})
|
||||
}
|
||||
const currentChange = (newValue: any) => {
|
||||
emits('select', newValue.row.id)
|
||||
}
|
||||
const search = () => {
|
||||
tableStore.table.data = filterData(JSON.parse(JSON.stringify(props.menuData)))
|
||||
if (tableStore.table.params.searchValue) {
|
||||
nextTick(() => {
|
||||
tableStore.table.ref?.setAllTreeExpand(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤
|
||||
const filterData = (arr: treeData[]): treeData[] => {
|
||||
if (!tableStore.table.params.searchValue) {
|
||||
return arr
|
||||
}
|
||||
return arr.filter((item: treeData) => {
|
||||
if (item.title.includes(tableStore.table.params.searchValue)) {
|
||||
return true
|
||||
} else if (item.children?.length > 0) {
|
||||
item.children = filterData(item.children)
|
||||
return item.children.length > 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="custom-table-header">
|
||||
<div class="title">菜单列表</div>
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue"
|
||||
style="width: 310px" placeholder="请输入菜单名称" class="ml10" clearable @input="search" />
|
||||
<el-button :icon="Plus" type="primary" @click="addMenu" class="ml10">新增</el-button>
|
||||
</div>
|
||||
<Table @currentChange="currentChange" />
|
||||
<popupMenu ref="popupRef" @init="emits('init')"></popupMenu>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { ref, nextTick, inject, provide, watch } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import popupMenu from './popupMenu.vue'
|
||||
import { delMenu } from '@/api/systerm'
|
||||
import { ElMessage } from 'element-plus'
|
||||
defineOptions({
|
||||
name: 'auth/menu/menu'
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
(e: 'select', row: any): void
|
||||
}>()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
menuData: treeData[]
|
||||
}>(),
|
||||
{
|
||||
menuData: () => {
|
||||
return []
|
||||
}
|
||||
}
|
||||
)
|
||||
const popupRef = ref()
|
||||
const tableStore = new TableStore({
|
||||
showPage: false,
|
||||
url: '/user-boot/function/functionTree',
|
||||
publicHeight: 60,
|
||||
column: [
|
||||
{ title: '菜单名称', field: 'title', align: 'left', treeNode: true },
|
||||
{
|
||||
title: '图标',
|
||||
field: 'icon',
|
||||
align: 'center',
|
||||
width: '60',
|
||||
render: 'icon'
|
||||
},
|
||||
{
|
||||
title: '操作', fixed: 'right',
|
||||
align: 'center',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
text: '新增',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Plus',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
popupRef.value.open('新增菜单', { pid: row.id })
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
text: '编辑',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
popupRef.value.open('编辑菜单', row)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'del',
|
||||
text: '删除',
|
||||
type: 'danger',
|
||||
icon: 'el-icon-Delete',
|
||||
render: 'confirmButton',
|
||||
popconfirm: {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonType: 'danger',
|
||||
title: '确定删除该菜单吗?'
|
||||
},
|
||||
click: row => {
|
||||
delMenu(row.id).then(() => {
|
||||
ElMessage.success('删除成功!')
|
||||
|
||||
emits('init')
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
tableStore.table.loading = false
|
||||
watch(
|
||||
() => props.menuData,
|
||||
() => {
|
||||
search()
|
||||
}
|
||||
)
|
||||
provide('tableStore', tableStore)
|
||||
|
||||
const addMenu = () => {
|
||||
popupRef.value.open('新增菜单', {})
|
||||
}
|
||||
const currentChange = (newValue: any) => {
|
||||
emits('select', newValue.row.id)
|
||||
}
|
||||
const search = () => {
|
||||
tableStore.table.data = filterData(JSON.parse(JSON.stringify(props.menuData)))
|
||||
if (tableStore.table.params.searchValue) {
|
||||
nextTick(() => {
|
||||
tableStore.table.ref?.setAllTreeExpand(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤
|
||||
const filterData = (arr: treeData[]): treeData[] => {
|
||||
if (!tableStore.table.params.searchValue) {
|
||||
return arr
|
||||
}
|
||||
return arr.filter((item: treeData) => {
|
||||
if (item.title.includes(tableStore.table.params.searchValue)) {
|
||||
return true
|
||||
} else if (item.children?.length > 0) {
|
||||
item.children = filterData(item.children)
|
||||
return item.children.length > 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,104 +1,134 @@
|
||||
<template>
|
||||
<el-dialog width="700px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-scrollbar>
|
||||
<el-form :mode="form" :inline="false" :model="form" label-width="120px" :rules="rules" class="form-one">
|
||||
<el-form-item prop="name" label="接口/按钮名称">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入接口名称" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" label="接口/按钮标识">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.code" placeholder="请输入英文接口标识" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="path" label="接口路径">
|
||||
<el-input v-model.trim="form.path" placeholder="请输入接口路径" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="接口类型">
|
||||
<el-radio-group v-model.trim="form.type">
|
||||
<el-radio :label="1">普通接口</el-radio>
|
||||
<el-radio :label="2">公用接口</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="sort" label="排序">
|
||||
<el-input maxlength="32" show-word-limit-number v-model.trim="form.sort" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="接口/按钮描述">
|
||||
<el-input maxlength="300" show-word-limit v-model.trim="form.remark" :rows="2" type="textarea"
|
||||
placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import { update, add } from '@/api/user-boot/function'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/menu/popupApi'
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
}>()
|
||||
const form: any = reactive({
|
||||
id: '',
|
||||
pid: '0',
|
||||
code: '',
|
||||
name: '',
|
||||
path: '',
|
||||
type: 1,
|
||||
sort: 100,
|
||||
remark: ''
|
||||
})
|
||||
const rules = {
|
||||
code: [
|
||||
{ required: true, message: '标识不能为空', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^[a-zA-Z_]{1}[a-zA-Z0-9_]{2,20}$/,
|
||||
message: '请输入至少3-20位英文',
|
||||
min: 3,
|
||||
max: 20,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
name: [{ required: true, message: '请输入接口名称', trigger: 'blur' }],
|
||||
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }],
|
||||
path: [{ required: true, message: '请输入接口路径', trigger: 'blur' }]
|
||||
}
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const open = (text: string, data: anyObj) => {
|
||||
title.value = text
|
||||
// 重置表单
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
form.type = 1
|
||||
form.pid = data.pid
|
||||
if (data.id) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key] || ''
|
||||
}
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submit = async () => {
|
||||
if (form.id) {
|
||||
await update(form)
|
||||
} else {
|
||||
let obj = JSON.parse(JSON.stringify(form))
|
||||
delete obj.id
|
||||
await add(obj)
|
||||
}
|
||||
emits('init')
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<template>
|
||||
<el-dialog width="700px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-scrollbar>
|
||||
<el-form
|
||||
:mode="form"
|
||||
:inline="false"
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
label-width="120px"
|
||||
:rules="rules"
|
||||
class="form-one"
|
||||
>
|
||||
<el-form-item prop="name" label="接口/按钮名称">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入接口名称" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" label="接口/按钮标识">
|
||||
<el-input
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
v-model.trim="form.code"
|
||||
placeholder="请输入英文接口标识"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="path" label="接口路径">
|
||||
<el-input v-model.trim="form.path" placeholder="请输入接口路径" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="接口类型">
|
||||
<el-radio-group v-model.trim="form.type">
|
||||
<el-radio :label="1">普通接口</el-radio>
|
||||
<el-radio :label="2">公用接口</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="sort" label="排序">
|
||||
<el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="接口/按钮描述">
|
||||
<el-input
|
||||
maxlength="300"
|
||||
show-word-limit
|
||||
v-model.trim="form.remark"
|
||||
:rows="2"
|
||||
type="textarea"
|
||||
placeholder="请输入描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import { update, add } from '@/api/user-boot/function'
|
||||
import { ElMessage } from 'element-plus'
|
||||
defineOptions({
|
||||
name: 'auth/menu/popupApi'
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
}>()
|
||||
const formRef = ref()
|
||||
const form: any = reactive({
|
||||
id: '',
|
||||
pid: '0',
|
||||
code: '',
|
||||
name: '',
|
||||
path: '',
|
||||
type: 1,
|
||||
sort: 100,
|
||||
remark: ''
|
||||
})
|
||||
const rules = {
|
||||
code: [
|
||||
{ required: true, message: '请输入标识', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^[a-zA-Z_]{1}[a-zA-Z0-9_]{2,20}$/,
|
||||
message: '请输入至少3-20位英文',
|
||||
min: 3,
|
||||
max: 20,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
name: [{ required: true, message: '请输入接口名称', trigger: 'blur' }],
|
||||
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }],
|
||||
path: [{ required: true, message: '请输入接口路径', trigger: 'blur' }]
|
||||
}
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const open = (text: string, data: anyObj) => {
|
||||
formRef.value?.resetFields()
|
||||
title.value = text
|
||||
// 重置表单
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
form.type = 1
|
||||
form.sort = 100
|
||||
form.pid = data.pid
|
||||
if (data.id) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key] || ''
|
||||
}
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submit = async () => {
|
||||
formRef.value.validate(async valid => {
|
||||
if (valid) {
|
||||
if (form.id) {
|
||||
await update(form).then(() => {
|
||||
ElMessage.success('修改成功!')
|
||||
})
|
||||
} else {
|
||||
let obj = JSON.parse(JSON.stringify(form))
|
||||
delete obj.id
|
||||
await add(obj).then(() => {
|
||||
ElMessage.success('新增成功!')
|
||||
})
|
||||
}
|
||||
emits('init')
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
@@ -1,107 +1,141 @@
|
||||
<template>
|
||||
<el-dialog class="cn-operate-dialog" width="700px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-scrollbar>
|
||||
<el-form :inline="false" :model="form" label-width="auto" class="form-one">
|
||||
<el-form-item label="上级菜单">
|
||||
<el-cascader v-model.trim="form.pid" :options="tableStore.table.data" :props="cascaderProps"
|
||||
style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单名称">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="图标">
|
||||
<IconSelector v-model.trim="form.icon" placeholder="请选择图标" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单路由">
|
||||
<el-input v-model.trim="form.path" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="组件路径">
|
||||
<el-input v-model.trim="form.routeName" placeholder="请输入组件路径,如/src/views/dashboard/index.vue" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input maxlength="32" show-word-limit-number v-model.trim="form.sort" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单描述">
|
||||
<el-input maxlength="300" show-word-limit v-model.trim="form.remark" :rows="2" type="textarea"
|
||||
placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import IconSelector from '@/components/baInput/components/iconSelector.vue'
|
||||
import { updateMenu, addMenu } from '@/api/systerm'
|
||||
|
||||
defineOptions({
|
||||
name: 'auth/menu/popupMenu'
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
}>()
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
const cascaderProps = {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
checkStrictly: true,
|
||||
emitPath: false
|
||||
}
|
||||
const form: any = reactive({
|
||||
code: '',
|
||||
icon: '',
|
||||
id: '',
|
||||
name: '',
|
||||
path: '',
|
||||
pid: '0',
|
||||
remark: '',
|
||||
routeName: '',
|
||||
sort: 100,
|
||||
type: 0
|
||||
})
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const open = (text: string, data: anyObj) => {
|
||||
title.value = text
|
||||
// 重置表单
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
form.pid = data.pid || '0'
|
||||
form.sort = 100
|
||||
form.type = 0
|
||||
|
||||
if (data.id) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key] ? data[key] : data[key] === 0 ? 0 : ''
|
||||
}
|
||||
form.path = data.routePath || ''
|
||||
form.name = data.title || ''
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submit = async () => {
|
||||
if (form.id) {
|
||||
await updateMenu(form)
|
||||
} else {
|
||||
form.code = 'menu'
|
||||
let obj = JSON.parse(JSON.stringify(form))
|
||||
delete obj.id
|
||||
await addMenu(obj)
|
||||
}
|
||||
emits('init')
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<template>
|
||||
<el-dialog class="cn-operate-dialog" width="700px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-scrollbar>
|
||||
<el-form :inline="false" :model="form" label-width="auto" ref="formRef" class="form-one" :rules="rules">
|
||||
<el-form-item label="上级菜单">
|
||||
<el-cascader
|
||||
v-model.trim="form.pid"
|
||||
:options="tableStore.table.data"
|
||||
:props="cascaderProps"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单名称" prop="name">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="图标">
|
||||
<IconSelector v-model.trim="form.icon" placeholder="请选择图标" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单路由" prop="path">
|
||||
<el-input v-model.trim="form.path" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="组件路径" prop="routeName">
|
||||
<el-input
|
||||
v-model.trim="form.routeName"
|
||||
placeholder="请输入组件路径,如/src/views/dashboard/index.vue"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input maxlength="32" show-word-limit-number v-model.number="form.sort" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单描述">
|
||||
<el-input
|
||||
maxlength="300"
|
||||
show-word-limit
|
||||
v-model.trim="form.remark"
|
||||
:rows="2"
|
||||
type="textarea"
|
||||
placeholder="请输入描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import IconSelector from '@/components/baInput/components/iconSelector.vue'
|
||||
import { updateMenu, addMenu } from '@/api/systerm'
|
||||
import { ElMessage } from 'element-plus'
|
||||
defineOptions({
|
||||
name: 'auth/menu/popupMenu'
|
||||
})
|
||||
const formRef = ref()
|
||||
const emits = defineEmits<{
|
||||
(e: 'init'): void
|
||||
}>()
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
const cascaderProps = {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
checkStrictly: true,
|
||||
emitPath: false
|
||||
}
|
||||
const form: any = reactive({
|
||||
code: '',
|
||||
icon: '',
|
||||
id: '',
|
||||
name: '',
|
||||
path: '',
|
||||
pid: '0',
|
||||
remark: '',
|
||||
routeName: '',
|
||||
sort: 100,
|
||||
type: 0
|
||||
})
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入菜单名称', trigger: 'blur' }],
|
||||
path: [{ required: true, message: '请输入菜单路由', trigger: 'blur' }],
|
||||
icon: [{ required: true, message: '请选择图标', trigger: 'blur' }],
|
||||
routeName: [{ required: true, message: '请输入组件路径', trigger: 'blur' }],
|
||||
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
|
||||
}
|
||||
const open = (text: string, data: anyObj) => {
|
||||
formRef.value?.resetFields()
|
||||
title.value = text
|
||||
// 重置表单
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
form.pid = data.pid || '0'
|
||||
form.sort = 100
|
||||
form.type = 0
|
||||
|
||||
if (data.id) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key] ? data[key] : data[key] === 0 ? 0 : ''
|
||||
}
|
||||
form.path = data.routePath || ''
|
||||
form.name = data.title || ''
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const submit = async () => {
|
||||
formRef.value.validate(async valid => {
|
||||
if (valid) {
|
||||
if (form.id) {
|
||||
form.pid = form.pid || '0'
|
||||
await updateMenu(form).then(() => {
|
||||
ElMessage.success('编辑成功!')
|
||||
})
|
||||
} else {
|
||||
form.code = 'menu'
|
||||
form.pid = form.pid || '0'
|
||||
let obj = JSON.parse(JSON.stringify(form))
|
||||
delete obj.id
|
||||
|
||||
await addMenu(obj).then(() => {
|
||||
ElMessage.success('新增成功!')
|
||||
})
|
||||
}
|
||||
emits('init')
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
@@ -5,18 +5,36 @@
|
||||
<div class="title">角色列表</div>
|
||||
<el-button :icon="Plus" type="primary" @click="addRole" class="ml10">新增</el-button>
|
||||
</div>
|
||||
<Table ref="tableRef" @currentChange="currentChange" />
|
||||
<Table ref="tableRef" :row-config="{ isCurrent: true, isHover: true }" @currentChange="currentChange" />
|
||||
</div>
|
||||
<div>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="菜单">
|
||||
<Tree
|
||||
v-if="menuListId"
|
||||
ref="treeRef"
|
||||
show-checkbox
|
||||
width="350px"
|
||||
:data="menuTree"
|
||||
:checkStrictly="checkStrictly"
|
||||
@checkChange="checkChange"
|
||||
></Tree>
|
||||
<el-empty
|
||||
style="width: 350px; padding-top: 300px; box-sizing: border-box"
|
||||
description="请选择角色"
|
||||
v-else
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="系统">
|
||||
<div class="mt10 mr10" style="display: flex; justify-content: end">
|
||||
<el-button type="primary" icon="el-icon-Select" @click="saveSystem">保存</el-button>
|
||||
</div>
|
||||
<el-checkbox-group v-model="systemIds" class="md10 system">
|
||||
<el-checkbox v-for="item in systemList" :label="item.name" :value="item.id" :key="item.id" />
|
||||
</el-checkbox-group>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<Tree
|
||||
v-if="menuListId"
|
||||
ref="treeRef"
|
||||
show-checkbox
|
||||
width="350px"
|
||||
:data="menuTree"
|
||||
:checkStrictly="checkStrictly"
|
||||
@check-change="checkChange"
|
||||
></Tree>
|
||||
<el-empty style="width: 350px; padding-top: 300px; box-sizing: border-box" description="请选择角色" v-else />
|
||||
<PopupForm ref="popupRef"></PopupForm>
|
||||
</div>
|
||||
</template>
|
||||
@@ -26,23 +44,27 @@ 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 Tree from '@/components/tree/index.vue'
|
||||
import Tree from '@/components/tree/allocation.vue'
|
||||
import { functionTree } from '@/api/user-boot/function'
|
||||
import { getFunctionsByRoleIndex, updateRoleMenu } from '@/api/user-boot/roleFuction'
|
||||
import { getFunctionsByRoleIndex, updateRoleMenu, getSystemByRoleId, systemAdd } from '@/api/user-boot/roleFuction'
|
||||
import { mainHeight } from '@/utils/layout'
|
||||
import { del } from '@/api/user-boot/role'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import PopupForm from './popupForm.vue'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
|
||||
import { useDictData } from '@/stores/dictData'
|
||||
const dictData = useDictData()
|
||||
const systemList = dictData.getBasicData('System_Type')
|
||||
const adminInfo = useAdminInfo()
|
||||
defineOptions({
|
||||
name: 'auth/role'
|
||||
})
|
||||
const systemIds = ref([])
|
||||
const height = mainHeight(20).height
|
||||
const treeRef = ref()
|
||||
const menuTree = ref<treeData[]>([])
|
||||
const popupRef = ref()
|
||||
const tableRef = ref()
|
||||
const checkStrictly = ref(true)
|
||||
const menuListId = ref('')
|
||||
const tableStore = new TableStore({
|
||||
@@ -68,7 +90,7 @@ const tableStore = new TableStore({
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
title: '操作', fixed: 'right',
|
||||
align: 'center',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
@@ -104,7 +126,13 @@ const tableStore = new TableStore({
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
loadCallback: () => {
|
||||
tableRef.value.getRef().setCurrentRow(tableStore.table.data[0])
|
||||
currentChange({
|
||||
row: tableStore.table.data[0]
|
||||
})
|
||||
}
|
||||
})
|
||||
tableStore.table.params.searchValue = ''
|
||||
|
||||
@@ -134,6 +162,12 @@ const currentChange = (data: any) => {
|
||||
menuListId.value = data.row.id
|
||||
getFunctionsByRoleIndex({ id: data.row.id }).then((res: any) => {
|
||||
treeRef.value.treeRef.setCheckedKeys(res.data.map((item: any) => item.id))
|
||||
setTimeout(() => {
|
||||
checkStrictly.value = false
|
||||
}, 100)
|
||||
})
|
||||
getSystemByRoleId({ id: data.row.id }).then((res: any) => {
|
||||
systemIds.value = res.data.systemIds || []
|
||||
})
|
||||
}
|
||||
|
||||
@@ -143,17 +177,31 @@ const checkChange = (data: any) => {
|
||||
checkStrictly.value = false
|
||||
return
|
||||
}
|
||||
if (timeout.value) {
|
||||
clearTimeout(timeout.value)
|
||||
}
|
||||
timeout.value = setTimeout(() => {
|
||||
updateRoleMenu({
|
||||
id: menuListId.value,
|
||||
idList: treeRef.value.treeRef.getCheckedNodes(false, true).map((node: any) => node.id)
|
||||
}).then(() => {
|
||||
|
||||
updateRoleMenu({
|
||||
id: menuListId.value,
|
||||
idList: treeRef.value.treeRef.getCheckedNodes(false, true).map((node: any) => node.id)
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('操作成功!')
|
||||
treeRef.value.loading = false
|
||||
})
|
||||
.catch(() => {
|
||||
treeRef.value.loading = false
|
||||
})
|
||||
}
|
||||
const saveSystem = () => {
|
||||
systemAdd({
|
||||
roleId: menuListId.value,
|
||||
systemIds: systemIds.value
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success('操作成功!')
|
||||
treeRef.value.loading = false
|
||||
})
|
||||
.catch(() => {
|
||||
treeRef.value.loading = false
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
onMounted(() => {
|
||||
tableStore.index()
|
||||
@@ -162,3 +210,17 @@ const addRole = () => {
|
||||
popupRef.value.open('新增角色')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs--border-card > .el-tabs__content) {
|
||||
padding: 0px !important;
|
||||
}
|
||||
.system {
|
||||
width: 330px;
|
||||
height: calc(100vh - 225px);
|
||||
border: 1px solid var(--el-border-color);
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,74 +1,86 @@
|
||||
<template>
|
||||
<el-dialog class="cn-operate-dialog" width="700px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-form :inline="false" :model="form" label-width="auto" class="form-one" :rules="rules">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色编码">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.code" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色描述">
|
||||
<el-input maxlength="300" show-word-limit v-model.trim="form.remark" :rows="2" type="textarea"
|
||||
placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { add, update } from '@/api/user-boot/role'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
|
||||
const adminInfo = useAdminInfo()
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
code: '',
|
||||
name: '',
|
||||
remark: '',
|
||||
id: '',
|
||||
type: 0
|
||||
})
|
||||
const rules = {
|
||||
name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '角色编码不能为空', trigger: 'blur' }]
|
||||
}
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const open = (text: string, data?: anyObj) => {
|
||||
title.value = text
|
||||
dialogVisible.value = true
|
||||
if (data) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key]
|
||||
}
|
||||
} else {
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
const submit = async () => {
|
||||
if (form.id) {
|
||||
await update(form)
|
||||
} else {
|
||||
form.type = adminInfo.$state.userType + 1
|
||||
await add(form)
|
||||
}
|
||||
ElMessage.success('保存成功')
|
||||
tableStore.index()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<template>
|
||||
<el-dialog class="cn-operate-dialog" width="500px" v-model.trim="dialogVisible" :title="title">
|
||||
<el-form :inline="false" ref="formRef" :model="form" label-width="auto" class="form-one" :rules="rules">
|
||||
<el-form-item label="角色名称" prop="name">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色编码" prop="code">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.code" placeholder="请输入菜单名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色描述">
|
||||
<el-input
|
||||
maxlength="300"
|
||||
show-word-limit
|
||||
v-model.trim="form.remark"
|
||||
:rows="2"
|
||||
type="textarea"
|
||||
placeholder="请输入描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { add, update } from '@/api/user-boot/role'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
|
||||
const adminInfo = useAdminInfo()
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
code: '',
|
||||
name: '',
|
||||
remark: '',
|
||||
id: '',
|
||||
type: 0
|
||||
})
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '请输入角色编码', trigger: 'blur' }]
|
||||
}
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const formRef = ref()
|
||||
const open = (text: string, data?: anyObj) => {
|
||||
formRef.value?.resetFields()
|
||||
title.value = text
|
||||
dialogVisible.value = true
|
||||
if (data) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key]
|
||||
}
|
||||
} else {
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
const submit = async () => {
|
||||
formRef.value.validate(async valid => {
|
||||
if (valid) {
|
||||
if (form.id) {
|
||||
await update(form)
|
||||
} else {
|
||||
form.type = adminInfo.$state.userType + 1
|
||||
await add(form)
|
||||
}
|
||||
ElMessage.success('保存成功')
|
||||
tableStore.index()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
@@ -79,10 +79,10 @@ const tableStore = new TableStore({
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
title: '操作', fixed: 'right',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
fixed: 'right',
|
||||
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
@@ -95,6 +95,7 @@ const tableStore = new TableStore({
|
||||
},
|
||||
click: row => {
|
||||
popupEditRef.value.open('编辑用户', row)
|
||||
console.log("🚀 ~ row:", row)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,245 +1,277 @@
|
||||
<template>
|
||||
<el-dialog class="cn-operate-dialog" v-model.trim="dialogVisible" :title="title">
|
||||
|
||||
<el-form :model="form" label-width="auto" class="form-two" :rules="rules">
|
||||
<el-form-item label="用户名" prop="name">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录名" prop="loginName">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.loginName" placeholder="请输入登录名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="默认密码" prop="password" v-if="title === '新增用户'">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.password" placeholder="请输入密码" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限类型" prop="type">
|
||||
<el-select v-model.trim="form.type" @change="changeValue" disabled placeholder="请选择权限类型">
|
||||
<el-option v-for="(item, index) in UserTypeOption" :label="item.label" :value="item.value"
|
||||
:key="index"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户类型" prop="casualUser">
|
||||
<el-select v-model.trim="form.casualUser" placeholder="请选择权限类型">
|
||||
<el-option v-for="(item, index) in TypeOptions" :label="item.label" :value="item.value"
|
||||
:key="index"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="所属部门" prop="deptId">
|
||||
<Area v-model.trim="form.deptId" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="角色" prop="role">
|
||||
<el-select v-model.trim="form.role" placeholder="请选择角色" multiple collapse-tags>
|
||||
<el-option v-for="(item, index) in roleOptions" :label="item.label" :value="item.value"
|
||||
:key="index"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.phone" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间段" prop="limitTime">
|
||||
<el-slider v-model.trim="form.limitTime" style="width: 95%" range show-stops :max="24" />
|
||||
</el-form-item>
|
||||
<el-form-item label="起始IP" prop="limitIpStart">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束IP" prop="limitIpEnd">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpEnd" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="短信通知" prop="smsNotice">
|
||||
<el-radio-group v-model.trim="form.smsNotice" style="width: 200px">
|
||||
<el-radio-button :label="0">是</el-radio-button>
|
||||
<el-radio-button :label="1">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮件通知" prop="emailNotice">
|
||||
<el-radio-group v-model.trim="form.emailNotice" style="width: 200px">
|
||||
<el-radio-button :label="0">是</el-radio-button>
|
||||
<el-radio-button :label="1">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户ID">
|
||||
<div style="display: flex; width: 100%">
|
||||
<el-radio-group v-model.trim="useId">
|
||||
<el-radio-button :label="1">是</el-radio-button>
|
||||
<el-radio-button :label="0">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-input maxlength="32" show-word-limit :disabled="title !== '新增用户'" v-model.trim="form.id"
|
||||
placeholder="请输入用户id" v-if="useId" style="flex: 1;" class="ml10"></el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { ElMessage, FormItemRule } from 'element-plus'
|
||||
import { roleList } from '@/api/user-boot/role'
|
||||
import { add, edit } from '@/api/user-boot/user'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
import Area from '@/components/form/area/index.vue'
|
||||
import { Arrayable } from 'element-plus/es/utils'
|
||||
|
||||
const adminInfo = useAdminInfo()
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
password: '123456',
|
||||
email: '',
|
||||
limitIpStart: '',
|
||||
deptId: '',
|
||||
deptName: '',
|
||||
casualUser: 1,
|
||||
loginName: '',
|
||||
phone: '',
|
||||
limitIpEnd: '',
|
||||
limitTime: [1, 2],
|
||||
role: [],
|
||||
smsNotice: 0,
|
||||
emailNotice: 0,
|
||||
type: 0
|
||||
})
|
||||
const rules: Partial<Record<string, Arrayable<FormItemRule>>> = {
|
||||
name: [{ required: true, message: '用户名不能为空', trigger: 'blur' }],
|
||||
role: [{ required: true, message: '角色不能为空', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }],
|
||||
loginName: [{ required: true, message: '登录名不能为空', trigger: 'blur' }],
|
||||
casualUser: [{ required: true, message: '用户类型不能为空', trigger: 'blur' }],
|
||||
smsNotice: [{ required: true, message: '短信通知不能为空', trigger: 'blur' }],
|
||||
emailNotice: [{ required: true, message: '邮件通知不能为空', trigger: 'blur' }],
|
||||
email: [
|
||||
{ required: false, message: '邮箱不能为空', trigger: 'blur' },
|
||||
{
|
||||
type: 'email',
|
||||
message: "'请输入正确的邮箱地址",
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
phone: [
|
||||
{ required: false, message: '手机号不能为空', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
limitTime: [{ required: true, message: '时间段不能为空', trigger: 'blur' }],
|
||||
limitIpStart: [
|
||||
{ required: true, message: '起始IP不能为空', trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/
|
||||
let isCorrect = regexp.test(value)
|
||||
if (value == '') {
|
||||
return callback(new Error('请输入IP地址'))
|
||||
} else if (!isCorrect) {
|
||||
callback(new Error('请输入正确的IP地址'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
limitIpEnd: [
|
||||
{ required: true, message: '结束IP不能为空', trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/
|
||||
let isCorrect = regexp.test(value)
|
||||
if (value == '') {
|
||||
return callback(new Error('请输入IP地址'))
|
||||
} else if (!isCorrect) {
|
||||
callback(new Error('请输入正确的IP地址'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
const UserTypeOption = [
|
||||
{ label: '管理员', value: 1 },
|
||||
{ label: '普通用户', value: 2 }
|
||||
]
|
||||
const TypeOptions = [
|
||||
{ label: '临时用户', value: 0 },
|
||||
{ label: '长期用户', value: 1 }
|
||||
]
|
||||
const useId = ref(1)
|
||||
const roleOptions = ref<treeData>()
|
||||
const queryRole = () => {
|
||||
roleList(adminInfo.$state.userType).then((res: any) => {
|
||||
roleOptions.value = res.data.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
queryRole()
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const open = (text: string, data?: anyObj) => {
|
||||
title.value = text
|
||||
dialogVisible.value = true
|
||||
if (data) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key]
|
||||
}
|
||||
form.limitTime = data.limitTime.split('-')
|
||||
form.role = data.roleList
|
||||
} else {
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
form.casualUser = 1
|
||||
form.limitTime = [0, 24]
|
||||
form.role = []
|
||||
form.smsNotice = 0
|
||||
form.emailNotice = 0
|
||||
useId.value = 1
|
||||
form.id = ''
|
||||
form.limitIpStart = '0.0.0.0'
|
||||
form.limitIpEnd = '255.255.255.255'
|
||||
form.password = '123456'
|
||||
}
|
||||
form.type = adminInfo.$state.userType + 1
|
||||
}
|
||||
const submit = async () => {
|
||||
let obj = JSON.parse(JSON.stringify(form))
|
||||
obj.limitTime = obj.limitTime.join('-')
|
||||
delete obj.password
|
||||
if (form.id) {
|
||||
await edit(obj)
|
||||
ElMessage.success('修改成功')
|
||||
} else {
|
||||
form.type = adminInfo.$state.userType + 1
|
||||
await add(obj)
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
tableStore.index()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const changeValue = () => { }
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<template>
|
||||
<el-dialog class="cn-operate-dialog" v-model.trim="dialogVisible" :title="title">
|
||||
<el-form :model="form" ref="formRef" label-width="auto" class="form-two" :rules="rules">
|
||||
<el-form-item label="用户名" prop="name">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.name" placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="登录名" prop="loginName">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.loginName" placeholder="请输入登录名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="默认密码" prop="password" v-if="title === '新增用户'">
|
||||
<el-input
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
v-model.trim="form.password"
|
||||
placeholder="请输入密码"
|
||||
disabled
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="权限类型" prop="type">
|
||||
<el-select v-model.trim="form.type" @change="changeValue" disabled placeholder="请选择权限类型">
|
||||
<el-option
|
||||
v-for="(item, index) in UserTypeOption"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="index"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户类型" prop="casualUser">
|
||||
<el-select v-model.trim="form.casualUser" placeholder="请选择权限类型">
|
||||
<el-option
|
||||
v-for="(item, index) in TypeOptions"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="index"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="所属部门" prop="deptId">
|
||||
<Area v-model.trim="form.deptId" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="角色" prop="role">
|
||||
<el-select v-model.trim="form.role" placeholder="请选择角色" multiple collapse-tags>
|
||||
<el-option
|
||||
v-for="(item, index) in roleOptions"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="index"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.phone" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.email" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间段" prop="limitTime">
|
||||
<el-slider v-model.trim="form.limitTime" style="width: 95%" range show-stops :max="24" />
|
||||
</el-form-item>
|
||||
<el-form-item label="起始IP" prop="limitIpStart">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpStart" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束IP" prop="limitIpEnd">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="form.limitIpEnd" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="短信通知" prop="smsNotice">
|
||||
<el-radio-group v-model.trim="form.smsNotice" style="width: 200px">
|
||||
<el-radio-button :label="0">是</el-radio-button>
|
||||
<el-radio-button :label="1">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="邮件通知" prop="emailNotice">
|
||||
<el-radio-group v-model.trim="form.emailNotice" style="width: 200px">
|
||||
<el-radio-button :label="0">是</el-radio-button>
|
||||
<el-radio-button :label="1">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户ID">
|
||||
<div style="display: flex; width: 100%">
|
||||
<el-radio-group v-model.trim="useId">
|
||||
<el-radio-button :label="1">是</el-radio-button>
|
||||
<el-radio-button :label="0">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-input
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
:disabled="title !== '新增用户'"
|
||||
v-model.trim="form.id"
|
||||
placeholder="请输入用户id"
|
||||
v-if="useId"
|
||||
style="flex: 1"
|
||||
class="ml10"
|
||||
></el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from 'vue'
|
||||
import { reactive } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { ElMessage, FormItemRule } from 'element-plus'
|
||||
import { roleList } from '@/api/user-boot/role'
|
||||
import { add, edit } from '@/api/user-boot/user'
|
||||
import { useAdminInfo } from '@/stores/adminInfo'
|
||||
import Area from '@/components/form/area/index.vue'
|
||||
import { Arrayable } from 'element-plus/es/utils'
|
||||
|
||||
const adminInfo = useAdminInfo()
|
||||
const tableStore = inject('tableStore') as TableStore
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
password: '123456',
|
||||
email: '',
|
||||
limitIpStart: '',
|
||||
deptId: '',
|
||||
deptName: '',
|
||||
casualUser: 1,
|
||||
loginName: '',
|
||||
phone: '',
|
||||
limitIpEnd: '',
|
||||
limitTime: [1, 2],
|
||||
role: [],
|
||||
smsNotice: 0,
|
||||
emailNotice: 0,
|
||||
type: 0
|
||||
})
|
||||
const formRef = ref()
|
||||
const rules: Partial<Record<string, Arrayable<FormItemRule>>> = {
|
||||
name: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
role: [{ required: true, message: '请输入角色', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '请输入用户密码', trigger: 'blur' }],
|
||||
loginName: [{ required: true, message: '请输入登录名', trigger: 'blur' }],
|
||||
casualUser: [{ required: true, message: '请输入用户类型', trigger: 'blur' }],
|
||||
smsNotice: [{ required: true, message: '请输入短信通知', trigger: 'blur' }],
|
||||
emailNotice: [{ required: true, message: '请输入邮件通知', trigger: 'blur' }],
|
||||
email: [
|
||||
{ required: false, message: '请输入邮箱', trigger: 'blur' },
|
||||
{
|
||||
type: 'email',
|
||||
message: "'请输入正确的邮箱地址",
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
phone: [
|
||||
{ required: false, message: '请输入手机号', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
limitTime: [{ required: true, message: '请选择时间段', trigger: 'blur' }],
|
||||
limitIpStart: [
|
||||
{ required: true, message: '请输入起始IP', trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/
|
||||
let isCorrect = regexp.test(value)
|
||||
if (value == '') {
|
||||
return callback(new Error('请输入IP地址'))
|
||||
} else if (!isCorrect) {
|
||||
callback(new Error('请输入正确的IP地址'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
limitIpEnd: [
|
||||
{ required: true, message: '请输入结束IP', trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
validator: (rule: any, value: string, callback: any) => {
|
||||
let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/
|
||||
let isCorrect = regexp.test(value)
|
||||
if (value == '') {
|
||||
return callback(new Error('请输入IP地址'))
|
||||
} else if (!isCorrect) {
|
||||
callback(new Error('请输入正确的IP地址'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
const UserTypeOption = [
|
||||
{ label: '管理员', value: 1 },
|
||||
{ label: '普通用户', value: 2 }
|
||||
]
|
||||
const TypeOptions = [
|
||||
{ label: '临时用户', value: 0 },
|
||||
{ label: '长期用户', value: 1 }
|
||||
]
|
||||
const useId = ref(1)
|
||||
const roleOptions = ref<treeData>()
|
||||
const queryRole = () => {
|
||||
roleList(adminInfo.$state.userType).then((res: any) => {
|
||||
roleOptions.value = res.data.map((item: any) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
queryRole()
|
||||
const dialogVisible = ref(false)
|
||||
const title = ref('新增菜单')
|
||||
const open = (text: string, data?: anyObj) => {
|
||||
formRef.value?.resetFields()
|
||||
title.value = text
|
||||
dialogVisible.value = true
|
||||
|
||||
if (data) {
|
||||
for (let key in form) {
|
||||
form[key] = data[key]
|
||||
}
|
||||
form.limitTime = data.limitTime.split('-')
|
||||
form.role = data.roleList
|
||||
} else {
|
||||
for (let key in form) {
|
||||
form[key] = ''
|
||||
}
|
||||
form.casualUser = 1
|
||||
form.limitTime = [0, 24]
|
||||
form.role = []
|
||||
form.smsNotice = 0
|
||||
form.emailNotice = 0
|
||||
useId.value = 1
|
||||
form.id = ''
|
||||
form.limitIpStart = '0.0.0.0'
|
||||
form.limitIpEnd = '255.255.255.255'
|
||||
form.password = '123456'
|
||||
form.type = adminInfo.$state.userType + 1
|
||||
}
|
||||
}
|
||||
const submit = async () => {
|
||||
formRef.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
let obj = JSON.parse(JSON.stringify(form))
|
||||
obj.limitTime = obj.limitTime.join('-')
|
||||
delete obj.password
|
||||
if (form.id) {
|
||||
await edit(obj)
|
||||
ElMessage.success('修改成功')
|
||||
} else {
|
||||
form.type = adminInfo.$state.userType + 1
|
||||
await add(obj)
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
tableStore.index()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const changeValue = () => {}
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@change="sourceChange"
|
||||
:options="deviceTreeOptions"
|
||||
:show-all-levels="false"
|
||||
:props="{ checkStrictly: true }"
|
||||
:props="{ checkStrictly: true, value: 'id', label: 'name' }"
|
||||
clearable
|
||||
></el-cascader>
|
||||
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
|
||||
@@ -57,17 +57,33 @@ const tabsList = ref([
|
||||
])
|
||||
const rankOptions = ref([
|
||||
{
|
||||
value: '1',
|
||||
label: '1级'
|
||||
value: '1,7',
|
||||
label: '1级(ERROR)'
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: '2级'
|
||||
value: '2,6',
|
||||
label: '2级(WARN)'
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: '3级'
|
||||
}
|
||||
value: '3,4,5',
|
||||
label: '3级(DEBUG,NORMAL)'
|
||||
},
|
||||
// {
|
||||
// value: '4',
|
||||
// label: 'DEBUG'
|
||||
// },
|
||||
// {
|
||||
// value: '5',
|
||||
// label: 'NORMAL'
|
||||
// },
|
||||
// {
|
||||
// value: '6',
|
||||
// label: 'WARN'
|
||||
// },
|
||||
// {
|
||||
// value: '7',
|
||||
// label: 'ERROR'
|
||||
// }
|
||||
])
|
||||
|
||||
const tableStore = new TableStore({
|
||||
@@ -83,14 +99,15 @@ const tableStore = new TableStore({
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{ title: '设备名称', field: 'equipmentName', align: 'center' },
|
||||
{ title: '工程名称', field: 'engineeringName', align: 'center' },
|
||||
{ title: '项目名称', field: 'projectName', align: 'center' },
|
||||
{ title: '设备名称', field: 'equipmentName', align: 'center', width: 120 },
|
||||
{ title: '工程名称', field: 'engineeringName', align: 'center', width: 120 },
|
||||
{ title: '项目名称', field: 'projectName', align: 'center', width: 120 },
|
||||
{ title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true },
|
||||
{
|
||||
title: '模块信息',
|
||||
field: 'moduleNo',
|
||||
align: 'center',
|
||||
width: 100,
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue ? row.cellValue : '/'
|
||||
}
|
||||
@@ -107,7 +124,7 @@ const tableStore = new TableStore({
|
||||
},
|
||||
{
|
||||
title: '事件描述',
|
||||
minWidth: 220,
|
||||
minWidth: 250,
|
||||
field: 'showName'
|
||||
},
|
||||
{
|
||||
@@ -116,14 +133,24 @@ const tableStore = new TableStore({
|
||||
width: 100,
|
||||
render: 'tag',
|
||||
custom: {
|
||||
// 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR
|
||||
|
||||
1: 'danger',
|
||||
2: 'warning',
|
||||
3: 'success'
|
||||
3: 'success',
|
||||
4: 'warning',
|
||||
5: 'success',
|
||||
6: 'warning',
|
||||
7: 'danger'
|
||||
},
|
||||
replaceValue: {
|
||||
1: '1级',
|
||||
2: '2级',
|
||||
3: '3级'
|
||||
3: '3级',
|
||||
4: 'DEBUG',
|
||||
5: 'NORMAL',
|
||||
6: 'WARN',
|
||||
7: 'ERROR'
|
||||
}
|
||||
}
|
||||
// {
|
||||
@@ -135,7 +162,25 @@ const tableStore = new TableStore({
|
||||
// }
|
||||
],
|
||||
beforeSearchFun: () => {},
|
||||
|
||||
exportProcessingData: () => {
|
||||
tableStore.table.allData = tableStore.table.allData.filter(item => {
|
||||
item.level =
|
||||
item.level == 1
|
||||
? '1级'
|
||||
: item.level == 2
|
||||
? '2级'
|
||||
: item.level == 3
|
||||
? '3级'
|
||||
: item.level == 4
|
||||
? 'DEBUG'
|
||||
: item.level == 5
|
||||
? 'NORMAL'
|
||||
: item.level == 6
|
||||
? 'WARN'
|
||||
: 'ERROR'
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
provide('tableStore', tableStore)
|
||||
@@ -177,6 +222,7 @@ const sourceChange = (e: any) => {
|
||||
tableStore.table.params.deviceTypeId = e[0] || ''
|
||||
tableStore.table.params.engineeringid = e[1] || ''
|
||||
tableStore.table.params.projectId = e[2] || ''
|
||||
tableStore.table.params.deviceId = e[3] || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
<template>
|
||||
<TableHeader datePicker ref="refheader" showExport>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="关键词">
|
||||
<el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入前置服务器名称,ip" />
|
||||
<el-form-item label="关键字筛选">
|
||||
<el-input
|
||||
maxlength="32"
|
||||
show-word-limit
|
||||
v-model.trim="tableStore.table.params.searchValue"
|
||||
placeholder="请输入前置服务器名称,ip"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="级别">
|
||||
<el-select v-model.trim="tableStore.table.params.level" placeholder="请选择级别" clearable>
|
||||
<el-option
|
||||
v-for="item in rankOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
</template>
|
||||
</TableHeader>
|
||||
<!-- <div style="height: 300px;"> -->
|
||||
@@ -21,7 +35,36 @@ import { mainHeight } from '@/utils/layout'
|
||||
const props = defineProps(['deviceTree'])
|
||||
|
||||
const refheader = ref()
|
||||
|
||||
const rankOptions = ref([
|
||||
{
|
||||
value: '1,7',
|
||||
label: '1级(ERROR)'
|
||||
},
|
||||
{
|
||||
value: '2,6',
|
||||
label: '2级(WARN)'
|
||||
},
|
||||
{
|
||||
value: '3,4,5',
|
||||
label: '3级(DEBUG,NORMAL)'
|
||||
},
|
||||
// {
|
||||
// value: '4',
|
||||
// label: 'DEBUG'
|
||||
// },
|
||||
// {
|
||||
// value: '5',
|
||||
// label: 'NORMAL'
|
||||
// },
|
||||
// {
|
||||
// value: '6',
|
||||
// label: 'WARN'
|
||||
// },
|
||||
// {
|
||||
// value: '7',
|
||||
// label: 'ERROR'
|
||||
// }
|
||||
])
|
||||
const tableStore = new TableStore({
|
||||
url: '/cs-harmonic-boot/eventUser/frontWarnInfo',
|
||||
method: 'POST',
|
||||
@@ -35,33 +78,79 @@ const tableStore = new TableStore({
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{ title: '前置服务器名称', field: 'lineId', align: 'center' },
|
||||
{ title: '前置服务器ip', field: 'wavePath', align: 'center' },
|
||||
{ title: '进程号', field: 'clDid', align: 'center' },
|
||||
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 80, sortable: true },
|
||||
|
||||
{ title: '前置服务器名称', field: 'lineId', align: 'center', width: 120 },
|
||||
{ title: '前置服务器ip', field: 'wavePath', align: 'center', width: 120 },
|
||||
{ title: '进程号', field: 'clDid', align: 'center', width: 60 },
|
||||
{ title: '发生时刻', field: 'startTime', align: 'center', width: 180, sortable: true },
|
||||
|
||||
{
|
||||
title: '事件描述',
|
||||
field: 'tag',
|
||||
minWidth: 350
|
||||
},
|
||||
{
|
||||
{
|
||||
title: '告警代码',
|
||||
field: 'code',
|
||||
align: 'center',
|
||||
width: 100,
|
||||
|
||||
formatter: (row: any) => {
|
||||
return row.cellValue ? '\u200B' + row.cellValue : '/'
|
||||
return row.cellValue ? '\u200B' + row.cellValue : '/'
|
||||
},
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
title: '级别',
|
||||
field: 'level',
|
||||
width: 100,
|
||||
render: 'tag',
|
||||
custom: {
|
||||
// 1:Ⅰ级 2:Ⅱ级 3:Ⅲ级 4:DEBUG 5:NORMAL 6:WARN 7:ERROR
|
||||
|
||||
1: 'danger',
|
||||
2: 'warning',
|
||||
3: 'success',
|
||||
4: 'warning',
|
||||
5: 'success',
|
||||
6: 'warning',
|
||||
7: 'danger'
|
||||
},
|
||||
replaceValue: {
|
||||
1: '1级',
|
||||
2: '2级',
|
||||
3: '3级',
|
||||
4: 'DEBUG',
|
||||
5: 'NORMAL',
|
||||
6: 'WARN',
|
||||
7: 'ERROR'
|
||||
}
|
||||
}
|
||||
],
|
||||
beforeSearchFun: () => {}
|
||||
beforeSearchFun: () => {},
|
||||
exportProcessingData: () => {
|
||||
tableStore.table.allData = tableStore.table.allData.filter(item => {
|
||||
item.level =
|
||||
item.level == 1
|
||||
? '1级'
|
||||
: item.level == 2
|
||||
? '2级'
|
||||
: item.level == 3
|
||||
? '3级'
|
||||
: item.level == 4
|
||||
? 'DEBUG'
|
||||
: item.level == 5
|
||||
? 'NORMAL'
|
||||
: item.level == 6
|
||||
? 'WARN'
|
||||
: 'ERROR'
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
provide('tableStore', tableStore)
|
||||
tableStore.table.params.searchValue = ''
|
||||
tableStore.table.params.level = ''
|
||||
const deviceTreeOptions = ref<any>(props.deviceTree)
|
||||
deviceTreeOptions.value.map((item: any, index: any) => {
|
||||
if (item.children.length == 0) {
|
||||
@@ -75,6 +164,5 @@ onMounted(() => {
|
||||
setTimeout(() => {
|
||||
// tableStore.table.height = mainHeight(200).height as any
|
||||
}, 0)
|
||||
|
||||
</script>
|
||||
<style></style>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@change="sourceChange"
|
||||
:options="deviceTreeOptions"
|
||||
:show-all-levels="false"
|
||||
:props="{ checkStrictly: true }"
|
||||
:props="{ checkStrictly: true, value: 'id', label: 'name' }"
|
||||
clearable
|
||||
></el-cascader>
|
||||
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
|
||||
@@ -133,6 +133,7 @@ const sourceChange = (e: any) => {
|
||||
tableStore.table.params.deviceTypeId = e[0] || ''
|
||||
tableStore.table.params.engineeringid = e[1] || ''
|
||||
tableStore.table.params.projectId = e[2] || ''
|
||||
tableStore.table.params.deviceId = e[3] || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div ref="refheader" v-if="!isWaveCharts">
|
||||
<div ref="refheader" v-show="!isWaveCharts" style="width: 100%;">
|
||||
<TableHeader datePicker showExport>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="数据来源">
|
||||
@@ -10,7 +10,7 @@
|
||||
v-model.trim="tableStore.table.params.cascader"
|
||||
:options="deviceTreeOptions"
|
||||
:show-all-levels="false"
|
||||
:props="{ checkStrictly: true }"
|
||||
:props="{ checkStrictly: true, value: 'id', label: 'name' }"
|
||||
clearable
|
||||
></el-cascader>
|
||||
<!-- <el-input maxlength="32" show-word-limit v-model.trim="tableStore.table.params.searchValue" placeholder="请输入设备名称" /> -->
|
||||
@@ -121,28 +121,30 @@ const tableStore = new TableStore({
|
||||
method: 'POST',
|
||||
publicHeight: 65,
|
||||
exportName: '暂态事件',
|
||||
column: [ {
|
||||
column: [
|
||||
{
|
||||
title: '序号',
|
||||
width: 80,
|
||||
formatter: (row: any) => {
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{ title: '设备名称', field: 'equipmentName', align: 'center' },
|
||||
{ title: '工程名称', field: 'engineeringName', align: 'center' },
|
||||
{ title: '项目名称', field: 'projectName', align: 'center' },
|
||||
{ title: '发生时刻', field: 'startTime', align: 'center', width: '240',sortable: true },
|
||||
{ title: '监测点名称', field: 'lineName', align: 'center' },
|
||||
{ title: '事件描述', field: 'showName', align: 'center' },
|
||||
{ title: '事件发生位置', field: 'evtParamPosition', align: 'center' },
|
||||
{ title: '相别', field: 'evtParamPhase', align: 'center' },
|
||||
{ title: '持续时间(s)', field: 'evtParamTm', align: 'center',sortable: true },
|
||||
{ title: '暂降(聚升)幅值(%)', minWidth: 100, field: 'evtParamVVaDepth', align: 'center',sortable: true },
|
||||
{ title: '设备名称', field: 'equipmentName', minWidth: 120, align: 'center' },
|
||||
{ title: '工程名称', field: 'engineeringName', minWidth: 120, align: 'center' },
|
||||
{ title: '项目名称', field: 'projectName', minWidth: 120, align: 'center' },
|
||||
{ title: '发生时刻', field: 'startTime', align: 'center', minWidth: 180, sortable: true },
|
||||
{ title: '监测点名称', field: 'lineName', minWidth: 120, align: 'center' },
|
||||
{ title: '事件描述', field: 'showName', minWidth: 120, align: 'center' },
|
||||
{ title: '事件发生位置', field: 'evtParamPosition', minWidth: 150, align: 'center' },
|
||||
{ title: '相别', field: 'evtParamPhase', minWidth: 80, align: 'center' },
|
||||
{ title: '持续时间(s)', field: 'evtParamTm', minWidth: 80, align: 'center', sortable: true },
|
||||
{ title: '暂降(聚升)幅值(%)', minWidth: 100, field: 'evtParamVVaDepth', align: 'center', sortable: true },
|
||||
|
||||
{
|
||||
title: '操作',
|
||||
title: '操作', fixed: 'right',
|
||||
align: 'center',
|
||||
width: '180',
|
||||
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
@@ -153,7 +155,7 @@ const tableStore = new TableStore({
|
||||
render: 'basicButton',
|
||||
loading: 'loading1',
|
||||
disabled: row => {
|
||||
return !row.wavePath
|
||||
return !row.wavePath
|
||||
},
|
||||
click: async row => {
|
||||
row.loading1 = true
|
||||
@@ -164,9 +166,10 @@ const tableStore = new TableStore({
|
||||
row.loading1 = false
|
||||
if (res != undefined) {
|
||||
boxoList.value = row
|
||||
boxoList.value.persistTime = row.evtParamTm
|
||||
boxoList.value.featureAmplitude =
|
||||
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
|
||||
boxoList.value.systemType = 'WX'
|
||||
row.evtParamVVaDepth != '-' ? (row.evtParamVVaDepth - 0) / 100 : null
|
||||
boxoList.value.systemType = 'YPT'
|
||||
wp.value = res.data
|
||||
}
|
||||
loading.value = false
|
||||
@@ -234,24 +237,24 @@ const tableStore = new TableStore({
|
||||
icon: 'el-icon-DataLine',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return row.showName != '未知';
|
||||
return row.showName != '未知'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
title: '波形补召',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Check',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return row.wavePath || row.showName === '未知';
|
||||
},
|
||||
click: row => {
|
||||
getFileByEventId(row.id).then(res => {
|
||||
name: 'edit',
|
||||
title: '波形补召',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Check',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return row.wavePath || row.showName === '未知'
|
||||
},
|
||||
click: row => {
|
||||
getFileByEventId(row.id).then(res => {
|
||||
ElMessage.success(res.message)
|
||||
tableStore.index()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -312,8 +315,8 @@ const sourceChange = (e: any) => {
|
||||
tableStore.table.params.deviceTypeId = e[0] || ''
|
||||
tableStore.table.params.engineeringid = e[1] || ''
|
||||
tableStore.table.params.projectId = e[2] || ''
|
||||
tableStore.table.params.deviceId = e[3] || ''
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// tableStore.table.params.engineeringid = e[1] || ''
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
<el-tab-pane label="前置告警" name="2">
|
||||
<Front v-if="activeName == '2'" :deviceTree="deviceTree" :key="key" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="稳态越限告警" name="3">
|
||||
<!-- <el-tab-pane label="稳态越限告警" name="3">
|
||||
<Steady v-if="activeName == '3'" :deviceTree="deviceTree" :key="key" />
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
<el-tab-pane label="暂态事件" name="4">
|
||||
<Transient v-if="activeName == '4'" :deviceTree="deviceTree" :key="key" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="异常事件" name="5">
|
||||
<!-- <el-tab-pane label="异常事件" name="5">
|
||||
<Abnormal v-if="activeName == '5'" :deviceTree="deviceTree" :key="key" />
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
@@ -31,24 +31,25 @@ defineOptions({
|
||||
name: 'govern/alarm/index'
|
||||
})
|
||||
const deviceTree = ref([])
|
||||
const activeName = ref('1')
|
||||
const activeName = ref('0')
|
||||
const key = ref(0)
|
||||
getDeviceTree().then(res => {
|
||||
res.data.forEach((item: any) => {
|
||||
item.value = item.id
|
||||
item.label = item.name
|
||||
item.children.forEach((child: any) => {
|
||||
child.value = child.id
|
||||
child.label = child.name
|
||||
child.children.forEach((grand: any) => {
|
||||
grand.value = grand.id
|
||||
grand.label = grand.name
|
||||
delete grand.children
|
||||
})
|
||||
})
|
||||
})
|
||||
// res.data.forEach((item: any) => {
|
||||
// item.value = item.id
|
||||
// item.label = item.name
|
||||
// item.children.forEach((child: any) => {
|
||||
// child.value = child.id
|
||||
// child.label = child.name
|
||||
// child.children.forEach((grand: any) => {
|
||||
// grand.value = grand.id
|
||||
// grand.label = grand.name
|
||||
// delete grand.children
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
deviceTree.value = res.data
|
||||
key.value += 1
|
||||
activeName.value = '1'
|
||||
})
|
||||
onMounted(() => { })
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
v-model.trim="formInline.statisticalId"
|
||||
filterable
|
||||
@change="frequencyFlag"
|
||||
placeholder="请选择"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in zblist"
|
||||
@@ -158,6 +158,7 @@ const nodeClick = async (e: anyObj) => {
|
||||
getDevCapacity(formInline.devId)
|
||||
.then(res => {
|
||||
devCapacity.value = res.data
|
||||
|
||||
search()
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -1,294 +1,315 @@
|
||||
<template>
|
||||
<div class="default-main">
|
||||
<div class="analyze-dvr" v-show="!isWaveCharts" :style="{ height: pageHeight.height }" v-loading="loading">
|
||||
<DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange="deviceTypeChange"></DeviceTree>
|
||||
<div class="analyze-dvr-right" v-if="tableStore.table.params.deviceId">
|
||||
<TableHeader datePicker showExport>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="事件类型">
|
||||
<el-select
|
||||
v-model.trim="tableStore.table.params.eventType"
|
||||
clearable
|
||||
placeholder="请选择事件类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in eventList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="位置">
|
||||
<el-select
|
||||
v-model.trim="tableStore.table.params.location"
|
||||
clearable
|
||||
placeholder="请选择位置"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in locationList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</TableHeader>
|
||||
<Table v-if="view" ref="tableRef"></Table>
|
||||
</div>
|
||||
<el-empty v-else description="请选择设备" class="analyze-dvr-right" />
|
||||
</div>
|
||||
<waveFormAnalysis
|
||||
v-loading="loading"
|
||||
v-if="isWaveCharts"
|
||||
ref="waveFormAnalysisRef"
|
||||
@handleHideCharts="isWaveCharts = false"
|
||||
:wp="wp"
|
||||
style="padding: 10px"
|
||||
/>
|
||||
<!-- <div :style="{ height: pageHeight.height }" style="padding: 10px; overflow: hidden" v-if="!view">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<div v-if="view2" style="display: flex">
|
||||
<el-radio-group v-model.trim="value" @change="changeView">
|
||||
<el-radio-button label="一次值" :value="1" />
|
||||
<el-radio-button label="二次值" :value="2" />
|
||||
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button v-if="view2" @click="backbxlb" class="el-icon-refresh-right" icon="el-icon-Back"
|
||||
style="float: right">
|
||||
返回
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-tabs v-if="view2" class="default-main" v-model.trim="bxactiveName" @tab-click="bxhandleClick">
|
||||
<el-tab-pane label="瞬时波形" name="ssbx" class="boxbx pt10 pb10"
|
||||
:style="'height:' + bxecharts + ';overflow-y: scroll;'">
|
||||
<shushiboxi v-if="bxactiveName == 'ssbx' && showBoxi" :value="value" :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 v-if="bxactiveName == 'rmsbx' && showBoxi" :value="value" :boxoList="boxoList" :wp="wp">
|
||||
</rmsboxi>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick, provide, onMounted } from 'vue'
|
||||
import { mainHeight } from '@/utils/layout'
|
||||
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
|
||||
import { analyseWave } from '@/api/common'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
|
||||
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
|
||||
import { ElMessage } from 'element-plus'
|
||||
defineOptions({
|
||||
name: 'govern/analyze/DVR/index'
|
||||
})
|
||||
const pageHeight = mainHeight(20)
|
||||
const loading = ref(false)
|
||||
const view = ref(true)
|
||||
const view2 = ref(false)
|
||||
const showBoxi = ref(true)
|
||||
const isWaveCharts = ref(false)
|
||||
const bxactiveName = ref('ssbx')
|
||||
const boxoList: any = ref({})
|
||||
const wp = ref({})
|
||||
const eventList = ref([
|
||||
{
|
||||
value: 'Evt_Sys_DipStr',
|
||||
label: '电压暂降'
|
||||
},
|
||||
{
|
||||
value: 'Evt_Sys_SwlStr',
|
||||
label: '电压暂升'
|
||||
},
|
||||
{
|
||||
value: 'Evt_Sys_IntrStr',
|
||||
label: '电压中断'
|
||||
}
|
||||
])
|
||||
const locationList = ref([
|
||||
{
|
||||
value: 'grid',
|
||||
label: '电网侧'
|
||||
},
|
||||
{
|
||||
value: 'load',
|
||||
label: '负载侧'
|
||||
}
|
||||
])
|
||||
const waveFormAnalysisRef = ref()
|
||||
|
||||
const tableStore = new TableStore({
|
||||
url: '/cs-harmonic-boot/eventUser/queryEventpageWeb',
|
||||
method: 'POST',
|
||||
column: [
|
||||
{ title: '事件描述', field: 'showName' },
|
||||
{ title: '发生位置', field: 'evtParamPosition' },
|
||||
{ title: '持续时间(s)', field: 'evtParamTm', sortable: true },
|
||||
{
|
||||
title: '暂降(聚升)幅值(%)',
|
||||
field: 'evtParamVVaDepth',
|
||||
formatter: (row: any) => {
|
||||
let a = row.cellValue.split('%')[0] - 0
|
||||
console.log('🚀 ~ a:', a)
|
||||
return a ? a.toFixed(2) : '/'
|
||||
}, sortable: true
|
||||
},
|
||||
{ title: '发生时刻', field: 'startTime', sortable: true },
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
text: '波形分析',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-DataLine',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return !row.wavePath && row.evtParamTm < 20
|
||||
},
|
||||
|
||||
click: async row => {
|
||||
row.loading1 = true
|
||||
loading.value = true
|
||||
isWaveCharts.value = true
|
||||
await analyseWave(row.id)
|
||||
.then(res => {
|
||||
row.loading1 = false
|
||||
if (res != undefined) {
|
||||
boxoList.value = row
|
||||
boxoList.value.featureAmplitude =
|
||||
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth - 0 : null
|
||||
// boxoList.value.systemType = 'WX'
|
||||
wp.value = res.data
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
row.loading1 = false
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
waveFormAnalysisRef.value &&
|
||||
waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
|
||||
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(false, 150)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
text: '暂无波形',
|
||||
type: 'info',
|
||||
icon: 'el-icon-DataLine',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return !(!row.wavePath && row.evtParamTm < 20)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
title: '波形下载',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Check',
|
||||
loading: 'loading2',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
// && row.evtParamTm < 20
|
||||
return !row.wavePath
|
||||
},
|
||||
click: row => {
|
||||
getFileZip({ eventId: row.id }).then(res => {
|
||||
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a') // 创建a标签
|
||||
link.href = url
|
||||
link.download = row.wavePath.split('/')[2] || '波形文件' // 设置下载的文件名
|
||||
document.body.appendChild(link)
|
||||
link.click() //执行下载
|
||||
document.body.removeChild(link) //释放标签
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
tableStore.table.data.forEach((item: any) => {
|
||||
item.loading = false
|
||||
item.evtParamTm = item.evtParamTm.split('s')[0]
|
||||
})
|
||||
}
|
||||
})
|
||||
const flag = ref(false)
|
||||
tableStore.table.params.type = 0
|
||||
tableStore.table.params.eventType = ''
|
||||
tableStore.table.params.location = ''
|
||||
provide('tableStore', tableStore)
|
||||
const deviceTypeChange = (val: any, obj: any) => {
|
||||
flag.value = true
|
||||
nodeClick(obj)
|
||||
}
|
||||
const nodeClick = async (e: anyObj) => {
|
||||
// console.log("🚀 ~ nodeClick ~ e:", e)
|
||||
if (e.level == 2&& flag.value) {
|
||||
loading.value = false
|
||||
tableStore.table.params.deviceId = e.id
|
||||
nextTick(() => {
|
||||
tableStore.index()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const changeView = () => {
|
||||
showBoxi.value = false
|
||||
setTimeout(() => {
|
||||
showBoxi.value = true
|
||||
}, 0)
|
||||
}
|
||||
const bxhandleClick = (tab: any) => {
|
||||
if (tab.name == 'ssbx') {
|
||||
bxactiveName.value = 'ssbx'
|
||||
} else if (tab.name == 'rmsbx') {
|
||||
bxactiveName.value = 'rmsbx'
|
||||
}
|
||||
// console.log(tab, event);
|
||||
}
|
||||
const backbxlb = () => {
|
||||
view.value = true
|
||||
view2.value = false
|
||||
}
|
||||
const bxecharts = mainHeight(95).height as any
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.analyze-dvr {
|
||||
display: flex;
|
||||
|
||||
&-right {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
padding: 10px 10px 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="default-main">
|
||||
<div class="analyze-dvr" v-show="!isWaveCharts" :style="{ height: pageHeight.height }" v-loading="loading">
|
||||
<DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange="deviceTypeChange"></DeviceTree>
|
||||
<div class="analyze-dvr-right" v-if="tableStore.table.params.deviceId">
|
||||
<TableHeader datePicker showExport>
|
||||
<template v-slot:select>
|
||||
<el-form-item label="事件类型">
|
||||
<el-select
|
||||
v-model.trim="tableStore.table.params.eventType"
|
||||
clearable
|
||||
placeholder="请选择事件类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in eventList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="位置">
|
||||
<el-select
|
||||
v-model.trim="tableStore.table.params.location"
|
||||
clearable
|
||||
placeholder="请选择位置"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in locationList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</TableHeader>
|
||||
<Table v-if="view" ref="tableRef"></Table>
|
||||
</div>
|
||||
<el-empty v-else description="请选择设备" class="analyze-dvr-right" />
|
||||
</div>
|
||||
<waveFormAnalysis
|
||||
v-loading="loading"
|
||||
v-if="isWaveCharts"
|
||||
ref="waveFormAnalysisRef"
|
||||
@handleHideCharts="isWaveCharts = false"
|
||||
:wp="wp"
|
||||
style="padding: 10px"
|
||||
/>
|
||||
<!-- <div :style="{ height: pageHeight.height }" style="padding: 10px; overflow: hidden" v-if="!view">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<div v-if="view2" style="display: flex">
|
||||
<el-radio-group v-model.trim="value" @change="changeView">
|
||||
<el-radio-button label="一次值" :value="1" />
|
||||
<el-radio-button label="二次值" :value="2" />
|
||||
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button v-if="view2" @click="backbxlb" class="el-icon-refresh-right" icon="el-icon-Back"
|
||||
style="float: right">
|
||||
返回
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-tabs v-if="view2" class="default-main" v-model.trim="bxactiveName" @tab-click="bxhandleClick">
|
||||
<el-tab-pane label="瞬时波形" name="ssbx" class="boxbx pt10 pb10"
|
||||
:style="'height:' + bxecharts + ';overflow-y: scroll;'">
|
||||
<shushiboxi v-if="bxactiveName == 'ssbx' && showBoxi" :value="value" :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 v-if="bxactiveName == 'rmsbx' && showBoxi" :value="value" :boxoList="boxoList" :wp="wp">
|
||||
</rmsboxi>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick, provide, onMounted } from 'vue'
|
||||
import { mainHeight } from '@/utils/layout'
|
||||
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import waveFormAnalysis from '@/views/govern/device/control/tabs/components/waveFormAnalysis.vue'
|
||||
import { analyseWave } from '@/api/common'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
|
||||
import { getFileZip } from '@/api/cs-harmonic-boot/datatrend'
|
||||
import { ElMessage } from 'element-plus'
|
||||
defineOptions({
|
||||
name: 'govern/analyze/DVR/index'
|
||||
})
|
||||
const pageHeight = mainHeight(20)
|
||||
const loading = ref(false)
|
||||
const view = ref(true)
|
||||
const view2 = ref(false)
|
||||
const showBoxi = ref(true)
|
||||
const isWaveCharts = ref(false)
|
||||
const bxactiveName = ref('ssbx')
|
||||
const boxoList: any = ref({})
|
||||
const wp = ref({})
|
||||
const eventList = ref([
|
||||
{
|
||||
value: 'Evt_Sys_DipStr',
|
||||
label: '电压暂降'
|
||||
},
|
||||
{
|
||||
value: 'Evt_Sys_SwlStr',
|
||||
label: '电压暂升'
|
||||
},
|
||||
{
|
||||
value: 'Evt_Sys_IntrStr',
|
||||
label: '电压中断'
|
||||
}
|
||||
])
|
||||
const locationList = ref([
|
||||
{
|
||||
value: 'grid',
|
||||
label: '电网侧'
|
||||
},
|
||||
{
|
||||
value: 'load',
|
||||
label: '负载侧'
|
||||
}
|
||||
])
|
||||
const waveFormAnalysisRef = ref()
|
||||
|
||||
const tableStore = new TableStore({
|
||||
url: '/cs-harmonic-boot/eventUser/queryEventpageWeb',
|
||||
method: 'POST',
|
||||
column: [
|
||||
{
|
||||
field: 'index',
|
||||
title: '序号',
|
||||
width: '80',
|
||||
formatter: (row: any) => {
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{ title: '事件描述', field: 'showName', minWidth: 150 },
|
||||
{ title: '发生位置', field: 'evtParamPosition', minWidth: 150 },
|
||||
{
|
||||
title: '持续时间(s)',
|
||||
field: 'evtParamTm',
|
||||
sortable: true,
|
||||
minWidth: 110,
|
||||
formatter: (row: any) => {
|
||||
return Math.floor(row.cellValue * 10000) / 100
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '暂降(聚升)幅值(%)',
|
||||
field: 'evtParamVVaDepth',
|
||||
minWidth: 150,
|
||||
formatter: (row: any) => {
|
||||
let a = row.cellValue.split('%')[0] - 0
|
||||
return a ? a.toFixed(2) : '/'
|
||||
},
|
||||
sortable: true
|
||||
},
|
||||
{ title: '发生时刻', field: 'startTime', sortable: true, minWidth: 180 },
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
text: '波形分析',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-DataLine',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return !row.wavePath && row.evtParamTm < 20
|
||||
},
|
||||
|
||||
click: async row => {
|
||||
row.loading1 = true
|
||||
loading.value = true
|
||||
isWaveCharts.value = true
|
||||
await analyseWave(row.id)
|
||||
.then(res => {
|
||||
row.loading1 = false
|
||||
if (res != undefined) {
|
||||
boxoList.value = row
|
||||
boxoList.value.featureAmplitude =
|
||||
row.evtParamVVaDepth != '-' ? row.evtParamVVaDepth.split('%')[0] / 100 : null
|
||||
boxoList.value.persistTime =
|
||||
row.evtParamTm != '-' ? Math.floor(row.evtParamTm * 10000) / 100 : null
|
||||
// boxoList.value.systemType = 'WX'
|
||||
boxoList.value.systemType = 'YPT'
|
||||
wp.value = res.data
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
row.loading1 = false
|
||||
loading.value = false
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
waveFormAnalysisRef.value &&
|
||||
waveFormAnalysisRef.value.getWpData(wp.value, boxoList.value, true)
|
||||
waveFormAnalysisRef.value && waveFormAnalysisRef.value.setHeight(false, 150)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
text: '暂无波形',
|
||||
type: 'info',
|
||||
icon: 'el-icon-DataLine',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
return !(!row.wavePath && row.evtParamTm < 20)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
title: '波形下载',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-Check',
|
||||
loading: 'loading2',
|
||||
render: 'basicButton',
|
||||
disabled: row => {
|
||||
// && row.evtParamTm < 20
|
||||
return !row.wavePath
|
||||
},
|
||||
click: row => {
|
||||
getFileZip({ eventId: row.id }).then(res => {
|
||||
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a') // 创建a标签
|
||||
link.href = url
|
||||
link.download = row.wavePath.split('/')[2] || '波形文件' // 设置下载的文件名
|
||||
document.body.appendChild(link)
|
||||
link.click() //执行下载
|
||||
document.body.removeChild(link) //释放标签
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
tableStore.table.data.forEach((item: any) => {
|
||||
item.loading = false
|
||||
item.evtParamTm = item.evtParamTm.split('s')[0]
|
||||
})
|
||||
}
|
||||
})
|
||||
const flag = ref(false)
|
||||
tableStore.table.params.type = 0
|
||||
tableStore.table.params.eventType = ''
|
||||
tableStore.table.params.location = ''
|
||||
provide('tableStore', tableStore)
|
||||
const deviceTypeChange = (val: any, obj: any) => {
|
||||
flag.value = true
|
||||
nodeClick(obj)
|
||||
}
|
||||
const nodeClick = async (e: anyObj) => {
|
||||
// console.log("🚀 ~ nodeClick ~ e:", e)
|
||||
if (e.level == 2 && flag.value) {
|
||||
loading.value = false
|
||||
tableStore.table.params.deviceId = e.id
|
||||
nextTick(() => {
|
||||
tableStore.index()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const changeView = () => {
|
||||
showBoxi.value = false
|
||||
setTimeout(() => {
|
||||
showBoxi.value = true
|
||||
}, 0)
|
||||
}
|
||||
const bxhandleClick = (tab: any) => {
|
||||
if (tab.name == 'ssbx') {
|
||||
bxactiveName.value = 'ssbx'
|
||||
} else if (tab.name == 'rmsbx') {
|
||||
bxactiveName.value = 'rmsbx'
|
||||
}
|
||||
// console.log(tab, event);
|
||||
}
|
||||
const backbxlb = () => {
|
||||
view.value = true
|
||||
view2.value = false
|
||||
}
|
||||
const bxecharts = mainHeight(95).height as any
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.analyze-dvr {
|
||||
display: flex;
|
||||
|
||||
&-right {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
padding: 10px 10px 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,176 +1,176 @@
|
||||
<!-- 补召日志 -->
|
||||
<template>
|
||||
<el-dialog modal-class="analysisList" v-model.trim="dialogVisible" title="补召日志" width="70%" draggable
|
||||
@closed="close">
|
||||
<TableHeader date-picker :showReset="false">
|
||||
<template #operation>
|
||||
<el-button type="primary" icon="el-icon-Connection" @click="handleImport">
|
||||
离线补召
|
||||
</el-button>
|
||||
<el-button type="primary" icon="el-icon-Monitor" @click="handleaddDevice">
|
||||
在线补召
|
||||
</el-button>
|
||||
</template>
|
||||
</TableHeader>
|
||||
<Table ref="tableRef" />
|
||||
</el-dialog>
|
||||
<popup ref="detailRef"></popup>
|
||||
<!-- 离线数据导入组件 -->
|
||||
<!-- <offLineDataImport ref="offLineDataImportRef"></offLineDataImport> -->
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, provide, onBeforeUnmount } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
import offLineDataImport from '../offLineDataImport/index.vue'
|
||||
import popup from './popup.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
defineOptions({
|
||||
name: 'offLineDataImport'
|
||||
})
|
||||
const emit = defineEmits(['back'])
|
||||
const dialogVisible = ref(false)
|
||||
const height = ref(0)
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
const detailRef: any = ref()
|
||||
const lineId = ref('')
|
||||
const deviceId = ref('')
|
||||
const deviceData = ref({})
|
||||
const { push, options, currentRoute } = useRouter()
|
||||
const tableStore: any = new TableStore({
|
||||
url: '/cs-device-boot/portableOfflLog/queryMainLogPage',
|
||||
publicHeight: 420,
|
||||
method: 'POST',
|
||||
column: [
|
||||
// { width: '60', type: 'checkbox', fixed: 'left' },
|
||||
{
|
||||
title: '序号', width: 80, formatter: (row: any) => {
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'projectName',
|
||||
title: '工程名称',
|
||||
minWidth: 170,
|
||||
formatter: row => {
|
||||
return row.cellValue ? row.cellValue : '/'
|
||||
}
|
||||
},
|
||||
{ field: 'successCount', title: '成功解析数', minWidth: 150 },
|
||||
{ field: 'startTime', title: '导入开始时间', minWidth: 170, sortable: true },
|
||||
{ field: 'endTime', title: '导入结束时间', minWidth: 170 , sortable: true},
|
||||
{
|
||||
title: '解析状态',
|
||||
field: 'status',
|
||||
width: 100,
|
||||
render: 'tag',
|
||||
custom: {
|
||||
0: 'warning',
|
||||
1: 'success',
|
||||
2: 'danger',
|
||||
3: 'primary'
|
||||
},
|
||||
replaceValue: {
|
||||
0: '未解析',
|
||||
1: '解析成功',
|
||||
2: '解析失败',
|
||||
3: '文件不存在'
|
||||
}
|
||||
// formatter: row => {
|
||||
// return row.cellValue == 1 ? '未注册' : row.cellValue == 2 ? '注册' : '接入'
|
||||
// },
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: '100',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '详情',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
// console.log(row.portableOfflLogList)
|
||||
detailRef.value.open(row.portableOfflLogList)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
beforeSearchFun: () => {
|
||||
// tableStore.table.params.devId = tableParams.value.devId
|
||||
// tableStore.table.params.lineId = tableParams.value.lineId
|
||||
// tableStore.table.params.list = tableParams.value.list
|
||||
// tableStore.table.params.type = 3
|
||||
},
|
||||
loadCallback: () => {
|
||||
// tableStore.table.data=[]
|
||||
tableStore.table.height = 400
|
||||
// console.log(tableStore.table.publicHeight, 'tableStore.table.data')
|
||||
}
|
||||
})
|
||||
provide('tableStore', tableStore)
|
||||
//返回
|
||||
const handleBack = () => {
|
||||
emit('back')
|
||||
}
|
||||
const open = (row: any) => {
|
||||
lineId.value = row.lineId
|
||||
deviceData.value = row.deviceData
|
||||
deviceId.value = row.deviceId
|
||||
dialogVisible.value = true
|
||||
setTimeout(() => {
|
||||
tableStore.index()
|
||||
}, 10)
|
||||
}
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const updateViewportHeight = async () => {
|
||||
// height.value = window.innerHeight;
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
// tableStore.table.publicHeight = height.value
|
||||
// await tableStore.index()
|
||||
}
|
||||
//设备补召
|
||||
const handleaddDevice = () => {
|
||||
push({
|
||||
path: '/supplementaryRecruitment',
|
||||
query: {
|
||||
activeName: '0',
|
||||
id: lineId.value,
|
||||
ndid: deviceData.value?.ndid,
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
const offLineDataImportRef = ref()
|
||||
const handleImport = () => {
|
||||
//设备devId&监测点lineId带入组件
|
||||
// offLineDataImportRef.value && offLineDataImportRef.value.open(deviceId.value, lineId.value)
|
||||
push({
|
||||
path: '/supplementaryRecruitment',
|
||||
query: {
|
||||
activeName: '1',
|
||||
lineId: lineId.value,
|
||||
deviceId: deviceId.value,
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateViewportHeight() // 初始化视口高度
|
||||
window.addEventListener('resize', updateViewportHeight) // 监听窗口大小变化
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateViewportHeight) // 移除监听
|
||||
})
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
<!-- 补召日志 -->
|
||||
<template>
|
||||
<el-dialog modal-class="analysisList" v-model.trim="dialogVisible" title="补召日志" width="70%" draggable
|
||||
@closed="close">
|
||||
<TableHeader date-picker :showReset="false">
|
||||
<template #operation>
|
||||
<el-button type="primary" icon="el-icon-Connection" @click="handleImport">
|
||||
离线补召
|
||||
</el-button>
|
||||
<el-button type="primary" icon="el-icon-Monitor" @click="handleaddDevice">
|
||||
在线补召
|
||||
</el-button>
|
||||
</template>
|
||||
</TableHeader>
|
||||
<Table ref="tableRef" :height="`calc(45vh - 50px)`"/>
|
||||
</el-dialog>
|
||||
<popup ref="detailRef"></popup>
|
||||
<!-- 离线数据导入组件 -->
|
||||
<!-- <offLineDataImport ref="offLineDataImportRef"></offLineDataImport> -->
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, provide, onBeforeUnmount } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import Table from '@/components/table/index.vue'
|
||||
import TableHeader from '@/components/table/header/index.vue'
|
||||
import offLineDataImport from '../offLineDataImport/index.vue'
|
||||
import popup from './popup.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
defineOptions({
|
||||
name: 'offLineDataImport'
|
||||
})
|
||||
const emit = defineEmits(['back'])
|
||||
const dialogVisible = ref(false)
|
||||
const height = ref(0)
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
const detailRef: any = ref()
|
||||
const lineId = ref('')
|
||||
const deviceId = ref('')
|
||||
const deviceData = ref({})
|
||||
const { push, options, currentRoute } = useRouter()
|
||||
const tableStore: any = new TableStore({
|
||||
url: '/cs-device-boot/portableOfflLog/queryMainLogPage',
|
||||
publicHeight: 420,
|
||||
method: 'POST',
|
||||
column: [
|
||||
// { width: '60', type: 'checkbox', fixed: 'left' },
|
||||
{
|
||||
title: '序号', width: 80, formatter: (row: any) => {
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'projectName',
|
||||
title: '工程名称',
|
||||
minWidth: 170,
|
||||
formatter: row => {
|
||||
return row.cellValue ? row.cellValue : '/'
|
||||
}
|
||||
},
|
||||
{ field: 'successCount', title: '成功解析数', minWidth: 150 },
|
||||
{ field: 'startTime', title: '导入开始时间', minWidth: 170, sortable: true },
|
||||
{ field: 'endTime', title: '导入结束时间', minWidth: 170 , sortable: true},
|
||||
{
|
||||
title: '解析状态',
|
||||
field: 'status',
|
||||
width: 100,
|
||||
render: 'tag',
|
||||
custom: {
|
||||
0: 'warning',
|
||||
1: 'success',
|
||||
2: 'danger',
|
||||
3: 'primary'
|
||||
},
|
||||
replaceValue: {
|
||||
0: '未解析',
|
||||
1: '解析成功',
|
||||
2: '解析失败',
|
||||
3: '文件不存在'
|
||||
}
|
||||
// formatter: row => {
|
||||
// return row.cellValue == 1 ? '未注册' : row.cellValue == 2 ? '注册' : '接入'
|
||||
// },
|
||||
},
|
||||
{
|
||||
title: '操作', fixed: 'right',
|
||||
width: '100',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '详情',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
// console.log(row.portableOfflLogList)
|
||||
detailRef.value.open(row.portableOfflLogList)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
beforeSearchFun: () => {
|
||||
// tableStore.table.params.devId = tableParams.value.devId
|
||||
// tableStore.table.params.lineId = tableParams.value.lineId
|
||||
// tableStore.table.params.list = tableParams.value.list
|
||||
// tableStore.table.params.type = 3
|
||||
},
|
||||
loadCallback: () => {
|
||||
// tableStore.table.data=[]
|
||||
tableStore.table.height = 400
|
||||
// console.log(tableStore.table.publicHeight, 'tableStore.table.data')
|
||||
}
|
||||
})
|
||||
provide('tableStore', tableStore)
|
||||
//返回
|
||||
const handleBack = () => {
|
||||
emit('back')
|
||||
}
|
||||
const open = (row: any) => {
|
||||
lineId.value = row.lineId
|
||||
deviceData.value = row.deviceData
|
||||
deviceId.value = row.deviceId
|
||||
dialogVisible.value = true
|
||||
setTimeout(() => {
|
||||
tableStore.index()
|
||||
}, 10)
|
||||
}
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const updateViewportHeight = async () => {
|
||||
// height.value = window.innerHeight;
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
// tableStore.table.publicHeight = height.value
|
||||
// await tableStore.index()
|
||||
}
|
||||
//设备补召
|
||||
const handleaddDevice = () => {
|
||||
push({
|
||||
path: '/supplementaryRecruitment',
|
||||
query: {
|
||||
activeName: '0',
|
||||
id: lineId.value,
|
||||
ndid: deviceData.value?.ndid,
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
const offLineDataImportRef = ref()
|
||||
const handleImport = () => {
|
||||
//设备devId&监测点lineId带入组件
|
||||
// offLineDataImportRef.value && offLineDataImportRef.value.open(deviceId.value, lineId.value)
|
||||
push({
|
||||
path: '/supplementaryRecruitment',
|
||||
query: {
|
||||
activeName: '1',
|
||||
lineId: lineId.value,
|
||||
deviceId: deviceId.value,
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateViewportHeight() // 初始化视口高度
|
||||
window.addEventListener('resize', updateViewportHeight) // 监听窗口大小变化
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateViewportHeight) // 移除监听
|
||||
})
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,123 +1,123 @@
|
||||
<!-- 解析列表 -->
|
||||
<template>
|
||||
<el-dialog v-model.trim="dialogVisible" title="详情" width="70%" draggable @closed="close">
|
||||
<div :style="tableHeight">
|
||||
<vxe-table border auto-resize height="auto" :data="tableData" v-bind="defaultAttribute">
|
||||
<vxe-column field="name" align="center" title="文件名称"></vxe-column>
|
||||
<vxe-column field="createTime" align="center" title="导入时间"></vxe-column>
|
||||
<vxe-column field="allCount" align="center" title="数据总数(条)" width="120"></vxe-column>
|
||||
<vxe-column field="realCount" align="center" title="已入库总数(条)" width="120"></vxe-column>
|
||||
<vxe-column field="state" align="center" title="解析状态" width="100">
|
||||
<template v-slot:default="scoped">
|
||||
<el-tag type="warning" v-if="scoped.row.state == 0">未解析</el-tag>
|
||||
<el-tag type="success" v-if="scoped.row.state == 1">解析成功</el-tag>
|
||||
<el-tag type="danger" v-if="scoped.row.state == 2">解析失败</el-tag>
|
||||
<el-tag type="primary" v-if="scoped.row.state == 3">文件不存在</el-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { defaultAttribute } from '@/components/table/defaultAttribute'
|
||||
import { mainHeight } from '@/utils/layout'
|
||||
const emit = defineEmits(['back'])
|
||||
const dialogVisible = ref(false)
|
||||
const tableHeight = mainHeight(440)
|
||||
const height = ref(0)
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
|
||||
const tableStore: any = new TableStore({
|
||||
url: '',
|
||||
// publicHeight: height.value,
|
||||
showPage: false,
|
||||
column: [
|
||||
{ width: '60', type: 'checkbox', fixed: 'left' },
|
||||
{
|
||||
title: '序号', width: 80, formatter: (row: any) => {
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{ field: 'name', title: '文件名称', minWidth: 170 },
|
||||
{ field: 'createTime', title: '导入时间', minWidth: 170 , sortable: true},
|
||||
{ field: 'allCount', title: '数据总数(条)', minWidth: 170 , sortable: true},
|
||||
{ field: 'realCount', title: '已入库总数(条)', minWidth: 170, sortable: true },
|
||||
{
|
||||
title: '解析状态',
|
||||
field: 'state',
|
||||
width: 100,
|
||||
render: 'tag',
|
||||
custom: {
|
||||
0: 'warning',
|
||||
1: 'success',
|
||||
2: 'danger',
|
||||
3: 'primary'
|
||||
},
|
||||
replaceValue: {
|
||||
0: '未解析',
|
||||
1: '解析成功',
|
||||
2: '解析失败',
|
||||
3: '文件不存在'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '详情',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
// console.log(row.portableOfflLogList)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
tableStore.table.data = []
|
||||
}
|
||||
})
|
||||
//返回
|
||||
const handleBack = () => {
|
||||
emit('back')
|
||||
}
|
||||
const tableData: any = ref()
|
||||
const open = (val: any) => {
|
||||
dialogVisible.value = true
|
||||
tableData.value = val
|
||||
setTimeout(() => {
|
||||
tableStore.index()
|
||||
}, 10)
|
||||
}
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const updateViewportHeight = async () => {
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
tableStore.table.publicHeight = height.value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateViewportHeight() // 初始化视口高度
|
||||
window.addEventListener('resize', updateViewportHeight) // 监听窗口大小变化
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateViewportHeight) // 移除监听
|
||||
})
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .el-dialog__body {
|
||||
overflow-y: none !important;
|
||||
max-height: 70vh !important;
|
||||
}
|
||||
</style>
|
||||
<!-- 解析列表 -->
|
||||
<template>
|
||||
<el-dialog v-model.trim="dialogVisible" title="详情" width="70%" draggable @closed="close">
|
||||
<div :style="tableHeight">
|
||||
<vxe-table border auto-resize height="auto" :data="tableData" v-bind="defaultAttribute">
|
||||
<vxe-column field="name" align="center" title="文件名称"></vxe-column>
|
||||
<vxe-column field="createTime" align="center" title="导入时间"></vxe-column>
|
||||
<vxe-column field="allCount" align="center" title="数据总数(条)" width="120"></vxe-column>
|
||||
<vxe-column field="realCount" align="center" title="已入库总数(条)" width="120"></vxe-column>
|
||||
<vxe-column field="state" align="center" title="解析状态" width="100">
|
||||
<template v-slot:default="scoped">
|
||||
<el-tag type="warning" v-if="scoped.row.state == 0">未解析</el-tag>
|
||||
<el-tag type="success" v-if="scoped.row.state == 1">解析成功</el-tag>
|
||||
<el-tag type="danger" v-if="scoped.row.state == 2">解析失败</el-tag>
|
||||
<el-tag type="primary" v-if="scoped.row.state == 3">文件不存在</el-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import TableStore from '@/utils/tableStore'
|
||||
import { defaultAttribute } from '@/components/table/defaultAttribute'
|
||||
import { mainHeight } from '@/utils/layout'
|
||||
const emit = defineEmits(['back'])
|
||||
const dialogVisible = ref(false)
|
||||
const tableHeight = mainHeight(440)
|
||||
const height = ref(0)
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
|
||||
const tableStore: any = new TableStore({
|
||||
url: '',
|
||||
// publicHeight: height.value,
|
||||
showPage: false,
|
||||
column: [
|
||||
{ width: '60', type: 'checkbox', fixed: 'left' },
|
||||
{
|
||||
title: '序号', width: 80, formatter: (row: any) => {
|
||||
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||
}
|
||||
},
|
||||
{ field: 'name', title: '文件名称', minWidth: 170 },
|
||||
{ field: 'createTime', title: '导入时间', minWidth: 170 , sortable: true},
|
||||
{ field: 'allCount', title: '数据总数(条)', minWidth: 170 , sortable: true},
|
||||
{ field: 'realCount', title: '已入库总数(条)', minWidth: 170, sortable: true },
|
||||
{
|
||||
title: '解析状态',
|
||||
field: 'state',
|
||||
width: 100,
|
||||
render: 'tag',
|
||||
custom: {
|
||||
0: 'warning',
|
||||
1: 'success',
|
||||
2: 'danger',
|
||||
3: 'primary'
|
||||
},
|
||||
replaceValue: {
|
||||
0: '未解析',
|
||||
1: '解析成功',
|
||||
2: '解析失败',
|
||||
3: '文件不存在'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作', fixed: 'right',
|
||||
width: '180',
|
||||
render: 'buttons',
|
||||
buttons: [
|
||||
{
|
||||
name: 'edit',
|
||||
title: '详情',
|
||||
type: 'primary',
|
||||
icon: 'el-icon-EditPen',
|
||||
render: 'basicButton',
|
||||
click: row => {
|
||||
// console.log(row.portableOfflLogList)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loadCallback: () => {
|
||||
tableStore.table.data = []
|
||||
}
|
||||
})
|
||||
//返回
|
||||
const handleBack = () => {
|
||||
emit('back')
|
||||
}
|
||||
const tableData: any = ref()
|
||||
const open = (val: any) => {
|
||||
dialogVisible.value = true
|
||||
tableData.value = val
|
||||
setTimeout(() => {
|
||||
tableStore.index()
|
||||
}, 10)
|
||||
}
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const updateViewportHeight = async () => {
|
||||
height.value = window.innerHeight < 1080 ? 230 : 450
|
||||
tableStore.table.publicHeight = height.value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateViewportHeight() // 初始化视口高度
|
||||
window.addEventListener('resize', updateViewportHeight) // 监听窗口大小变化
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateViewportHeight) // 移除监听
|
||||
})
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .el-dialog__body {
|
||||
overflow-y: none !important;
|
||||
max-height: 70vh !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -50,10 +50,18 @@
|
||||
</el-descriptions-item> -->
|
||||
|
||||
<el-descriptions-item label="PT变比" width="160">
|
||||
{{ devData.ptRatio || '/' }}
|
||||
{{
|
||||
devData.ptRatio == null
|
||||
? '/'
|
||||
: devData.ptRatio + (devData.pt2Ratio == null ? '' : ':' + devData.pt2Ratio)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="CT变比" width="160">
|
||||
{{ devData.ctRatio || '/' }}
|
||||
{{
|
||||
devData.ctRatio == null
|
||||
? '/'
|
||||
: devData.ctRatio + (devData.ct2Ratio == null ? '' : ':' + devData.ct2Ratio)
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
|
||||
<!-- <el-descriptions-item label="名称">
|
||||
@@ -77,17 +85,17 @@
|
||||
</el-descriptions-item> -->
|
||||
</el-descriptions>
|
||||
<el-tabs v-model.trim="dataSet" type="border-card" class="device-control-box-card" @tab-click="handleClick">
|
||||
|
||||
<el-tab-pane
|
||||
lazy
|
||||
:label="item.name"
|
||||
:name="item.id"
|
||||
v-for="(item, index) in deviceData.dataSetList"
|
||||
:key="index"
|
||||
:disabled="tableLoading"
|
||||
>
|
||||
<template #label>
|
||||
<span class="custom-tabs-label">
|
||||
<el-icon>
|
||||
<!-- <el-icon>
|
||||
<TrendCharts v-if="item.name == 'APF模块数据'" />
|
||||
<DataLine v-if="item.name == '历史APF模块数据'" />
|
||||
<DataAnalysis v-if="item.name.includes('趋势数据')" />
|
||||
@@ -113,7 +121,7 @@
|
||||
!item.name.includes('暂态事件')
|
||||
"
|
||||
/>
|
||||
</el-icon>
|
||||
</el-icon> -->
|
||||
<span>{{ item.name }}</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -157,7 +165,7 @@
|
||||
</el-select> -->
|
||||
<el-radio-group
|
||||
v-model.trim="formInline.dataLevel"
|
||||
v-if="!dataSet.includes('_moduleData')"
|
||||
v-if="!dataSet.includes('_moduleData') && TrendList?.lineType == 1"
|
||||
:disabled="TrendList?.lineType != 1"
|
||||
@change="handleClick"
|
||||
>
|
||||
@@ -701,7 +709,7 @@ const handleTrend = async () => {
|
||||
// }
|
||||
})
|
||||
} else {
|
||||
ElMessage.success('装置应答失败')
|
||||
ElMessage.warning('装置应答失败')
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
@@ -733,14 +741,14 @@ const handleHarmonicSpectrum = async () => {
|
||||
// getRealDataMqttMsg()
|
||||
await getBasicRealData(lineId.value).then((res: any) => {
|
||||
if (res.code == 'A0000') {
|
||||
ElMessage.success('装置应答成功')
|
||||
ElMessage.success('装置应答成功')
|
||||
// mqttMessage.value = {}
|
||||
|
||||
realDataTimer.value = window.setInterval(() => {
|
||||
if (!dataSet.value.includes('_realtimedata')) return
|
||||
|
||||
getBasicRealData(lineId.value).then((res: any) => {
|
||||
console.log(res, '获取基础实时数据')
|
||||
console.log(res, '获取基础实时数据')
|
||||
})
|
||||
}, 30000)
|
||||
}
|
||||
@@ -749,11 +757,13 @@ const handleHarmonicSpectrum = async () => {
|
||||
//返回
|
||||
const handleReturn = async () => {
|
||||
if (realDataTimer.value) {
|
||||
clearInterval(realDataTimer.value)
|
||||
window.clearInterval(realDataTimer.value)
|
||||
}
|
||||
|
||||
if (trendTimer.value) {
|
||||
clearInterval(trendTimer.value)
|
||||
window.clearInterval(trendTimer.value)
|
||||
}
|
||||
|
||||
realTimeFlag.value = true
|
||||
sonTab.value = null
|
||||
activeTrendName.value = 0
|
||||
@@ -838,7 +848,8 @@ const devData: any = ref({})
|
||||
const lineId: any = ref('')
|
||||
const dataLevel: any = ref('')
|
||||
const dataSource = ref([])
|
||||
const nodeClick = async (e: anyObj) => {
|
||||
const engineeringName = ref('')
|
||||
const nodeClick = async (e: anyObj, node: any) => {
|
||||
if (e == undefined || e.level == 2) {
|
||||
return (loading.value = false)
|
||||
}
|
||||
@@ -854,6 +865,8 @@ const nodeClick = async (e: anyObj) => {
|
||||
|
||||
//选中设备名称后,点击标签页也能查询数据,要求点击设备名称后,点击标签页默认查询第一个监测点数据
|
||||
if (e.level == 3 || e.level == 2) {
|
||||
engineeringName.value = node?.parent.parent.data.name
|
||||
|
||||
await queryDictType({
|
||||
lineId: e?.id,
|
||||
conType: e.conType
|
||||
@@ -984,44 +997,42 @@ const getRealDataMqttMsg = async () => {
|
||||
// JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
|
||||
// )
|
||||
let obj = JSON.parse(JSON.stringify(JSON.parse(new TextDecoder().decode(message))))
|
||||
|
||||
|
||||
if (lineId.value != obj.lineId || adminInfo.userIndex != obj.userId) return
|
||||
|
||||
//处理mqtt数据 1转2除 2转1乘
|
||||
//如果消息返回值是二次值,下拉框是二次值只需要单位换算 除以1000
|
||||
//如果消息返回值是一次值,下拉框是一次值只需要单位换算 除以1000
|
||||
if (obj.dataLevel == formInline.dataLevel) {
|
||||
|
||||
obj = {
|
||||
...obj,
|
||||
// 电压有效值
|
||||
vRmsA: obj.vRmsA ,
|
||||
vRmsB: obj.vRmsB ,
|
||||
vRmsC: obj.vRmsC ,
|
||||
vRmsA: obj.vRmsA,
|
||||
vRmsB: obj.vRmsB,
|
||||
vRmsC: obj.vRmsC,
|
||||
//基波电压幅值
|
||||
v1A: obj.v1A ,
|
||||
v1B: obj.v1B ,
|
||||
v1C: obj.v1C ,
|
||||
v1A: obj.v1A,
|
||||
v1B: obj.v1B,
|
||||
v1C: obj.v1C,
|
||||
//有功功率
|
||||
pA: obj.pA ,
|
||||
pB: obj.pB ,
|
||||
pC: obj.pC ,
|
||||
pTot: obj.pTot ,
|
||||
pA: obj.pA,
|
||||
pB: obj.pB,
|
||||
pC: obj.pC,
|
||||
pTot: obj.pTot,
|
||||
//无功功率
|
||||
qA: obj.qA ,
|
||||
qB: obj.qB ,
|
||||
qC: obj.qC ,
|
||||
qTot: obj.qTot ,
|
||||
qA: obj.qA,
|
||||
qB: obj.qB,
|
||||
qC: obj.qC,
|
||||
qTot: obj.qTot,
|
||||
//视在功率
|
||||
sA: obj.sA ,
|
||||
sB: obj.sB ,
|
||||
sC: obj.sC ,
|
||||
sTot: obj.sTot
|
||||
sA: obj.sA,
|
||||
sB: obj.sB,
|
||||
sC: obj.sC,
|
||||
sTot: obj.sTot
|
||||
}
|
||||
}
|
||||
//如果消息返回值是二次值,下拉框是一次值需要单位换算 除以1000 并且乘以pt ct
|
||||
if (obj.dataLevel == 'Secondary' && formInline.dataLevel == 'Primary') {
|
||||
|
||||
obj = {
|
||||
...obj,
|
||||
// 电压有效值
|
||||
@@ -1102,9 +1113,11 @@ const getRealDataMqttMsg = async () => {
|
||||
}
|
||||
if (obj.hasOwnProperty('pA') && obj.hasOwnProperty('pB')) {
|
||||
mqttMessage.value = obj
|
||||
|
||||
|
||||
//更新实时数据主页面值
|
||||
realTimeFlag.value && realTimeRef.value && realTimeRef.value.setRealData(mqttMessage.value,formInline.dataLevel)
|
||||
realTimeFlag.value &&
|
||||
realTimeRef.value &&
|
||||
realTimeRef.value.setRealData(mqttMessage.value, formInline.dataLevel)
|
||||
tableLoading.value = false
|
||||
//更新实时趋势折线图数据
|
||||
if (sonTab.value == 2) {
|
||||
@@ -1150,10 +1163,10 @@ const mqttMessage = ref<any>({})
|
||||
const handleClick = async (tab?: any) => {
|
||||
tableLoading.value = true
|
||||
if (realDataTimer.value) {
|
||||
clearInterval(realDataTimer.value)
|
||||
window.clearInterval(realDataTimer.value)
|
||||
}
|
||||
if (trendTimer.value) {
|
||||
clearInterval(trendTimer.value)
|
||||
window.clearInterval(trendTimer.value)
|
||||
}
|
||||
sonTab.value = null
|
||||
activeTrendName.value = 0
|
||||
@@ -1228,6 +1241,7 @@ const handleClick = async (tab?: any) => {
|
||||
let obj = {
|
||||
devId: deviceId.value, //e.id
|
||||
lineId: lineId.value, //e.pid
|
||||
engineeringName: engineeringName.value, //e.name
|
||||
type: 3,
|
||||
list: [
|
||||
{
|
||||
@@ -1422,9 +1436,7 @@ const echoName = (value: any, arr: any[]) => {
|
||||
return value ? arr.find(item => item.value == value)?.label : '/'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
onMounted(() => {})
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(realDataTimer.value)
|
||||
clearInterval(trendTimer.value)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,14 +16,19 @@
|
||||
<el-button type="primary" :icon="Setting" @click="handleUpDevice">补召</el-button>
|
||||
<el-button :icon="Back" @click="go(-1)">返回</el-button>
|
||||
</template>
|
||||
|
||||
</TableHeader>
|
||||
<!-- 设备补召 -->
|
||||
<div class=" current_device" v-loading="loading">
|
||||
|
||||
<div class="current_device" v-loading="loading">
|
||||
<div class="current_body" ref="tbodyRef">
|
||||
<vxe-table border ref="tableRef" :data="dirList" align="center" height="auto"
|
||||
:style="{ height: tableHeight }" @radio-change="radioChangeEvent">
|
||||
<vxe-table
|
||||
border
|
||||
ref="tableRef"
|
||||
:data="dirList"
|
||||
align="center"
|
||||
height="auto"
|
||||
:style="{ height: tableHeight }"
|
||||
@radio-change="radioChangeEvent"
|
||||
>
|
||||
<vxe-column type="radio" width="60">
|
||||
<template #header>
|
||||
<vxe-button mode="text" @click="clearRadioRowEvent" :disabled="!selectRow">取消</vxe-button>
|
||||
@@ -34,13 +39,20 @@
|
||||
<vxe-column field="status" title="补召进度">
|
||||
<template #default="{ row }">
|
||||
<div class="finish" v-if="row.status == 100">
|
||||
<SuccessFilled style="width: 16px;" /><span class="ml5">补召完成</span>
|
||||
<SuccessFilled style="width: 16px" />
|
||||
<span class="ml5">补召完成</span>
|
||||
</div>
|
||||
<el-progress v-model.trim="row.status" v-else :class="row.status == 100 ? 'progress' : ''"
|
||||
:format="format" :stroke-width="10" striped :percentage="row.status" :duration="30"
|
||||
striped-flow />
|
||||
|
||||
|
||||
<el-progress
|
||||
v-model.trim="row.status"
|
||||
v-else
|
||||
:class="row.status == 100 ? 'progress' : ''"
|
||||
:format="format"
|
||||
:stroke-width="10"
|
||||
striped
|
||||
:percentage="row.status"
|
||||
:duration="30"
|
||||
striped-flow
|
||||
/>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="startTime" title="起始时间"></vxe-column>
|
||||
@@ -58,10 +70,7 @@ import { useRouter, useRoute } from 'vue-router'
|
||||
import { mainHeight } from '@/utils/layout'
|
||||
import { VxeUI, VxeTableInstance, VxeTableEvents } from 'vxe-table'
|
||||
import { SuccessFilled } from '@element-plus/icons-vue'
|
||||
import {
|
||||
Back,
|
||||
Setting, Search
|
||||
} from '@element-plus/icons-vue'
|
||||
import { Back, Setting, Search } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import mqtt from 'mqtt'
|
||||
defineOptions({
|
||||
@@ -75,7 +84,7 @@ const loading = ref(false)
|
||||
const dirList = ref([])
|
||||
const route: any = ref({})
|
||||
const datePickerRef = ref()
|
||||
const format = (percentage) => (percentage === 100 ? '完成' : `${percentage}%`)
|
||||
const format = percentage => (percentage === 100 ? '完成' : `${percentage}%`)
|
||||
const getMakeUpDataList = (row: any) => {
|
||||
route.value = row
|
||||
loading.value = true
|
||||
@@ -139,11 +148,11 @@ const handleUpDevice = () => {
|
||||
}
|
||||
|
||||
const radioChangeEvent: VxeTableEvents.RadioChange = ({ row }) => {
|
||||
datePickerRef.value.timeValue = [row.startTime.split(' ')[0] , row.endTime.split(' ')[0] ]
|
||||
selectRow.value = row
|
||||
// console.log('单选事件')
|
||||
}
|
||||
|
||||
|
||||
const clearRadioRowEvent = () => {
|
||||
const $table = tableRef.value
|
||||
if ($table) {
|
||||
@@ -152,7 +161,7 @@ const clearRadioRowEvent = () => {
|
||||
}
|
||||
}
|
||||
const mqttRef = ref()
|
||||
const url: any = window.localStorage.getItem('MQTTURL')
|
||||
const url: any = window.localStorage.getItem('MQTTURL')
|
||||
const connectMqtt = () => {
|
||||
if (mqttRef.value) {
|
||||
if (mqttRef.value.connected) {
|
||||
@@ -173,10 +182,10 @@ const connectMqtt = () => {
|
||||
const handleSearch = () => {
|
||||
getMakeUpDataList(route.value)
|
||||
}
|
||||
function parseStringToObject(str:string) {
|
||||
function parseStringToObject(str: string) {
|
||||
const content = str.replace(/^{|}$/g, '')
|
||||
const pairs = content.split(',')
|
||||
const result:any = {}
|
||||
const result: any = {}
|
||||
pairs.forEach(pair => {
|
||||
const [key, value] = pair.split(':')
|
||||
// 尝试将数字转换为Number类型
|
||||
@@ -210,7 +219,6 @@ mqttRef.value.on('message', (topic: any, message: any) => {
|
||||
let percentage = parseInt(Number((mqttMessage.value.nowStep / mqttMessage.value.allStep) * 100)) || 0
|
||||
if (percentage > 5) {
|
||||
item.status = percentage
|
||||
|
||||
}
|
||||
}
|
||||
// })
|
||||
@@ -226,6 +234,8 @@ mqttRef.value.on('close', function () {
|
||||
console.log('mqtt客户端已断开连接.....')
|
||||
})
|
||||
onMounted(() => {
|
||||
datePickerRef.value.setInterval(5)
|
||||
datePickerRef.value.setTimeOptions([{ label: '自定义', value: 5 }])
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
if (mqttRef.value) {
|
||||
@@ -261,14 +271,21 @@ defineExpose({ getMakeUpDataList })
|
||||
// }
|
||||
// }
|
||||
:deep(.el-progress-bar__inner--striped) {
|
||||
background-image: linear-gradient(45deg, rgba(255, 255, 255, .3) 25%, transparent 0, transparent 50%, rgba(255, 255, 255, .3) 0, rgba(255, 255, 255, .3) 75%, transparent 0, transparent);
|
||||
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
rgba(255, 255, 255, 0.3) 25%,
|
||||
transparent 0,
|
||||
transparent 50%,
|
||||
rgba(255, 255, 255, 0.3) 0,
|
||||
rgba(255, 255, 255, 0.3) 75%,
|
||||
transparent 0,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
:deep(.progress) {
|
||||
.el-progress__text {
|
||||
color: green;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +293,6 @@ defineExpose({ getMakeUpDataList })
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-weight: 550;
|
||||
color: #009688
|
||||
color: #009688;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user