|
@@ -49,6 +49,20 @@
|
|
|
|
|
|
|
|
<!-- 列表 -->
|
|
<!-- 列表 -->
|
|
|
<ContentWrap class="flex-1 overflow-hidden mt-15px" style="border: none">
|
|
<ContentWrap class="flex-1 overflow-hidden mt-15px" style="border: none">
|
|
|
|
|
+ <div class="stats-cards">
|
|
|
|
|
+ <div class="stats-card stats-card--expired">
|
|
|
|
|
+ <div class="stats-card__label">已过期</div>
|
|
|
|
|
+ <div class="stats-card__value text-[40px]! pt-10">{{ expired }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="stats-card stats-card--warn">
|
|
|
|
|
+ <div class="stats-card__label">90天预警</div>
|
|
|
|
|
+ <div class="stats-card__value text-[40px]! pt-10">{{ warn }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="stats-chart-card">
|
|
|
|
|
+ <div class="stats-card__label">分类统计</div>
|
|
|
|
|
+ <div ref="staticChartRef" class="stats-chart"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
<zm-table
|
|
<zm-table
|
|
|
:loading="loading"
|
|
:loading="loading"
|
|
|
:data="list"
|
|
:data="list"
|
|
@@ -376,6 +390,7 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
|
|
+import * as echarts from 'echarts'
|
|
|
import { IotInstrumentApi, IotMeasureDetectApi } from '@/api/pms/qhse/index'
|
|
import { IotInstrumentApi, IotMeasureDetectApi } from '@/api/pms/qhse/index'
|
|
|
import DeptTree from '@/views/system/user/DeptTree2.vue'
|
|
import DeptTree from '@/views/system/user/DeptTree2.vue'
|
|
|
import { handleTree } from '@/utils/tree'
|
|
import { handleTree } from '@/utils/tree'
|
|
@@ -385,10 +400,13 @@ import { ElMessageBox } from 'element-plus'
|
|
|
const deptList = ref<Tree[]>([]) // 树形结构
|
|
const deptList = ref<Tree[]>([]) // 树形结构
|
|
|
const deptList2 = ref<Tree[]>([]) // 树形结构
|
|
const deptList2 = ref<Tree[]>([]) // 树形结构
|
|
|
import { formatDate } from '@/utils/formatTime'
|
|
import { formatDate } from '@/utils/formatTime'
|
|
|
|
|
+import { useUserStore } from '@/store/modules/user'
|
|
|
import { DICT_TYPE, getStrDictOptions, getBoolDictOptions } from '@/utils/dict'
|
|
import { DICT_TYPE, getStrDictOptions, getBoolDictOptions } from '@/utils/dict'
|
|
|
import { useTableComponents } from '@/components/ZmTable/useTableComponents'
|
|
import { useTableComponents } from '@/components/ZmTable/useTableComponents'
|
|
|
const { ZmTable, ZmTableColumn } = useTableComponents()
|
|
const { ZmTable, ZmTableColumn } = useTableComponents()
|
|
|
|
|
|
|
|
|
|
+const userStore = useUserStore()
|
|
|
|
|
+
|
|
|
defineOptions({ name: 'IotQHSEMeasure' })
|
|
defineOptions({ name: 'IotQHSEMeasure' })
|
|
|
|
|
|
|
|
const loading = ref(true) // 列表的加载中
|
|
const loading = ref(true) // 列表的加载中
|
|
@@ -400,8 +418,14 @@ const isLeftContentCollapsed = ref(false)
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
+type StaticItem = {
|
|
|
|
|
+ classify: string
|
|
|
|
|
+ count: number
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const list = ref([]) // 列表的数据
|
|
const list = ref([]) // 列表的数据
|
|
|
const total = ref(0) // 列表的总页数
|
|
const total = ref(0) // 列表的总页数
|
|
|
|
|
+const staticChartRef = ref<HTMLDivElement>()
|
|
|
const queryParams = reactive({
|
|
const queryParams = reactive({
|
|
|
pageNo: 1,
|
|
pageNo: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
@@ -543,6 +567,7 @@ const handleExport = async () => {
|
|
|
const handleDeptNodeClick = async (row) => {
|
|
const handleDeptNodeClick = async (row) => {
|
|
|
queryParams.deptId = row.id
|
|
queryParams.deptId = row.id
|
|
|
await getList()
|
|
await getList()
|
|
|
|
|
+ await getStatic()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** 搜索按钮操作 */
|
|
/** 搜索按钮操作 */
|
|
@@ -774,11 +799,134 @@ const handleDownload = async (url) => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+let staticData = ref<StaticItem[]>([])
|
|
|
|
|
+let expired = ref(0)
|
|
|
|
|
+let warn = ref(0)
|
|
|
|
|
+let staticChart: echarts.ECharts | null = null
|
|
|
|
|
+
|
|
|
|
|
+function getStaticChartOption(): echarts.EChartsOption {
|
|
|
|
|
+ return {
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {
|
|
|
|
|
+ type: 'shadow'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ grid: {
|
|
|
|
|
+ left: 16,
|
|
|
|
|
+ right: 16,
|
|
|
|
|
+ top: 24,
|
|
|
|
|
+ bottom: 16,
|
|
|
|
|
+ containLabel: true
|
|
|
|
|
+ },
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ data: staticData.value.map((item) => item.classify),
|
|
|
|
|
+ axisTick: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLine: {
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#d9e2ef'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ color: '#6b7280',
|
|
|
|
|
+ fontSize: 12,
|
|
|
|
|
+ interval: 0
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ axisLine: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisTick: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ color: '#94a3b8',
|
|
|
|
|
+ fontSize: 12
|
|
|
|
|
+ },
|
|
|
|
|
+ splitLine: {
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#edf2f7',
|
|
|
|
|
+ type: 'dashed'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ barWidth: 32,
|
|
|
|
|
+ data: staticData.value.map((item) => item.count),
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: [8, 8, 0, 0],
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
+ { offset: 0, color: '#79b8ff' },
|
|
|
|
|
+ { offset: 1, color: '#2f78f6' }
|
|
|
|
|
+ ])
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: true,
|
|
|
|
|
+ position: 'top',
|
|
|
|
|
+ color: '#2563eb',
|
|
|
|
|
+ fontSize: 12,
|
|
|
|
|
+ fontWeight: 700
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function renderStaticChart() {
|
|
|
|
|
+ if (!staticChartRef.value) return
|
|
|
|
|
+ if (!staticChart) {
|
|
|
|
|
+ staticChart = echarts.init(staticChartRef.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ staticChart.setOption(getStaticChartOption(), true)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function resizeStaticChart() {
|
|
|
|
|
+ staticChart?.resize()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function destroyStaticChart() {
|
|
|
|
|
+ if (staticChart) {
|
|
|
|
|
+ staticChart.dispose()
|
|
|
|
|
+ staticChart = null
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function getStatic() {
|
|
|
|
|
+ if (queryParams.deptId) {
|
|
|
|
|
+ const res = await IotInstrumentApi.getInstrumentStatistics(queryParams.deptId)
|
|
|
|
|
+ staticData.value = res.classify
|
|
|
|
|
+ expired.value = res.expired
|
|
|
|
|
+ warn.value = res.warn
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const res = await IotInstrumentApi.getInstrumentStatistics(userStore.user.deptId)
|
|
|
|
|
+ staticData.value = res.classify
|
|
|
|
|
+ expired.value = res.expired
|
|
|
|
|
+ warn.value = res.warn
|
|
|
|
|
+ }
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ renderStaticChart()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
getList()
|
|
getList()
|
|
|
|
|
+ getStatic()
|
|
|
|
|
|
|
|
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
|
deptList2.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
deptList2.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
|
|
|
+ window.addEventListener('resize', resizeStaticChart)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+onUnmounted(() => {
|
|
|
|
|
+ window.removeEventListener('resize', resizeStaticChart)
|
|
|
|
|
+ destroyStaticChart()
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -790,6 +938,68 @@ onMounted(async () => {
|
|
|
height: 700px !important;
|
|
height: 700px !important;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.stats-cards {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: 180px 180px minmax(0, 1fr);
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+ align-items: stretch;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card {
|
|
|
|
|
+ padding: 14px 16px;
|
|
|
|
|
+ background: linear-gradient(180deg, #ffffff 0%, #f7faff 100%);
|
|
|
|
|
+ border: 1px solid #e4ecf7;
|
|
|
|
|
+ border-radius: 10px;
|
|
|
|
|
+ box-shadow: 0 4px 12px rgb(31 91 184 / 8%);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card__label {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #6b7280;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card__value {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ font-size: 28px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ color: #1f5bb8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card--expired {
|
|
|
|
|
+ background: linear-gradient(180deg, #fff4f4 0%, #ffe8e8 100%);
|
|
|
|
|
+ border-color: #ffcfcf;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card--expired .stats-card__value {
|
|
|
|
|
+ color: #de3b3b;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card--warn {
|
|
|
|
|
+ background: linear-gradient(180deg, #fff8ef 0%, #ffeed9 100%);
|
|
|
|
|
+ border-color: #ffd7a1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card--warn .stats-card__value {
|
|
|
|
|
+ color: #d97706;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-chart-card {
|
|
|
|
|
+ padding: 14px 16px 10px;
|
|
|
|
|
+ background: linear-gradient(180deg, #ffffff 0%, #f7faff 100%);
|
|
|
|
|
+ border: 1px solid #e4ecf7;
|
|
|
|
|
+ border-radius: 10px;
|
|
|
|
|
+ box-shadow: 0 4px 12px rgb(31 91 184 / 8%);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-chart {
|
|
|
|
|
+ height: 130px;
|
|
|
|
|
+ margin-top: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/* 过期行的红色背景 - 基础状态 */
|
|
/* 过期行的红色背景 - 基础状态 */
|
|
|
:deep(.el-table__body tr.expired-row > td.el-table__cell) {
|
|
:deep(.el-table__body tr.expired-row > td.el-table__cell) {
|
|
|
background-color: #ffe6e6 !important;
|
|
background-color: #ffe6e6 !important;
|