This commit is contained in:
Free-sss 2025-08-28 10:37:29 +08:00
commit 1bb0f26f17
7 changed files with 395 additions and 137 deletions

View File

@ -5,7 +5,7 @@ export const LineDropdownConfig: ConfigType = {
key: 'LineDropdownHaz',
chartKey: 'VLineDropdownHaz',
conKey: 'VCLineDropdownHaz',
title: '下拉折线图',
title: '报警趋势',
category: 'HazardousChemicalsSpace',
categoryName: '危化品场景',
package: PackagesCategoryEnum.CHARTS,

View File

@ -7,17 +7,16 @@
:selectedValue="option.dateTime.selectValue"
@change="handleSelectChange"
/>
<div class="line-dropdown-container">
<!-- 下拉框 -->
<!-- <div class="dropdown-container">
<select v-model="selectedOption" @change="handleDropdownChange" class="custom-dropdown1">
<option value="总数">总数</option>
<option value="类型1">类型1</option>
<option value="类型2">类型2</option>
<option value="类型3">类型3</option>
</select>
<div class="dropdown-arrow"></div>
</div> -->
<!-- 新增的风险级别下拉框 -->
<div class="risk-level-select">
<CustomSelect
:options="riskLevelOptions"
:selectedValue="selectedRiskLevel"
@change="handleRiskLevelChange"
/>
</div>
<!-- 折线图 -->
<div class="chart-container">
@ -28,7 +27,7 @@
</template>
<script setup lang="ts">
import { computed, PropType, ref, watch } from 'vue'
import { computed, PropType, ref, watch, onMounted } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
@ -38,6 +37,7 @@ import { mergeTheme } from '@/packages/public/chart'
import config, { includes } from './config'
import { DatasetComponent, GridComponent, TooltipComponent } from 'echarts/components'
import CustomSelect from './select.vue'
import axios from 'axios'
const props = defineProps({
themeSetting: {
@ -56,10 +56,88 @@ const props = defineProps({
const selectedOption = ref('总数')
//
const selectedRiskLevel = ref(0)
const riskLevelOptions = ref([
{ label: '全部', value: 0 },
{ label: '重大风险', value: 1 },
{ label: '较大风险', value: 2 },
{ label: '一般风险', value: 3 },
{ label: '低风险', value: 4 }
])
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
use([DatasetComponent, CanvasRenderer, LineChart, GridComponent, TooltipComponent])
// API
const fetchChartData = async (timeType: string, riskLevel: number) => {
try {
const response = await axios.get(`/screen/alarmByOption/${timeType}/${riskLevel}`)
if (response.data.state === true) {
return response.data.value || []
} else {
console.error('API调用失败:', response.data)
return []
}
} catch (error) {
console.error('获取图表数据失败:', error)
return []
}
}
//
const processChartData = async (timeType: string, riskLevel: number) => {
// API
const rawData = await fetchChartData(timeType, riskLevel)
//
let processedData: Array<{time: string, value: number}> = []
switch (timeType) {
case 'day':
processedData = rawData.map((item: any) => ({
time: `${item.hour}:00`,
value: item.alarm_count
}))
break
case 'week':
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
processedData = rawData.map((item: any) => ({
time: weekDays[item.dayOfWeek],
value: item.alarm_count
}))
break
case 'month':
processedData = rawData.map((item: any) => ({
time: `${item.day}`,
value: item.alarm_count
}))
break
case 'quarter':
case 'year':
processedData = rawData.map((item: any) => ({
time: `${item.month}`,
value: item.alarm_count
}))
break
default:
processedData = []
}
return {
dimensions: ['time', 'value'],
source: processedData
}
}
//
const updateChartData = async () => {
const newData = await processChartData(props.chartConfig.option.dateTime.selectValue, selectedRiskLevel.value)
props.chartConfig.option.dataset = newData
}
const option = computed(() => {
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
})
@ -69,10 +147,23 @@ const handleDropdownChange = () => {
console.log('Selected option:', selectedOption.value)
}
//
//
const handleSelectChange = (value: any) => {
props.chartConfig.option.dateTime.selectValue = value
updateChartData()
}
//
const handleRiskLevelChange = (value: any) => {
selectedRiskLevel.value = value
updateChartData()
console.log('Selected risk level:', value)
}
//
onMounted(() => {
updateChartData()
})
</script>
<style scoped>
@ -293,22 +384,51 @@ const handleSelectChange = (value: any) => {
white-space: nowrap;
}
.header-title{
position: absolute;
//
.risk-level-select {
position: relative;
width: 90%;
margin: 10px auto 0;
:deep(.custom-select) {
z-index: 999;
position: relative;
top: 0;
height: 45px;
line-height: 45px;
left: 80px;
font-size: 16px;
font-weight: bold;
color: #eee;
font-style: italic;
text-shadow: 0 0 10px #00E5FF;
white-space: nowrap;
right: 0;
}
:deep(.select-display) {
background-color: rgb(19, 22, 33);
border: 1px solid rgb(75, 83, 99);
color: #fff;
border-radius: 6px;
padding: 0 12px;
height: 28px;
font-size: 12px;
min-width: 90px;
}
:deep(.select-dropdown) {
background-color: rgb(19, 22, 33);
border: 1px solid rgb(75, 83, 99);
border-radius: 6px;
}
:deep(.select-option) {
color: #fff;
&:hover {
background-color: rgba(75, 83, 99, 0.3);
}
&.selected {
background-color: rgba(75, 83, 99, 0.5);
}
}
}
.svg {
width: 100%;
height: 45px;
}
</style>
</style>

View File

@ -0,0 +1,104 @@
{
"day": [
{
"alarm_count": 1,
"hour": 6
},
{
"alarm_count": 1,
"hour": 7
},
{
"alarm_count": 59,
"hour": 12
},
{
"alarm_count": 71,
"hour": 13
}
],
"week": [
{
"dayOfWeek": 1,
"alarm_count": 14
},
{
"dayOfWeek": 2,
"alarm_count": 15
},
{
"dayOfWeek": 3,
"alarm_count": 132
}
],
"month": [
{
"alarm_count": 13,
"day": 17
},
{
"alarm_count": 75,
"day": 18
},
{
"alarm_count": 94,
"day": 19
},
{
"alarm_count": 34,
"day": 20
},
{
"alarm_count": 23,
"day": 21
},
{
"alarm_count": 6,
"day": 22
},
{
"alarm_count": 14,
"day": 25
},
{
"alarm_count": 15,
"day": 26
},
{
"alarm_count": 132,
"day": 27
}
],
"quarter": [
{
"alarm_count": 18,
"month": 7
},
{
"alarm_count": 406,
"month": 8
}
],
"year": [
{
"alarm_count": 10,
"month": 4
},
{
"alarm_count": 10,
"month": 5
},
{
"alarm_count": 10,
"month": 6
},
{
"alarm_count": 18,
"month": 7
},
{
"alarm_count": 406,
"month": 8
}
]
}

View File

@ -39,7 +39,7 @@
</div>
</template>
<script setup lang="ts">
import { PropType, computed, watch, ref, nextTick } from 'vue'
import { PropType, computed, watch, ref, nextTick,onMounted } from 'vue'
import VChart from 'vue-echarts'
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
import { use } from 'echarts/core'
@ -52,7 +52,7 @@ import { useChartDataFetch } from '@/hooks'
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
import isObject from 'lodash/isObject'
import CustomSelect from './select.vue'
import mockData1 from './mock1.json'
import axios from 'axios'
const props = defineProps({
themeSetting: {
@ -82,102 +82,101 @@ const replaceMergeArr = ref<string[]>()
//
const selectedTimeRange = ref('day')
// mock1.jsonmock.json
const convertMock1ToMockFormat = (mock1Data: any) => {
const convertedData: any = {}
Object.keys(mock1Data).forEach(timeRange => {
const dataArray = mock1Data[timeRange]
if (!Array.isArray(dataArray) || dataArray.length === 0) {
convertedData[timeRange] = {
alertTotal: 0,
unprocessedAlert: 0,
averageResolutionTime: 0,
datavalues: [['时间', '数值']]
}
return
// API
const fetchChartData = async (option: string) => {
try {
const response = await axios.get(`/screen/handleByOption/${option}`)
if (response.data.state === true) {
return response.data.value || []
} else {
console.error('API调用失败:', response.data)
return []
}
//
const alertTotal = dataArray.reduce((sum, item) => sum + (item.alarm_count || 0), 0)
const unprocessedAlert = dataArray.reduce((sum, item) => sum + (item.un_alarm_count || 0), 0)
const avgHandleTimeSum = dataArray.reduce((sum, item) => sum + (item.avg_handle_time_seconds || 0), 0)
const averageResolutionTime = Math.round(avgHandleTimeSum / dataArray.length)
// datavalues
let datavalues: (string | number)[][] = [['时间', '数值']]
switch (timeRange) {
case 'day':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
`${item.alarm_hour}:00`,
item.avg_handle_time_seconds || 0
])]
break
case 'week':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
item.week_day_name || '',
item.avg_handle_time_seconds || 0
])]
break
case 'month':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
item.alarm_date || '',
item.avg_handle_time_seconds || 0
])]
break
case 'quarter':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
`${item.month}`,
item.avg_handle_time_seconds || 0
])]
break
case 'year':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
`${item.month}`,
item.avg_handle_time_seconds || 0
])]
break
default:
datavalues = [['时间', '数值']]
}
convertedData[timeRange] = {
alertTotal,
unprocessedAlert,
averageResolutionTime,
datavalues
}
})
return convertedData
} catch (error) {
console.error('获取图表数据失败:', error)
return []
}
}
// mock1mock
const convertedMockData = convertMock1ToMockFormat(mockData1)
// API
const convertApiDataToMockFormat = async (timeRange: string) => {
const dataArray = await fetchChartData(timeRange)
if (!Array.isArray(dataArray) || dataArray.length === 0) {
return {
alertTotal: 0,
unprocessedAlert: 0,
averageResolutionTime: 0,
datavalues: [['时间', '数值']]
}
}
//
const alertTotal = dataArray.reduce((sum, item) => sum + (item.alarm_count || 0), 0)
const unprocessedAlert = dataArray.reduce((sum, item) => sum + (item.un_alarm_count || 0), 0)
const avgHandleTimeSum = dataArray.reduce((sum, item) => sum + (item.avg_handle_time_seconds || 0), 0)
const averageResolutionTime = Math.round(avgHandleTimeSum / dataArray.length)
// datavalues
let datavalues: (string | number)[][] = [['时间', '数值']]
switch (timeRange) {
case 'day':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
`${item.alarm_hour}:00`,
item.avg_handle_time_seconds || 0
])]
break
case 'week':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
item.week_day_name || '',
item.avg_handle_time_seconds || 0
])]
break
case 'month':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
item.alarm_date || '',
item.avg_handle_time_seconds || 0
])]
break
case 'quarter':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
`${item.month}`,
item.avg_handle_time_seconds || 0
])]
break
case 'year':
datavalues = [['时间', '数值'], ...dataArray.map(item => [
`${item.month}`,
item.avg_handle_time_seconds || 0
])]
break
default:
datavalues = [['时间', '数值']]
}
return {
alertTotal,
unprocessedAlert,
averageResolutionTime,
datavalues
}
}
//
const getMockDataByTimeRange = (timeRange: string) => {
const defaultData = {
alertTotal: 0,
unprocessedAlert: 0,
averageResolutionTime: 0,
datavalues: [['时间', '数值']]
}
if (convertedMockData && convertedMockData[timeRange]) {
return convertedMockData[timeRange]
}
return defaultData
}
//
const currentData = computed(() => {
return getMockDataByTimeRange(selectedTimeRange.value)
const currentData = ref({
alertTotal: 0,
unprocessedAlert: 0,
averageResolutionTime: 0,
datavalues: [['时间', '数值']]
})
//
const updateChartData = async () => {
const newData = await convertApiDataToMockFormat(selectedTimeRange.value)
currentData.value = newData
}
//
const chartOption = computed(() => {
const mergedOption = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
@ -224,10 +223,16 @@ const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (n
const handleSelectChange = (value: any) => {
props.chartConfig.option.dateTime.selectValue = value
selectedTimeRange.value = value
updateChartData()
}
//
//
selectedTimeRange.value = props.chartConfig.option.dateTime.selectValue || 'day'
//
onMounted(() => {
updateChartData()
})
</script>
<style lang="scss" scoped>
@include go(border-box) {

View File

@ -67,6 +67,9 @@ $border-gradient-end: rgba(128, 128, 128, 0);
// z-index: 2;
width: 100%;
height: 12%;
.svg{
width: 100%;
}
// background-color: antiquewhite;
& .svg {

View File

@ -28,23 +28,21 @@
</div>
<div class="content">
<ul>
<li v-for="(item, index) in option.dataset" :key="index" class="alarm-item">
<li v-for="(item, index) in displayData" :key="index" class="alarm-item">
<div class="item-info">
<span class="rank" :style="{ color: option.rankColor }">
<span class="rank-icon"></span>
{{ item.rank }}
{{ index + 1 }}
</span>
<span class="name" :style="{ color: option.nameColor }">{{ item.name }}</span>
<span class="name" :style="{ color: option.nameColor }">{{ item.enterpriseName }}</span>
</div>
<div class="item-value">
<span class="value" :style="{ color: option.valueColor }">{{ item.value }}</span>
<span class="value" :style="{ color: option.valueColor }">{{ item.alarmCount }}</span>
</div>
<div class="progress-bar-wrapper">
<div class="progress-bar">
<div class="progress" :style="{
width: calculateWidth(item.value),
width: calculateWidth(item.alarmCount),
background: `linear-gradient(to right, ${option.barColorStart}, ${option.barColorEnd})`
}"></div>
</div>
@ -54,14 +52,14 @@
</div>
</div>
</SmallBorder>
</template>
<script setup lang="ts">
import { ref, computed, PropType } from 'vue'
import { ref, computed, PropType, onMounted } from 'vue'
import { option as configOption } from './config'
import SmallBorder from '../SmallBorder/index.vue'
import CustomSelect from './select.vue'
import axios from 'axios'
const props = defineProps({
chartConfig: {
@ -71,9 +69,11 @@ const props = defineProps({
})
const option = computed(() => props.chartConfig.option)
const selectedOption = ref(props.chartConfig.option.dropdownDefault)
// API
const displayData = ref([])
const dropdownOptions = computed(() => {
return props.chartConfig.option.dropdownOptions.map(opt => ({
label: opt,
@ -81,24 +81,46 @@ const dropdownOptions = computed(() => {
}))
})
const maxValue = computed(() => {
const values = props.chartConfig.option.dataset.map(item => item.value)
return Math.max(...values, 1) // Avoid division by zero
})
// API
const calculateWidth = (value: number) => {
if (maxValue.value === 0) return '0%'
return `${(value / maxValue.value) * 80}%` // Max width 80% to leave space for value
if (displayData.value.length === 0 || displayData.value[0].alarmCount === 0) {
return '0%'
}
const firstRowValue = displayData.value[0].alarmCount
return `${(value / firstRowValue) * 100}%` // alarmCount/alarmCount
}
// API
const fetchCorpsData = async (option: string) => {
try {
const response = await axios.get(`/screen/corpsFive/${option}`)
if (response.data.state === true) {
displayData.value = response.data.value || []
} else {
console.error('API调用失败:', response.data)
displayData.value = []
}
} catch (error) {
console.error('获取企业数据失败:', error)
displayData.value = []
}
}
const handleSelect = (key: string) => {
selectedOption.value = key
}
//
const handleSelectChange = (value: any) => {
props.chartConfig.option.dateTime.selectValue = value
// API
const handleSelectChange = async (value: any) => {
props.chartConfig.option.dateTime.selectValue = value
await fetchCorpsData(value)
}
//
onMounted(async () => {
const initialValue = props.chartConfig.option.dateTime.selectValue || 'day'
await fetchCorpsData(initialValue)
})
</script>
<style lang="scss" scoped>

View File

@ -71,6 +71,10 @@ export default ({ mode }) =>
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/dev/, '')
},
'/screen/': {
target: 'http://127.0.0.1:8080/',
changeOrigin: true
}
}
},