|
@@ -4,7 +4,7 @@
|
|
|
<DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
|
|
<DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
|
|
|
|
|
|
|
|
<el-col :span="isLeftContentCollapsed ? 24 : 20" :xs="24">
|
|
<el-col :span="isLeftContentCollapsed ? 24 : 20" :xs="24">
|
|
|
- <div class="soc-summary-panel">
|
|
|
|
|
|
|
+ <div class="soc-summary-panel" v-loading="staticLoading">
|
|
|
<div class="soc-summary-chart">
|
|
<div class="soc-summary-chart">
|
|
|
<div class="soc-summary-chart__header">
|
|
<div class="soc-summary-chart__header">
|
|
|
<div class="soc-summary-chart__title">
|
|
<div class="soc-summary-chart__title">
|
|
@@ -18,8 +18,7 @@
|
|
|
ref="socChartRef"
|
|
ref="socChartRef"
|
|
|
:options="socChartOption"
|
|
:options="socChartOption"
|
|
|
:height="240"
|
|
:height="240"
|
|
|
- @chart-click="handleChartClick"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ @chart-click="handleChartClick" />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -193,8 +192,12 @@ const { ZmTable, ZmTableColumn } = useTableComponents()
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
const userStore = useUserStore()
|
|
const userStore = useUserStore()
|
|
|
|
|
|
|
|
-type SummaryItem = Record<string, number>
|
|
|
|
|
-type ChildMap = Record<string, SummaryItem[]>
|
|
|
|
|
|
|
+type SummaryItem = Record<string, any>
|
|
|
|
|
+type ChartDataItem = {
|
|
|
|
|
+ id?: string | number
|
|
|
|
|
+ name: string
|
|
|
|
|
+ value: number
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const DRILLDOWN_KEYS = ['个人防护', '规范操作', '规范指挥', '人员位置', '作业场所'] as const
|
|
const DRILLDOWN_KEYS = ['个人防护', '规范操作', '规范指挥', '人员位置', '作业场所'] as const
|
|
|
|
|
|
|
@@ -301,30 +304,49 @@ const downloadSOC = async (row) => {
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const child = ref<ChildMap>({})
|
|
|
|
|
|
|
+const childData = ref<SummaryItem[]>([])
|
|
|
const totalData = ref<SummaryItem[]>([])
|
|
const totalData = ref<SummaryItem[]>([])
|
|
|
const currentDrilldownKey = ref('')
|
|
const currentDrilldownKey = ref('')
|
|
|
|
|
+const currentDrilldownId = ref<string | number | undefined>(undefined)
|
|
|
|
|
+
|
|
|
|
|
+const normalizeChartData = (source: SummaryItem[] = []): ChartDataItem[] => {
|
|
|
|
|
+ return source
|
|
|
|
|
+ .map((item) => {
|
|
|
|
|
+ if (!item) return null
|
|
|
|
|
+
|
|
|
|
|
+ if ('name' in item || 'className' in item || 'label' in item) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: item.id ?? item.socClass ?? item.valueId,
|
|
|
|
|
+ name: item.name ?? item.className ?? item.label ?? '',
|
|
|
|
|
+ value: Number(item.value ?? item.count ?? item.total ?? 0) || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const entries = Object.entries(item)
|
|
|
|
|
+ const nameEntry =
|
|
|
|
|
+ entries.find(
|
|
|
|
|
+ ([key, value]) =>
|
|
|
|
|
+ !['id', 'socClass', 'valueId', 'value', 'count', 'total'].includes(key) &&
|
|
|
|
|
+ typeof value === 'number'
|
|
|
|
|
+ ) || entries.find(([key]) => !['id', 'socClass', 'valueId'].includes(key))
|
|
|
|
|
+
|
|
|
|
|
+ const [name, value] = nameEntry || ['', 0]
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: item.id ?? item.socClass ?? item.valueId,
|
|
|
|
|
+ name,
|
|
|
|
|
+ value: Number(value) || 0
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter((item): item is ChartDataItem => !!item && !!item.name)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const totalChartData = computed(() => {
|
|
const totalChartData = computed(() => {
|
|
|
- return (totalData.value as SummaryItem[]).map((item) => {
|
|
|
|
|
- const [name, value] = Object.entries(item || {})[0] || ['', 0]
|
|
|
|
|
- return {
|
|
|
|
|
- name,
|
|
|
|
|
- value: Number(value) || 0
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ return normalizeChartData(totalData.value as SummaryItem[])
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const childChartData = computed(() => {
|
|
const childChartData = computed(() => {
|
|
|
if (!currentDrilldownKey.value) return []
|
|
if (!currentDrilldownKey.value) return []
|
|
|
- const source = (child.value as ChildMap)?.[currentDrilldownKey.value] || []
|
|
|
|
|
- return source.map((item) => {
|
|
|
|
|
- const [name, value] = Object.entries(item || {})[0] || ['', 0]
|
|
|
|
|
- return {
|
|
|
|
|
- name,
|
|
|
|
|
- value: Number(value) || 0
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ return normalizeChartData(childData.value as SummaryItem[])
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const chartColorMap: Record<string, string> = {
|
|
const chartColorMap: Record<string, string> = {
|
|
@@ -336,6 +358,17 @@ const chartColorMap: Record<string, string> = {
|
|
|
作业场所: '#ff00ff'
|
|
作业场所: '#ff00ff'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const childChartColorPalette = [
|
|
|
|
|
+ '#4f8dff',
|
|
|
|
|
+ '#43c7ca',
|
|
|
|
|
+ '#ff981f',
|
|
|
|
|
+ '#8d8cff',
|
|
|
|
|
+ '#ff7a7a',
|
|
|
|
|
+ '#52c41a',
|
|
|
|
|
+ '#13c2c2',
|
|
|
|
|
+ '#faad14'
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
const hexToRgba = (hex: string, alpha: number) => {
|
|
const hexToRgba = (hex: string, alpha: number) => {
|
|
|
const normalizedHex = hex.replace('#', '')
|
|
const normalizedHex = hex.replace('#', '')
|
|
|
const safeHex =
|
|
const safeHex =
|
|
@@ -353,16 +386,16 @@ const hexToRgba = (hex: string, alpha: number) => {
|
|
|
return `rgba(${red}, ${green}, ${blue}, ${alpha})`
|
|
return `rgba(${red}, ${green}, ${blue}, ${alpha})`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const getBarBaseColor = (name: string, isDrilldown: boolean) => {
|
|
|
|
|
- if (isDrilldown && currentDrilldownKey.value) {
|
|
|
|
|
- return chartColorMap[currentDrilldownKey.value] || '#67c23a'
|
|
|
|
|
|
|
+const getBarBaseColor = (name: string, isDrilldown: boolean, index: number) => {
|
|
|
|
|
+ if (isDrilldown) {
|
|
|
|
|
+ return childChartColorPalette[index % childChartColorPalette.length]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return chartColorMap[name] || '#409eff'
|
|
return chartColorMap[name] || '#409eff'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const getBarItemStyle = (name: string, isDrilldown: boolean) => {
|
|
|
|
|
- const baseColor = getBarBaseColor(name, isDrilldown)
|
|
|
|
|
|
|
+const getBarItemStyle = (name: string, isDrilldown: boolean, index: number) => {
|
|
|
|
|
+ const baseColor = getBarBaseColor(name, isDrilldown, index)
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
color: {
|
|
color: {
|
|
@@ -465,9 +498,11 @@ const socChartOption = computed<EChartsOption>(() => {
|
|
|
shadowOffsetY: 8
|
|
shadowOffsetY: 8
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- data: sourceData.map((item) => ({
|
|
|
|
|
|
|
+ data: sourceData.map((item, index) => ({
|
|
|
|
|
+ id: item.id,
|
|
|
|
|
+ name: item.name,
|
|
|
value: item.value,
|
|
value: item.value,
|
|
|
- itemStyle: getBarItemStyle(item.name, isDrilldown)
|
|
|
|
|
|
|
+ itemStyle: getBarItemStyle(item.name, isDrilldown, index)
|
|
|
})),
|
|
})),
|
|
|
label: {
|
|
label: {
|
|
|
show: true,
|
|
show: true,
|
|
@@ -482,37 +517,71 @@ const socChartOption = computed<EChartsOption>(() => {
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+let staticLoading = ref(false)
|
|
|
|
|
+async function loadChildChartData(drilldownKey: string, socClass: string | number) {
|
|
|
|
|
+ const res = await IotSocSummaryApi.getSocSummaryStatisticsChild({
|
|
|
|
|
+ deptId: queryParams.deptId || userStore.user.deptId,
|
|
|
|
|
+ observationDate: queryParams.observationDate,
|
|
|
|
|
+ socClass
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ currentDrilldownKey.value = drilldownKey
|
|
|
|
|
+ currentDrilldownId.value = socClass
|
|
|
|
|
+ childData.value = res?.total || res?.list || res || []
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function getStatic() {
|
|
async function getStatic() {
|
|
|
|
|
+ const drilldownKey = currentDrilldownKey.value
|
|
|
|
|
+ const drilldownId = currentDrilldownId.value
|
|
|
if (queryParams.deptId) {
|
|
if (queryParams.deptId) {
|
|
|
|
|
+ staticLoading.value = true
|
|
|
const res = await IotSocSummaryApi.getSocSummaryStatistics({
|
|
const res = await IotSocSummaryApi.getSocSummaryStatistics({
|
|
|
deptId: queryParams.deptId,
|
|
deptId: queryParams.deptId,
|
|
|
// 日期
|
|
// 日期
|
|
|
observationDate: queryParams.observationDate
|
|
observationDate: queryParams.observationDate
|
|
|
})
|
|
})
|
|
|
- child.value = res.child
|
|
|
|
|
- totalData.value = res.total
|
|
|
|
|
|
|
+ totalData.value = res.total || res || []
|
|
|
|
|
+ childData.value = []
|
|
|
|
|
+ if (drilldownKey && drilldownId !== undefined) {
|
|
|
|
|
+ await loadChildChartData(drilldownKey, drilldownId)
|
|
|
|
|
+ }
|
|
|
|
|
+ staticLoading.value = false
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ staticLoading.value = true
|
|
|
queryParams.deptId = userStore.user.deptId
|
|
queryParams.deptId = userStore.user.deptId
|
|
|
const res = await IotSocSummaryApi.getSocSummaryStatistics({
|
|
const res = await IotSocSummaryApi.getSocSummaryStatistics({
|
|
|
deptId: queryParams.deptId,
|
|
deptId: queryParams.deptId,
|
|
|
// 日期
|
|
// 日期
|
|
|
observationDate: queryParams.observationDate
|
|
observationDate: queryParams.observationDate
|
|
|
})
|
|
})
|
|
|
- child.value = res.child
|
|
|
|
|
- totalData.value = res.total
|
|
|
|
|
|
|
+ totalData.value = res.total || res || []
|
|
|
|
|
+ childData.value = []
|
|
|
|
|
+ if (drilldownKey && drilldownId !== undefined) {
|
|
|
|
|
+ await loadChildChartData(drilldownKey, drilldownId)
|
|
|
|
|
+ }
|
|
|
|
|
+ staticLoading.value = false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleChartClick = (params: any) => {
|
|
|
|
|
|
|
+const handleChartClick = async (params: any) => {
|
|
|
const name = params?.name
|
|
const name = params?.name
|
|
|
if (!name || currentDrilldownKey.value) return
|
|
if (!name || currentDrilldownKey.value) return
|
|
|
- if (DRILLDOWN_KEYS.some((item) => item === name)) {
|
|
|
|
|
- currentDrilldownKey.value = name
|
|
|
|
|
|
|
+ const target = totalChartData.value.find((item) => item.name === name)
|
|
|
|
|
+ const socClass = params?.data?.id ?? target?.id
|
|
|
|
|
+ if (DRILLDOWN_KEYS.some((item) => item === name) && socClass !== undefined) {
|
|
|
|
|
+ staticLoading.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ await loadChildChartData(name, socClass)
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ staticLoading.value = false
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const resetDrilldown = () => {
|
|
const resetDrilldown = () => {
|
|
|
currentDrilldownKey.value = ''
|
|
currentDrilldownKey.value = ''
|
|
|
|
|
+ currentDrilldownId.value = undefined
|
|
|
|
|
+ childData.value = []
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** 初始化 **/
|
|
/** 初始化 **/
|
|
@@ -645,17 +714,41 @@ onMounted(() => {
|
|
|
padding: 3px 16px 0px;
|
|
padding: 3px 16px 0px;
|
|
|
border: 1px solid rgba(143, 168, 211, 0.22);
|
|
border: 1px solid rgba(143, 168, 211, 0.22);
|
|
|
border-radius: 10px;
|
|
border-radius: 10px;
|
|
|
- background: radial-gradient(circle at top left, rgba(64, 158, 255, 0.18), transparent 0%),
|
|
|
|
|
- radial-gradient(circle at top right, rgba(208, 156, 255, 0.16), transparent 2%),
|
|
|
|
|
|
|
+ background: radial-gradient(
|
|
|
|
|
+ circle at 102% 104%,
|
|
|
|
|
+ rgba(142, 164, 255, 0.12) 0,
|
|
|
|
|
+ rgba(142, 164, 255, 0.12) 14%,
|
|
|
|
|
+ transparent 14.5%
|
|
|
|
|
+ ),
|
|
|
|
|
+ radial-gradient(
|
|
|
|
|
+ circle at 88% 16%,
|
|
|
|
|
+ rgba(118, 183, 255, 0.08) 0,
|
|
|
|
|
+ rgba(118, 183, 255, 0.08) 7%,
|
|
|
|
|
+ transparent 100%
|
|
|
|
|
+ ),
|
|
|
linear-gradient(135deg, #fdfefe 0%, #f3f8ff 68%, #eef4ff 100%);
|
|
linear-gradient(135deg, #fdfefe 0%, #f3f8ff 68%, #eef4ff 100%);
|
|
|
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
|
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ 0 12px 28px rgba(64, 112, 196, 0.08),
|
|
|
|
|
+ inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.soc-summary-panel::before {
|
|
.soc-summary-panel::before {
|
|
|
content: '';
|
|
content: '';
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
inset: 0;
|
|
inset: 0;
|
|
|
- background: linear-gradient(115deg, rgba(255, 255, 255, 0.22), transparent 42%),
|
|
|
|
|
|
|
+ background: radial-gradient(
|
|
|
|
|
+ circle at 76% -4%,
|
|
|
|
|
+ rgba(129, 224, 202, 0.14) 0,
|
|
|
|
|
+ rgba(129, 224, 202, 0.14) 12.8%,
|
|
|
|
|
+ transparent 13%
|
|
|
|
|
+ ),
|
|
|
|
|
+ radial-gradient(
|
|
|
|
|
+ circle at 101% 101%,
|
|
|
|
|
+ rgba(142, 164, 255, 0.1) 0,
|
|
|
|
|
+ rgba(142, 164, 255, 0.1) 15.8%,
|
|
|
|
|
+ transparent 16%
|
|
|
|
|
+ ),
|
|
|
|
|
+ linear-gradient(115deg, rgba(255, 255, 255, 0.22), transparent 42%),
|
|
|
repeating-linear-gradient(
|
|
repeating-linear-gradient(
|
|
|
135deg,
|
|
135deg,
|
|
|
rgba(255, 255, 255, 0.08) 0,
|
|
rgba(255, 255, 255, 0.08) 0,
|