yanghao 4 dienas atpakaļ
vecāks
revīzija
60cd7716f0

+ 13 - 2
src/views/pms/qhse/certPerson/CertPersonForm.vue

@@ -6,6 +6,17 @@
       :rules="formRules"
       label-width="100px"
       v-loading="formLoading">
+      <el-form-item label="所属队伍" prop="deptId">
+        <el-tree-select
+          clearable
+          v-model="formData.deptId"
+          :data="deptList2"
+          :props="defaultProps"
+          :check-strictly="false"
+          node-key="id"
+          filterable
+          placeholder="请选择所在部门" />
+      </el-form-item>
       <el-form-item label="姓名" prop="nickname">
         <el-input v-model="formData.nickname" placeholder="请输入姓名" />
       </el-form-item>
@@ -30,7 +41,7 @@
 </template>
 <script setup lang="ts">
 import { CertPersonApi } from '@/api/pms/qhse/index'
-import { handleTree } from '@/utils/tree'
+import { handleTree, defaultProps } from '@/utils/tree'
 import * as DeptApi from '@/api/system/dept'
 import { useUserStore } from '@/store/modules/user'
 import { ElMessageBox } from 'element-plus'
@@ -58,7 +69,7 @@ const formData = ref({
 })
 const formRules = reactive({
   nickname: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
-
+  deptId: [{ required: true, message: '请选择所属队伍', trigger: 'blur' }],
   mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
   postName: [{ required: true, message: '请输入岗位名称', trigger: 'blur' }]
 })

+ 1 - 1
src/views/pms/qhse/jsa/IotSocSummaryForm.vue

@@ -12,7 +12,7 @@
           v-model="formData.deptId"
           :data="deptList2"
           :props="defaultProps"
-          check-strictly
+          :check-strictly="false"
           node-key="id"
           filterable
           placeholder="请选择所在部门" />

+ 141 - 82
src/views/pms/qhse/kanban/index.vue

@@ -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 {

+ 1 - 1
src/views/pms/qhse/ptw/index.vue

@@ -176,7 +176,7 @@
           v-model="formData.deptId"
           :data="deptList2"
           :props="defaultProps"
-          check-strictly
+          :check-strictly="false"
           node-key="id"
           filterable
           placeholder="请选择所在部门" />

+ 10 - 15
src/views/pms/qhse/socSummary/IotSocSummaryForm.vue

@@ -5,8 +5,7 @@
       :model="formData"
       :rules="formRules"
       label-width="100px"
-      v-loading="formLoading"
-    >
+      v-loading="formLoading">
       <el-form-item label="队伍" prop="deptId">
         <el-tree-select
           clearable
@@ -16,9 +15,7 @@
           :check-strictly="false"
           node-key="id"
           filterable
-          placeholder="请选择所在队伍"
-          @node-click="handleNodeClick"
-        />
+          placeholder="请选择所在队伍" />
       </el-form-item>
 
       <el-form-item label="观察日期" prop="observationDate">
@@ -27,8 +24,7 @@
           type="date"
           value-format="x"
           placeholder="选择观察日期"
-          style="width: 100%"
-        />
+          style="width: 100%" />
       </el-form-item>
       <el-form-item label="soc类型" prop="socClass">
         <el-tree-select
@@ -40,8 +36,7 @@
           node-key="id"
           filterable
           multiple
-          placeholder="请选择soc类型"
-        />
+          placeholder="请选择soc类型" />
       </el-form-item>
 
       <el-form-item label="姓名" prop="userName">
@@ -155,12 +150,12 @@ const submitForm = async () => {
   }
 }
 
-const handleNodeClick = (data: Tree) => {
-  if (data.type !== '3') {
-    ElMessage.warning('只能选择队伍')
-    return
-  }
-}
+// const handleNodeClick = (data: Tree) => {
+//   if (data.type !== '3') {
+//     ElMessage.warning('只能选择队伍')
+//     return
+//   }
+// }
 
 /** 重置表单 */
 const resetForm = () => {