修改菜单+权限+图表渲染

This commit is contained in:
ym1026
2025-09-11 19:01:01 +08:00
parent 45ff73c295
commit 506c2e98f2
26 changed files with 1069 additions and 527 deletions

View File

@@ -190,16 +190,16 @@ watch(
},
{ deep: true, immediate: true }
)
watch(
() => props.tableH,
(n, o) => {
if (n && n !== o) {
const pageH = data.newTableOpt.page ? 42 : 0
scroll.value = { y: n - pageH - 56 }
}
}
// { deep: true, immediate: true }
)
// watch(
// () => props.tableH,
// (n, o) => {
// if (n && n !== o) {
// const pageH = data.newTableOpt.page ? 42 : 0
// scroll.value = { y: n - pageH - 56 }
// }
// }
// // { deep: true, immediate: true }
// )
function rowClassName(record, index) {
return 'table-row'

View File

@@ -38,17 +38,18 @@
scroll: { y: 500 }
}"
>
<template #is_add="recordList">
<a-checkbox v-model:checked="recordList.is_add"></a-checkbox>
<template #is_add="{ record, index }">
<a-checkbox v-model:checked="record.is_add"></a-checkbox>
</template>
<template #is_del="recordList">
<a-checkbox v-model:checked="recordList.is_del"></a-checkbox>
<template #is_del="{ record, index }">
<a-checkbox v-model:checked="record.is_del"></a-checkbox>
</template>
<template #is_edit="recordList">
<a-checkbox v-model:checked="recordList.is_edit"></a-checkbox>
<template #is_edit="{ record, index }">
<a-checkbox v-model:checked="record.is_edit"></a-checkbox>
</template>
<template #isQuery="recordList">
<a-checkbox v-model:checked="recordList.isQuery"></a-checkbox>
<template #is_view="{ record, index }">
<a-checkbox v-model:checked="record.is_view"></a-checkbox>
</template>
</TreeTable>
</template>
@@ -120,7 +121,7 @@ export default {
role: 'roleConfirm',
device: 'deviceConfirm',
station: 'stationConfirm',
serviceApi: 'serviceApiConfirm',
serviceApi: 'serviceApiConfirm'
// permission: 'permissionConfirm',
},
form: {},
@@ -304,30 +305,127 @@ export default {
try {
const menuApi = {
add: '/insertRole',
edit: '/deleteRole'
edit: '/updateRole'
}
const { selectedRowKeys } = this.$refs.treeTable[0]
console.log(selectedRowKeys, 'selectedRowKeys')
const { selectedRowKeys ,selectedArr} = this.$refs.treeTable[0]
console.log(selectedRowKeys,selectedArr, 'selectedRowKeys')
// const arr = selectedArr.map((item) => ({
// ...item,
// // 转换操作权限为布尔值
// ...this.getPerOperBoolean(item),
// // 递归处理children
// children: item.children
// ? item.children.map((child) => ({
// ...child,
// ...this.getPerOperBoolean(child),
// }))
// : []
// }))
// console.log(arr,"arr")
const data=this.filterTreeData(selectedRowKeys,this.$refs.treeTable[0].tableData)
const arr = data.map((item) => ({
...item,
// 转换操作权限为布尔值
...this.getPerOperBoolean(item),
// 递归处理children
children: item.children
? item.children.map((child) => ({
...child,
...this.getPerOperBoolean(child),
}))
: []
}))
const paramsDate = {
...this.form
...this.form,
permission:arr
}
// if (this.action == 'edit') {
// paramsDate.role_id = this.record.role_id
// }
if (this.action == 'edit') {
paramsDate.role_id = this.record.role_id
}
// const res = await postReq(menuApi[this.action], paramsDate)
// if (res.errcode === 0) {
// setTimeout(() => {
// this.handleback()
// }, 1000)
// } else {
// throw res
// }
const res = await postReq(menuApi[this.action], paramsDate)
if (res.errcode === 0) {
setTimeout(() => {
this.handleback()
}, 1000)
} else {
throw res
}
} catch (error) {
console.log(error, 'roleConfirm')
}
},
// 定义筛选树形数据的函数
filterTreeData(list1, list2) {
const keySet = new Set(list1);
// 递归处理节点的函数
const filterNode = (node) => {
// 创建新节点对象(浅拷贝)
const newNode = Object.assign({}, node);
// 临时删除children属性以便处理
const { children, ...rest } = newNode;
// 处理子节点
let newChildren = [];
if (children) {
newChildren = children
.map((child) => filterNode(child))
.filter((child) => child !== null);
}
// 重建新节点
const resultNode = Object.assign(rest, {});
if (newChildren.length > 0) {
resultNode.children = newChildren;
}
// 判断是否保留节点
if (keySet.has(node.key)) {
return resultNode;
}
// 保留有符合条件子节点的父节点移除自身key
if (newChildren.length > 0) {
return resultNode;
}
return null;
};
// 处理根节点
const result = list2
.map((node) => filterNode(node))
.filter((node) => node !== null);
return result;
},
getPerOperBoolean(data) {
return {
is_add: Boolean(data.is_add)? '1' : '0',
is_del: Boolean(data.is_del)? '1' : '0',
is_edit: Boolean(data.is_edit)? '1' : '0',
is_view: Boolean(data.is_view)? '1' : '0'
}
},
getPermissionData(keys,list){
const arr=[]
// list.forEach(item=>{
// if(keys.include(item.key)){
// arr.push(item)
// if()
// }
// })
},
async stationConfirm() {
try {
const menuApi = {
@@ -363,10 +461,9 @@ export default {
const paramsDate = {
...this.form,
is_open: Number(is_open),
attrs: JSON.stringify({rated_capacity, rated_current, rated_voltage, reted_power})
attrs: JSON.stringify({ rated_capacity, rated_current, rated_voltage, reted_power })
}
if (this.action == 'edit') {
paramsDate.device_id = this.record.device_id
}
@@ -451,6 +548,6 @@ export default {
}
:deep(.treeTable) {
width: 550px !important;
width: 750px !important;
}
</style>

View File

@@ -169,7 +169,9 @@ export default {
},
axisLabel: {
interval: 4,
color: '#fff'
color: '#fff',
fontSize:12
}
},
series: this.lineChartData.ydata

View File

@@ -198,7 +198,9 @@ export default {
},
axisLabel: {
interval: 4,
color: '#fff'
color: '#fff',
fontSize:12
}
},
series: this.chargeChartData.ydata

View File

@@ -166,7 +166,9 @@ export default {
},
axisLabel: {
interval: 4,
color: '#fff'
color: '#fff',
fontSize:12
}
},
series: this.energyChartData.ydata

View File

@@ -1,7 +1,6 @@
<template>
<div class="map">
<div ref="mapContent" class="mapContent" >
<div ref="mapContent" class="mapContent">
<tdt-map
ref="tiandituMap"
:center="center"
@@ -16,7 +15,7 @@
:position="[marker.lon, marker.lat]"
:key="marker.id"
:icon="marker.iconMap"
style="width: 20px; height: 20px"
style="width: 20px;height: auto"
@click="clickArrayMarker(marker)"
:title="marker.name"
>
@@ -44,11 +43,12 @@
</a-modal>
</div>
</template>
<script>
import { getReq, postReq } from '@/request/api'
import Modal from '@/components/Home/Modal.vue'
import { loadTMap } from '@/utils/loadTMap'
import {gcj02ToWgs84} from '@/utils/gcj02ToWgs84'
import { gcj02ToWgs84, wgs84ToGcj02 } from '@/utils/gcj02ToWgs84'
import { TdtMap } from 'vue-tianditu'
export default {
@@ -57,14 +57,12 @@ export default {
data() {
return {
center: [],
// zoom: 16,
zoom: 2,
center: [116.404, 39.915], // 默认中心点(北京)
zoom: 12,
map: null,
currentMarker: {},
showCtrModal: false,
markers: [],
testVal: {
name: '场站211',
id: '2'
@@ -75,19 +73,11 @@ export default {
},
mounted() {
this.initMap()
// this.getMarkList()
},
methods: {
init(map) {
this.map = map
// console.log(this.map.getCenter(),"this.map.getCenter()")
this.map.centerAndZoom(new T.LngLat(116.404, 39.915), 12);
// 监听地图加载完成事件
this.map.addEventListener('load', () => {
const c= this.map.getCenter()
this.center =gcj02ToWgs84(c);
console.log('GCJ02坐标:', this.center);
});
this.center= [110.404, 40.915]
this.getMarkList()
},
async getMarkList() {
@@ -98,50 +88,41 @@ export default {
const res = await getReq('/queryStationList', query)
if (res.errcode === 0) {
this.markers = res.data.map((item) => {
console.log(gcj02ToWgs84(+item.lon, +item.lat),"gcj02ToWgs84(+item.lon, +item.lat)")
let wgs = +item.lat && +item.lon ? gcj02ToWgs84(+item.lon, +item.lat) : [null, null]
return {
...item,
lon: wgs[0],
lat: wgs[1],
iconMap: !+item.status
? require('../../assets/home/homeIcon1.png')
: require('../../assets/home/homeIcon.png')
}
})
console.log("this.markers ",this.markers )
} else {
this.markers = []
throw res
}
} catch (error) {
console.log(error,'eeeeeeeeeeeeeeee')
console.log(error, 'eeeeeeeeeeeeeeee')
}
},
async initMap() {
try {
const [mapKey] = await Promise.all([this.getSysConfig('map-key')])
// const gcj02LngLat=map.getCenter()
// console.log(gcj02LngLat.getLng())
console.log(mapKey,"mapKey")
await loadTMap(mapKey)
// 创建地图实例需要HTML中存在id为mapContainer的元素
// this.map = new T.Map("amap-vue");
// // this.map.centerAndZoom(new T.LngLat(116.404, 39.915), 12); // 初始中心点
// const centerGcj02 = this.map.getCenter();
// console.log("GCJ02坐标:", centerGcj02.getLng(), centerGcj02.getLat());
// const centerWgs84=gcj02ToWgs84(centerGcj02.getLng(),centerGcj02.getLat())
// this.center =[]
} catch (err) {
console.error('天地图加载失败:', err)
}
},
// 备用定位方案
// setFallbackLocation() {
// const fallbackCoords = [116.404, 39.915] // 北京坐标
// this.center = fallbackCoords
// if (this.map) {
// this.map.setCenter(new T.LngLat(...fallbackCoords))
// }
// },
async getSysConfig() {
let sysConfig
try {
@@ -157,28 +138,17 @@ export default {
}
return sysConfig
},
async clickArrayMarker(currentVal) {
this.changeStationId = currentVal.station_id
console.log(currentVal, 'cccccccccccccccccccccc')
this.showCtrModal = true
// try {
// const query = {
// // station_id:this.changeStationId
// }
// const res = await postReq(query, '')
// if (res.errcode === 0) {
// this.modalInfo = res.data.records
// } else {
// throw res
// }
// } catch (err) {
// console.log(err, 'eeeeeeeeeeeeeeeerr')
// }
}
}
}
</script>
<style scoped lang="scss">
/* 保持原有样式不变 */
.map {
width: 100%;
height: 100%;
@@ -266,4 +236,4 @@ export default {
height:auto!important;
}
}
</style>
</style>

View File

@@ -47,6 +47,7 @@ import DisCharge from '@/components/Home/Modal/DisCharge.vue'
import { getReq, postReq } from '@/request/api'
import { getRunDays, getDateDaysAgo } from '@/utils/dealWithData'
import EnvInfo from './Modal/EnvInfo.vue'
import { markRaw } from 'vue';
export default {
name: 'Home',
@@ -64,44 +65,44 @@ export default {
{
title: '预制舱信息',
class: '',
componentId: PrefabCabin,
componentId: markRaw( PrefabCabin),
infoKey: 'prefab'
},
{
title: '储能充放电量',
class: 'stats-cards',
componentId: DisCharge,
componentId:markRaw( DisCharge),
infoKey: 'energy'
},
{
title: '运行信息',
class: 'operation-status',
componentId: OperationalInfo,
componentId:markRaw( OperationalInfo),
infoKey: 'dataTotal'
},
{
title: '场站收益情况',
class: 'revenue',
componentId: Revenue,
componentId:markRaw( Revenue),
infoKey: 'energy'
},
{
title: '统计信息',
class: 'statistical',
componentId: StatisticalInfo,
componentId:markRaw( StatisticalInfo),
infoKey: ''
},
{
title: '设备利用率',
class: '',
componentId: Utilization,
componentId:markRaw( Utilization),
infoKey: 'energy'
},
{
title: '环境信息',
class: 'envInfo',
componentId: EnvInfo,
componentId:markRaw( EnvInfo),
infoKey: 'dataTotal'
}
],

View File

@@ -166,7 +166,9 @@ export default {
},
axisLabel: {
interval: 4,
color: '#fff'
color: '#fff',
fontSize:12
}
},
series: this.pvChartData.ydata

View File

@@ -258,7 +258,7 @@ input:-internal-autofill-selected {
}
.label {
width: 40px;
width: 80px;
color: var(--theme-text-default);
}
.top-right,

View File

@@ -8,18 +8,19 @@
:data-source="tableData"
:pagination="false"
:row-class-name="(record, index) => rowClassName(record, index)"
row-key="id"
row-key="key"
size="middle"
:row-selection="rowSelection"
:indent-size="30"
:check-strictly="false"
@resizeColumn="handleResizeColumn"
>
<template #bodyCell="{ column, record }">
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.scopedSlots">
<slot
v-bind="record"
v-bind="{record,index}"
:name="column.scopedSlots ? column.scopedSlots.customRender : ''"
></slot>
</template>
</template>
@@ -85,6 +86,7 @@ export default {
realColumns: [],
realTableData: [],
selectedRows: {},
defaultTabOpt: {
page: true,
align: 'center',
@@ -149,6 +151,8 @@ export default {
onSelectChange(selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows[selectedRows.length - 1]
this.selectedArr=selectedRows
console.log(selectedRowKeys, this.selectedArr ,"selectedRowKeys")
this.$emit('getSelectedIds', selectedRowKeys)
},
onSelect(record, selected) {
@@ -232,6 +236,8 @@ export default {
</script>
<style lang="scss" scoped>
:deep(.ant-table-body) {
border-radius: 0px 0px 20px 20px !important;
.ant-table-cell {
background: var(--theme-bg) !important;
}
@@ -272,8 +278,9 @@ export default {
}
:deep(.ant-table) {
border-radius:20px 20px 0 0 !important;
// border-radius:20px 20px 0 0 !important;
overflow: hidden; /* 确保圆角生效 */
}
:deep(.ant-table-wrapper .ant-table.ant-table-bordered >.ant-table-container){
border-inline-start:none!important;
@@ -516,6 +523,8 @@ export default {
}
:deep(.ant-table-wrapper .ant-table) {
border: 1px solid $table-border;
background-color: transparent !important;
}
@@ -525,4 +534,9 @@ export default {
:deep(.ant-table-wrapper .ant-table-thead th.ant-table-column-has-sorters:hover) {
background: var(--table-select) !important;
}
:deep(.ant-table-wrapper .ant-table-row-expand-icon){
background:#2c538a!important;
}
</style>

View File

@@ -17,7 +17,6 @@
ref="comTable"
:table-option="tableOption"
:page-option="pageOption"
:table-h="tableH"
></ComTable>
</div>
</div>
@@ -26,10 +25,16 @@
import { processData } from '@/utils/dealWithData'
import { postReq } from '@/request/api'
import ComTable from '@/components/ComTable'
// import { debounce } from 'lodash';
export default {
name: 'StatisicalAnView',
components: { ComTable },
props: {
pageOption: {
type: Object,
default: () => ({}), // 默认空对象
required: false // 非必须
},
tableInfo: {
type: Object,
default: () => ({}), // 默认空对象
@@ -50,6 +55,11 @@ export default {
default: () => ({}), // 默认空对象
required: false // 非必须
},
chartDatav: {
type: Object,
default: () => ({}), // 默认空对象
required: false // 非必须
},
tableData: {
type: Array,
default: () => [] // 默认空对象
@@ -64,94 +74,172 @@ export default {
},
select: false
},
tableH: 0,
chartInstances: [] // 存储 ECharts 实例
}
},
watch: {
chartData: {
handler(n) {
console.log(n, 'nnnnnnnnnnnnnnnnnnnnnnn')
this.$nextTick(() => {
this.initCharts()
window.addEventListener('resize', this.handleResize)
})
}
}
handler(newVal) {
if (newVal && Object.keys(newVal).length > 0) {
this.$nextTick(() => {
// this.destroyCharts(); // 先销毁旧图表
this.initBarCharts(); // 重新初始化
});
}
},
deep: true,
// immediate: true,
},
chartDatav: {
handler(newVal) {
if (newVal && Object.keys(newVal).length > 0) {
this.$nextTick(() => {
// this.destroyCharts(); // 先销毁旧图表
this.initLineCharts(); // 重新初始化
});
}
},
deep: true,
// immediate: true,
},
},
mounted() {
// this.initCharts()
// window.addEventListener('resize', this.handleResize)
window.addEventListener('resize', this.handleResize);
},
beforeUnmount() {
window.removeEventListener('resize', this.handleResize)
this.chartInstances.forEach((chart) => chart && chart.dispose())
window.removeEventListener('resize', this.handleResize);
this.destroyCharts();
},
methods: {
initCharts() {
this.chartOptions.forEach((option, index) => {
const dom = this.$refs[`chartContainer${index}`][0]
if (!dom) return
const chart = this.$echarts.init(dom)
this.chartInstances.push(chart) // 存储实例
const keys= option.infoKeys.map((item) => item.key)
// 设置图表配置
chart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
top: 20,
textStyle: {
color: '#fff'
}
},
grid: {
left: '3%',
right: '3%',
bottom: '5%',
containLabel: true
},
xAxis: {
type: 'category',
data: processData(this.chartData,keys)
.dates,
destroyCharts() {
this.chartInstances.forEach((chart) => {
if (chart && !chart.isDisposed()) {
chart.dispose(); // 销毁旧图表
}
});
this.chartInstances = []; // 清空实例数组
},
// 公共 ECharts 配置
getCommonOption(option, dataKeys, isLineChart = false) {
return {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
},
legend: {
top: 20,
textStyle: { color: '#fff' },
},
grid: {
left: '3%',
right: '3%',
bottom: '5%',
containLabel: true,
},
xAxis: {
type: 'category',
data: isLineChart ? this.generateTimePoints() : processData(this.chartData, dataKeys).dates,
axisLine: { lineStyle: { color: '#fff' } },
axisLabel: { color: '#fff' },
},
yAxis: {
type: 'value',
splitLine: { lineStyle: { type: 'dashed', color: '#435463' } },
axisLabel: { color: '#fff' },
},
};
},
generateTimePoints() {
const timePoints = []
axisLine: {
lineStyle: { type: 'dashed', color: '#435463' }
},
axisLabel: {
color: '#fff'
}
},
yAxis: {
type: 'value',
splitLine: {
lineStyle: { type: 'dashed', color: '#435463' }
},
axisLabel: {
color: '#fff'
}
},
series: option.infoKeys.map((info, i) => {
return {
for (let hour = 0; hour <= 24; hour++) {
for (let minute = 0; minute < 60; minute += 10) {
// 处理24:00的特殊情况
if (hour === 24 && minute === 0) {
timePoints.push('24:00')
break
}
// 格式化小时和分钟,确保两位数
const formattedHour = hour.toString().padStart(2, '0')
const formattedMinute = minute.toString().padStart(2, '0')
timePoints.push(`${formattedHour}:${formattedMinute}`)
}
}
return timePoints
},
// 初始化柱状图
initBarCharts() {
this.chartOptions.forEach((option, index) => {
if (option.type === 'bar') {
const dom = this.$refs[`chartContainer${index}`]?.[0];
if (!dom) return;
const chart = this.$echarts.init(dom);
this.chartInstances.push(chart);
const dataKeys = option.infoKeys.map((item) => item.key);
const commonOption = this.getCommonOption(option, dataKeys);
chart.setOption({
...commonOption,
series: option.infoKeys.map((info, i) => ({
name: info.label,
smooth: true,
type: option.type,
type: 'bar',
barWidth: 10,
itemStyle: {
borderRadius: [10, 10, 0, 0],
color: info.lineColor
color: info.lineColor,
},
emphasis: {
focus: 'series'
data: processData(this.chartData, dataKeys).values[i],
})),
});
}
});
},
// 初始化折线图
initLineCharts() {
this.chartOptions.forEach((option, index) => {
if (option.type === 'line') {
const dom = this.$refs[`chartContainer${index}`]?.[0];
if (!dom) return;
const chart = this.$echarts.init(dom);
this.chartInstances.push(chart);
const dataKeys = option.infoKeys.map((item) => item.key);
const commonOption = this.getCommonOption(option, dataKeys, true);
chart.setOption({
...commonOption,
dataZoom: [
{
type: 'slider',
show: true,
xAxisIndex: 0,
start: 0,
end: 20,
height: 20,
bottom: 10,
},
{
type: 'inside',
xAxisIndex: 0,
start: 0,
end: 20,
},
],
series: option.infoKeys.map((info, i) => ({
name: info.label,
type: 'line',
smooth: true,
showSymbol: false,
itemStyle: { color: info.lineColor },
areaStyle: {
global: false,
color: {
type: 'linear',
x: 0,
@@ -159,32 +247,33 @@ export default {
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: info.colorStart }, // 顶部颜色
{ offset: 1, color: info.colorEnd } // 底部颜色
]
}
{ offset: 0, color: info.colorStart },
{ offset: 1, color: info.colorEnd },
],
},
},
global: false,
showSymbol: false,
data: processData(
this.chartData,
keys
).values[i]
}
})
})
})
data: this.chartDatav[info.key],
})),
});
}
});
},
handleResize() {
this.chartInstances.forEach((chart) => chart && chart.resize())
handleResize () {
this.chartInstances.forEach((chart) => chart && chart.resize());
// this.handleResize = debounce(this.resize, 300);
},
resize() {
this.chartInstances.forEach((chart) => chart && chart.resize());
},
handlePagesizeChange(pageOption) {
this.$emit('pagesizeChange_energy', pageOption)
}
}
},
}
</script>
@@ -242,5 +331,11 @@ export default {
min-height: 500px;
width: 100%;
padding-right: 10px;
padding-bottom: 10px;
.comtable{
height:610px;
// overflow: scroll;
}
}
</style>