|
|
@@ -51,8 +51,11 @@ type BottomCard = {
|
|
|
lines: string[]
|
|
|
}
|
|
|
|
|
|
+type SafeDayMap = Record<string, number>
|
|
|
+
|
|
|
const wrapperRef = ref<HTMLDivElement>()
|
|
|
const hazardChartRef = ref<HTMLDivElement>()
|
|
|
+const safeDayChartRef = ref<HTMLDivElement>()
|
|
|
const socChartRef = ref<HTMLDivElement>()
|
|
|
const scale = ref(1)
|
|
|
const supportsZoom = ref(false)
|
|
|
@@ -60,6 +63,7 @@ const supportsZoom = ref(false)
|
|
|
let resizeObserver: ResizeObserver | null = null
|
|
|
let resizeRaf = 0
|
|
|
let hazardChart: echarts.ECharts | null = null
|
|
|
+let safeDayChart: echarts.ECharts | null = null
|
|
|
let socChart: echarts.ECharts | null = null
|
|
|
|
|
|
const pageTitle = 'QHSE管理看板'
|
|
|
@@ -113,11 +117,6 @@ const hazardBars = ref([
|
|
|
{ label: '未整改', value: 0, color: '#ff981f' }
|
|
|
])
|
|
|
|
|
|
-const incidentStats = ref([
|
|
|
- { label: '安全事故', value: '0起', accent: '#2ac7c9' },
|
|
|
- { label: '安全生产天数', value: '3起', accent: '#f2c11a' }
|
|
|
-])
|
|
|
-
|
|
|
const riskZones: RiskZone[] = [
|
|
|
{ title: '高危风险区', desc: '危化库 / 试压区 / 配电房', color: '#ff4c49' },
|
|
|
{ title: '中风险区', desc: '焊接 / 机加 / 吊装区', color: '#ff981f' },
|
|
|
@@ -219,9 +218,11 @@ onMounted(() => {
|
|
|
resizeObserver.observe(wrapperRef.value)
|
|
|
}
|
|
|
initHazardChart()
|
|
|
+ initSafeDayChart()
|
|
|
initSocChart()
|
|
|
window.addEventListener('resize', updateScale)
|
|
|
window.addEventListener('resize', resizeHazardChart)
|
|
|
+ window.addEventListener('resize', resizeSafeDayChart)
|
|
|
window.addEventListener('resize', resizeSocChart)
|
|
|
})
|
|
|
|
|
|
@@ -229,9 +230,11 @@ onUnmounted(() => {
|
|
|
resizeObserver?.disconnect()
|
|
|
window.removeEventListener('resize', updateScale)
|
|
|
window.removeEventListener('resize', resizeHazardChart)
|
|
|
+ window.removeEventListener('resize', resizeSafeDayChart)
|
|
|
window.removeEventListener('resize', resizeSocChart)
|
|
|
cancelAnimationFrame(resizeRaf)
|
|
|
destroyHazardChart()
|
|
|
+ destroySafeDayChart()
|
|
|
destroySocChart()
|
|
|
})
|
|
|
|
|
|
@@ -364,6 +367,132 @@ function destroyHazardChart() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+function getSafeDayEntries() {
|
|
|
+ const safeDayMap = (safeDay.value ?? {}) as SafeDayMap
|
|
|
+
|
|
|
+ return Object.entries(safeDayMap)
|
|
|
+ .map(([label, value]) => ({
|
|
|
+ label,
|
|
|
+ value: Number(value) || 0
|
|
|
+ }))
|
|
|
+ .sort((a, b) => a.value - b.value)
|
|
|
+}
|
|
|
+
|
|
|
+function getSafeDayChartOption(): echarts.EChartsOption {
|
|
|
+ const entries = getSafeDayEntries()
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...ANIMATION,
|
|
|
+ grid: {
|
|
|
+ left: 92,
|
|
|
+ right: 18,
|
|
|
+ top: 12,
|
|
|
+ bottom: 12
|
|
|
+ },
|
|
|
+ tooltip: createTooltip({
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow',
|
|
|
+ shadowStyle: {
|
|
|
+ color: 'rgba(31, 91, 184, 0.08)'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ formatter(params: any) {
|
|
|
+ const item = Array.isArray(params) ? params[0] : params
|
|
|
+ return `${item.name}<br/>安全天数:${item.value}`
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ xAxis: {
|
|
|
+ type: 'value',
|
|
|
+ axisLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#263854',
|
|
|
+ fontSize: 12,
|
|
|
+ fontFamily: FONT_FAMILY
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: 'rgba(83, 114, 173, 0.6)',
|
|
|
+ type: 'dashed'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: entries.map((item) => item.label),
|
|
|
+ axisLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#16263d',
|
|
|
+ fontSize: 14,
|
|
|
+ fontWeight: 700,
|
|
|
+ fontFamily: FONT_FAMILY
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'bar',
|
|
|
+ data: entries.map((item) => item.value),
|
|
|
+ barWidth: 16,
|
|
|
+ showBackground: true,
|
|
|
+ backgroundStyle: {
|
|
|
+ color: 'rgba(108, 149, 228, 0.08)',
|
|
|
+ borderRadius: 6
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 6,
|
|
|
+ color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
|
|
+ { offset: 0, color: '#78a0ec' },
|
|
|
+ { offset: 1, color: '#6a90dd' }
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ itemStyle: {
|
|
|
+ shadowBlur: 16,
|
|
|
+ shadowColor: 'rgba(106, 144, 221, 0.34)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function initSafeDayChart() {
|
|
|
+ if (!safeDayChartRef.value) return
|
|
|
+ if (safeDayChart) {
|
|
|
+ safeDayChart.dispose()
|
|
|
+ }
|
|
|
+ safeDayChart = echarts.init(safeDayChartRef.value, undefined, {
|
|
|
+ renderer: CHART_RENDERER
|
|
|
+ })
|
|
|
+ safeDayChart.setOption(getSafeDayChartOption(), true)
|
|
|
+}
|
|
|
+
|
|
|
+function updateSafeDayChart() {
|
|
|
+ if (!safeDayChart) return
|
|
|
+ safeDayChart.setOption(getSafeDayChartOption(), true)
|
|
|
+}
|
|
|
+
|
|
|
+function resizeSafeDayChart() {
|
|
|
+ safeDayChart?.resize()
|
|
|
+}
|
|
|
+
|
|
|
+function destroySafeDayChart() {
|
|
|
+ if (safeDayChart) {
|
|
|
+ safeDayChart.dispose()
|
|
|
+ safeDayChart = null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
function getSocChartOption(): echarts.EChartsOption {
|
|
|
return {
|
|
|
...ANIMATION,
|
|
|
@@ -495,7 +624,7 @@ function formatPercent(numerator: number, denominator: number) {
|
|
|
}
|
|
|
|
|
|
const summaryPanel = ref<any>(null)
|
|
|
-let safeDay = ref<any>(null)
|
|
|
+const safeDay = ref<SafeDayMap>({})
|
|
|
onMounted(async () => {
|
|
|
summaryPanel.value = await getKanban()
|
|
|
|
|
|
@@ -517,16 +646,15 @@ onMounted(async () => {
|
|
|
hazardBars.value[1].value = summaryPanel.value.totalHazard - summaryPanel.value.todoHazard
|
|
|
hazardBars.value[2].value = summaryPanel.value.todoHazard
|
|
|
|
|
|
- incidentStats.value[0].value = `${summaryPanel.value.accident}起`
|
|
|
-
|
|
|
try {
|
|
|
- safeDay.value = await kanbanApi.getSafeDay(userStore.getUser.deptId)
|
|
|
+ safeDay.value = (await kanbanApi.getSafeDay(userStore.getUser.deptId)) || {}
|
|
|
} catch (error) {
|
|
|
console.log(error)
|
|
|
}
|
|
|
|
|
|
nextTick(() => {
|
|
|
updateHazardChart()
|
|
|
+ updateSafeDayChart()
|
|
|
})
|
|
|
})
|
|
|
</script>
|
|
|
@@ -605,21 +733,7 @@ onMounted(async () => {
|
|
|
<span class="icon-decorator"><span></span><span></span></span>
|
|
|
安全生产天数
|
|
|
</div>
|
|
|
- <div class="incident-panel">
|
|
|
- <div class="incident-graphic">
|
|
|
- <div class="incident-graphic__axis"></div>
|
|
|
- <div class="incident-graphic__area"></div>
|
|
|
- <div class="incident-graphic__line"></div>
|
|
|
- </div>
|
|
|
- <div class="incident-metrics">
|
|
|
- <div v-for="item in incidentStats" :key="item.label" class="incident-metric">
|
|
|
- <span :style="{ color: item.label === '安全生产天数' ? '#259745' : '' }"
|
|
|
- >{{ item.label }}:</span
|
|
|
- >
|
|
|
- <strong :style="{ color: item.accent }">{{ item.value }}</strong>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div ref="safeDayChartRef" class="safe-day-chart-panel"></div>
|
|
|
</section>
|
|
|
</div>
|
|
|
|
|
|
@@ -787,64 +901,9 @@ onMounted(async () => {
|
|
|
height: 188px;
|
|
|
}
|
|
|
|
|
|
-.incident-panel {
|
|
|
- display: grid;
|
|
|
- margin-top: 50px;
|
|
|
- grid-template-columns: 180px 1fr;
|
|
|
- align-items: center;
|
|
|
- gap: 28px;
|
|
|
-}
|
|
|
-
|
|
|
-.incident-graphic {
|
|
|
- position: relative;
|
|
|
- width: 170px;
|
|
|
- height: 132px;
|
|
|
-}
|
|
|
-
|
|
|
-.incident-graphic__axis {
|
|
|
- position: absolute;
|
|
|
- inset: 24px 16px 16px 16px;
|
|
|
- border-bottom: 6px solid #4f8dff;
|
|
|
- border-left: 6px solid #4f8dff;
|
|
|
- border-radius: 2px;
|
|
|
- opacity: 0.92;
|
|
|
-}
|
|
|
-
|
|
|
-.incident-graphic__area {
|
|
|
- position: absolute;
|
|
|
- right: 22px;
|
|
|
- bottom: 24px;
|
|
|
- width: 102px;
|
|
|
- height: 68px;
|
|
|
- background: linear-gradient(180deg, rgb(79 141 255 / 88%) 0%, rgb(79 141 255 / 28%) 100%);
|
|
|
- clip-path: polygon(0 100%, 22% 50%, 54% 72%, 100% 0, 100% 100%);
|
|
|
-}
|
|
|
-
|
|
|
-.incident-graphic__line {
|
|
|
- position: absolute;
|
|
|
- right: 22px;
|
|
|
- bottom: 24px;
|
|
|
- width: 102px;
|
|
|
- height: 68px;
|
|
|
- border-top: 5px solid #4f8dff;
|
|
|
- clip-path: polygon(0 100%, 22% 50%, 54% 72%, 100% 0, 100% 5%, 54% 77%, 22% 55%, 0 100%);
|
|
|
-}
|
|
|
-
|
|
|
-.incident-metrics {
|
|
|
- display: grid;
|
|
|
- gap: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.incident-metric {
|
|
|
- font-size: 24px;
|
|
|
- font-weight: 700;
|
|
|
- color: #556b89;
|
|
|
-}
|
|
|
-
|
|
|
-.incident-metric strong {
|
|
|
- margin-left: 4px;
|
|
|
- font-size: 34px;
|
|
|
- line-height: 1;
|
|
|
+.safe-day-chart-panel {
|
|
|
+ height: 228px;
|
|
|
+ // margin-top: 18px;
|
|
|
}
|
|
|
|
|
|
.panel-title--center {
|