ソースを参照

完成告警综合信息页面及详情页面

Yue 2 年 前
コミット
e0521a78cb

+ 58 - 1
auto-imports.d.ts

@@ -4,5 +4,62 @@
 // Generated by unplugin-auto-import
 export {}
 declare global {
-  const ElCascader: typeof import('element-plus/es')['ElCascader']
+
+}
+onst EffectScope: typeof import('vue')['EffectScope']
+  const computed: typeof import('vue')['computed']
+  const createApp: typeof import('vue')['createApp']
+  const customRef: typeof import('vue')['customRef']
+  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
+  const defineComponent: typeof import('vue')['defineComponent']
+  const effectScope: typeof import('vue')['effectScope']
+  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
+  const getCurrentScope: typeof import('vue')['getCurrentScope']
+  const h: typeof import('vue')['h']
+  const inject: typeof import('vue')['inject']
+  const isProxy: typeof import('vue')['isProxy']
+  const isReactive: typeof import('vue')['isReactive']
+  const isReadonly: typeof import('vue')['isReadonly']
+  const isRef: typeof import('vue')['isRef']
+  const markRaw: typeof import('vue')['markRaw']
+  const nextTick: typeof import('vue')['nextTick']
+  const onActivated: typeof import('vue')['onActivated']
+  const onBeforeMount: typeof import('vue')['onBeforeMount']
+  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
+  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
+  const onDeactivated: typeof import('vue')['onDeactivated']
+  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
+  const onMounted: typeof import('vue')['onMounted']
+  const onRenderTracked: typeof import('vue')['onRenderTracked']
+  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
+  const onScopeDispose: typeof import('vue')['onScopeDispose']
+  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
+  const onUnmounted: typeof import('vue')['onUnmounted']
+  const onUpdated: typeof import('vue')['onUpdated']
+  const provide: typeof import('vue')['provide']
+  const reactive: typeof import('vue')['reactive']
+  const readonly: typeof import('vue')['readonly']
+  const ref: typeof import('vue')['ref']
+  const resolveComponent: typeof import('vue')['resolveComponent']
+  const shallowReactive: typeof import('vue')['shallowReactive']
+  const shallowReadonly: typeof import('vue')['shallowReadonly']
+  const shallowRef: typeof import('vue')['shallowRef']
+  const toRaw: typeof import('vue')['toRaw']
+  const toRef: typeof import('vue')['toRef']
+  const toRefs: typeof import('vue')['toRefs']
+  const triggerRef: typeof import('vue')['triggerRef']
+  const unref: typeof import('vue')['unref']
+  const useAttrs: typeof import('vue')['useAttrs']
+  const useCssModule: typeof import('vue')['useCssModule']
+  const useCssVars: typeof import('vue')['useCssVars']
+  const useSlots: typeof import('vue')['useSlots']
+  const watch: typeof import('vue')['watch']
+  const watchEffect: typeof import('vue')['watchEffect']
+  const watchPostEffect: typeof import('vue')['watchPostEffect']
+  const watchSyncEffect: typeof import('vue')['watchSyncEffect']
+}
+// for type re-export
+declare global {
+  // @ts-ignore
+  export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
 }

+ 1 - 1
src/assets/sass/_table.scss

@@ -1,6 +1,6 @@
 .vb-table{
   --table-footer-height:50px;
-  --table-radius:10px;
+  --table-radius:var(--bs-card-inner-border-radius);
   --table-bg:#fefefe;
   width: 100%;
   position: relative;

+ 17 - 9
src/components/Table/VbDataTable.vue

@@ -27,6 +27,7 @@ const props = withDefaults(
     sortField?: string //排序字段
     sortOrder?: "asc" | "desc"
     pagination?: boolean //是否分页
+    noPage?: boolean //查询不带分页参数
     loading?: boolean //加载中
     fixedColumn?: boolean //固定列
     fixedNumber?: number //左边固定列数
@@ -63,6 +64,7 @@ const props = withDefaults(
     checkMultiple: false,
     sortOrder: "asc",
     pagination: true,
+    noPage: false,
     loading: false,
     fixedColumn: false,
     fixedNumber: 0,
@@ -151,15 +153,21 @@ function remote() {
   loading.value = true
   remoteData.value = []
 
-  const configs = Object.assign({ url: props.url, successAlert: false }, props.configs, {
-    data: {
-      pageSize: itemsInTable.value,
-      pageIndex: currentPage.value,
-      params: queryParms.value,
-      //sort: `${props.sortLabel ? `${props.sortLabel} ${props.sortOrder}` : undefined} `,
-    },
-  })
-  //console.log(configs.data)
+  const configs = Object.assign(
+    { url: props.url, successAlert: false },
+    props.configs,
+    props.noPage
+      ? { data: queryParms.value }
+      : {
+          data: {
+            pageSize: itemsInTable.value,
+            pageIndex: currentPage.value,
+            params: queryParms.value,
+            //sort: `${props.sortLabel ? `${props.sortLabel} ${props.sortOrder}` : undefined} `,
+          },
+        }
+  )
+  console.log("======", props.noPage, configs.data)
   if (props.method.toLocaleLowerCase() == "post") {
     Rs.post(props.url + "", configs)
       .then((res: any) => {

+ 1 - 0
src/components/Table/VbTreeTable.vue

@@ -139,6 +139,7 @@ defineExpose({ search })
     @on-sort="onSort"
     @on-items-select="onItemSelect"
     @on-items-per-page-change="onItemsPerPageChange"
+    v-bind="$attrs"
   >
     <template v-for="(_, name) in $slots" #[name]="{ row: item }">
       <slot :name="name" :row="item" />

+ 1 - 0
src/components/Table/table-partials/TableFooter.vue

@@ -78,6 +78,7 @@ const pageCount = computed(() => {
     />
     <TableItemsPerPageSelect
       :current-page="page"
+      :total="total"
       :page-count="pageCount"
       v-model:itemsPerPage="itemsCountInTable"
       :items-per-page-dropdown-enabled="itemsPerPageDropdownEnabled"

+ 2 - 1
src/components/Table/table-partials/table-content/table-footer/TableItemsPerPageSelect.vue

@@ -3,6 +3,7 @@ import { toRefs, withDefaults, computed, type WritableComputedRef } from "vue"
 
 const props = withDefaults(
   defineProps<{
+    total: number
     itemsPerPage: number
     currentPage: number
     pageCount: number
@@ -49,7 +50,7 @@ const itemsCountInTable: WritableComputedRef<number> = computed({
           <option v-for="(v, i) in itemsPerPageDropdownArray" :key="i" :value="v">{{ v }}</option>
         </select>
       </label>
-      条
+      条,共 {{ total }} 条记录
     </span>
   </div>
 </template>

+ 17 - 15
src/components/select/DateRangeSelect.vue

@@ -10,6 +10,7 @@ const props = withDefaults(
     dateValue: [Date, Date]
     selectValue: number | string
     dateArray?: Array<DateSelectOption>
+    customDateSelectVlaue?: number
     label?: string
     rangeSeparator?: string
     placeholder?: string
@@ -33,13 +34,13 @@ const props = withDefaults(
           value: [moment(new Date()).add(-3, "d").toDate(), new Date()],
         },
         {
-          text: "近一周",
+          text: "周",
           value: [moment(new Date()).add(-7, "d").toDate(), new Date()],
         },
-        {
-          text: "近一月",
-          value: [moment(new Date()).add(-1, "M").toDate(), new Date()],
-        },
+        // {
+        //   text: "近一月",
+        //   value: [moment(new Date()).add(-1, "M").toDate(), new Date()],
+        // },
         {
           text: "本月",
           value: [moment(new Date()).startOf("month").toDate(), new Date()],
@@ -55,6 +56,7 @@ const props = withDefaults(
     rangeSeparator: "~",
     selectClass: "mb-0",
     dateClass: "mb-0",
+    customDateSelectVlaue: 4,
   }
 )
 const emits = defineEmits<{
@@ -62,12 +64,15 @@ const emits = defineEmits<{
   (e: "update:selectValue", v: string | number): void
   (e: "change", v: [Date, Date]): void
 }>()
-const dateStr = ref(``)
 const { dateValue, selectValue } = toRefs(props)
-const defaultDateValue = ref<[Date, Date]>([moment(dateValue.value[0]).add(-1, "d").toDate(), dateValue.value[1]])
+const dateStr = ref(
+  `${moment(dateValue.value[0]).format("YYYY/MM/dd")}${props.rangeSeparator}${moment(dateValue.value[1]).format(
+    "YYYY/MM/dd"
+  )}`
+)
+const defaultDateValue = ref<[Date, Date]>([moment(dateValue.value[0]).add(-1, "M").toDate(), dateValue.value[0]])
 const optionArray = ref<Array<string>>([])
 const dateArray = ref<Array<[Date, Date]>>([])
-const showDate = ref(false)
 function checkDate(v: Date) {
   if (props.disabledDate) {
     return props.disabledDate(v)
@@ -76,10 +81,7 @@ function checkDate(v: Date) {
   }
 }
 function selectChange(v: any) {
-  if (v == -1) {
-    showDate.value = true
-  } else {
-    showDate.value = false
+  if (v != props.customDateSelectVlaue) {
     emits("update:dateValue", dateArray.value[Number(v)])
     emits("change", dateArray.value[Number(v)])
   }
@@ -105,12 +107,12 @@ onMounted(() => {
 </script>
 <template>
   <el-form-item :label="label" :class="selectClass">
-    <el-select v-model="selectValue" :placeholder="placeholder" :size="size" @change="selectChange">
+    <el-select v-model="selectValue" :placeholder="placeholder" :size="size" @change="selectChange" v-bind="$attrs">
       <el-option v-for="(item, index) in optionArray" :key="index" :label="item" :value="index" />
-      <el-option label="自定义日期" :value="-1" />
+      <el-option label="自定义日期" :value="4" />
     </el-select>
   </el-form-item>
-  <el-form-item v-if="showDate" label="自定义日期" :class="dateClass">
+  <el-form-item v-if="customDateSelectVlaue == selectValue" label="自定义日期" :class="dateClass">
     <el-date-picker
       v-model="dateStr"
       type="daterange"

+ 5 - 2
src/components/select/DySelectTree.vue

@@ -88,8 +88,10 @@ function init() {
         const item = formatData(v)
         options.value.push(item)
       })
-      const val = options.value[0].id
-      emits("update:value", val)
+      if (!value.value) {
+        const val = options.value[0].id
+        emits("update:value", val)
+      }
     })
   }
 }
@@ -113,6 +115,7 @@ onMounted(() => {
     :searchable="searchable"
     :name="name"
     :default-expand-level="defaultExpandLevel"
+    v-bind="$attrs"
     @select="onSelect"
   />
 </template>

+ 110 - 62
src/core/charts/chartHelper.ts

@@ -94,23 +94,42 @@ const lineBarSeriesOption = (chart: any, serieData: Array<any>, serieType: strin
   const series = []
   let yAxisMax = 0
   let maxData = 0
+  let showLabel = getOption(chartOptions, "showLabel", false)
+  //const markPointStyles = getOption(chartOptions, "markPointStyles", [])
+  //const markPointLabel = getOption(chartOptions, "markPointLabel", {})
+  const markLineStyles = getOption(chartOptions, "markLineStyles", [
+    {
+      color: "#FF5147",
+    },
+  ])
+  const markLineLabel = getOption(chartOptions, "markLineLabel", {
+    color: "#FF5147",
+  })
+  const markAreaStyles = getOption(chartOptions, "markAreaStyles", {
+    color: "rgba(255,81,71,0.2)",
+  })
+  const markAreaLabel = getOption(chartOptions, "markAreaLabel", {
+    color: "#F2637B",
+    offset: [31, 0],
+  })
+  const labelFormatter = getOption(chartOptions, "labelFormatter", undefined)
+  const markPoint = getOption(chartOptions, "markPoint", {})
+  const markLine = getOption(chartOptions, "markLine", {})
+  const markArea = getOption(chartOptions, "markArea", {})
+  const barGap = getOption(chartOptions, "barGap", defaultOption.SIZE.barGap)
+  const symbol = getOption(chartOptions, "symbol", "circle")
+  const smooth = getOption(chartOptions, "smooth", 0.5)
+  const areaStyleColors = getOption(chartOptions, "areaStyle", [
+    "rgba(56,164,90,.5)",
+    "rgb(76,136,207,.3)",
+    "rgb(255,185,126,.3)",
+  ])
+  const dataMarkLine = getOption(chart, "markLine", {})
+  const dataMarkArea = getOption(chart, "markArea", {})
   serieData.forEach((v: any, i: number) => {
     legend.data.push({
       name: v.name,
     })
-    let showLabel = getOption(chartOptions, "showLabel", false)
-    // (params: any) => {
-    //   if (params.value == undefined || params.value !== params.value) {
-    //     params.value = 0
-    //   }
-    //   if (totalArr[params.dataIndex] == 0) {
-    //     return ""
-    //   } else {
-    //     const percent = ((params.value / totalArr[params.dataIndex]) * 100).toFixed(2)
-    //     return percent + "%"
-    //   }
-    // }
-    const labelFormatter = getOption(chartOptions, "labelFormatter", undefined)
     if (typeof labelFormatter == "function") {
       showLabel = true
     }
@@ -121,9 +140,10 @@ const lineBarSeriesOption = (chart: any, serieData: Array<any>, serieType: strin
       name: v.name,
       data: v.data,
       stack: has(v, "stack") ? v.stack : null,
-      markPoint: getOption(chartOptions, "markPoint", {}),
-      markLine: getOption(chartOptions, "markLine", {}),
-      markArea: getOption(chartOptions, "markArea", {}),
+      itemStyle: getOption(v, "itemStyle", {}),
+      markPoint,
+      markLine,
+      markArea,
       label: {
         show: showLabel,
         position: "",
@@ -136,18 +156,18 @@ const lineBarSeriesOption = (chart: any, serieData: Array<any>, serieType: strin
       type == "bar"
         ? {
             barWidth: defaultOption.SIZE.barWidth,
-            barGap: getOption(chartOptions, "barGap", defaultOption.SIZE.barGap),
+            barGap,
             //barMinWidth: defaultOption.SIZE.barMinWidth,
             barMaxWidth: defaultOption.SIZE.barMaxWidth,
           }
         : {
-            symbol: getOption(chartOptions, "symbol", "circle"),
+            symbol,
             symbolSize: defaultOption.SIZE.symbolSize,
             connectNulls: true,
-            smooth: getOption(chartOptions, "smooth", 0.5),
+            smooth,
             smoothMonotone: "x",
             areaStyle: {
-              color: getOption(chartOptions, "areaStyle", [])[i],
+              color: areaStyleColors[i],
             },
           }
     )
@@ -159,26 +179,35 @@ const lineBarSeriesOption = (chart: any, serieData: Array<any>, serieType: strin
       })
       yAxisMax = Math.floor(maxData * 1.2)
     }
-
+    if (dataMarkLine) {
+      seriesOption.markLine = dataMarkLine
+    }
     if (has(chart, "warning")) {
       const markLine: any = {
         silent: true,
-        label: getOption(chartOptions, "markLineLabel", {}),
+        label: markLineLabel,
         data: [
           {
             yAxis: chart.warning,
           },
         ],
       }
-      if (has(chart, "markLineStyles") && isArray(chart.markLineStyles)) {
-        markLine.lineStyle = chart.markLineStyles[i % chart.markLineStyles.length]
-        delete chartOptions.markLineStyles
+      if (markLineStyles && markLineStyles.length) {
+        markLine.lineStyle = markLineStyles[i % markLineStyles.length]
       }
       if (chart.warning > maxData) {
         yAxisMax = Math.floor(chart.warning * 1.1)
       }
       seriesOption.markLine = markLine
     }
+
+    if (dataMarkArea) {
+      seriesOption.markArea = {
+        data: dataMarkArea,
+        label: markAreaLabel,
+        itemStyle: markAreaStyles,
+      }
+    }
     const seriesItem = extendDeep(seriesOption, chartOptions.series || {})
 
     series.push(seriesItem)
@@ -308,7 +337,7 @@ const pieSeriesOption = (chart: any, serieData: Array<any>, serieType: string, c
       title.text = `{title|${titleText}}`
     }
   }
-
+  const itemStyle = getOption(chartOptions, "pieItemStyle", undefined)
   serieData.forEach((v: any) => {
     const name = showPercent ? `${v.name}:${getPercent(total, v.value)}` : v.name
     legend.data.push({
@@ -317,7 +346,7 @@ const pieSeriesOption = (chart: any, serieData: Array<any>, serieType: string, c
     const item = {
       name,
       value: v.value,
-      itemStyle: getOption(chartOptions, "pieItemStyle", undefined),
+      itemStyle,
     }
     data.push(item)
   })
@@ -326,8 +355,7 @@ const pieSeriesOption = (chart: any, serieData: Array<any>, serieType: string, c
   return { legend, title, tooltip, series }
 }
 
-export default {
-  /**
+/**
    *
    * @param data 远程数据
    * @param options 为eChart的配置项
@@ -349,48 +377,68 @@ export default {
    * @param serieType 图表类型
    * @returns eChart的配置项
    */
-  chart(data: any, options: any, serieType?: string) {
-    if (data == null) {
-      throw new Error("no data")
-    }
-    const chartOptions = extendDeep({}, options)
-    serieType = getOption(chartOptions, "serieType", serieType)
-    const _chart = compatibleData(data)
-    const _series = compatibleSeries(_chart)
-    //const _xAxisData = has(_chart, "categories") ? _chart.categories : []
+const chart = (data: any, options: any, serieType?: string) => {
+  if (data == null) {
+    throw new Error("NO CHART DATA")
+  }
+  const _chart = compatibleData(data)
+  const _series = compatibleSeries(_chart)
+  const chartOptions = extendDeep({}, options)
+  serieType = getOption(chartOptions, "serieType", serieType)
+  //const _xAxisData = has(_chart, "categories") ? _chart.categories : []
 
-    //标题默认在图表下居中 ,使用远程数据 yzTitle 字段
-    const title = {
-      show: true,
-      text: getOption(_chart, "title", "") || getOption(_chart, "yzTitle", ""),
-    }
-    const grid = Object.assign({}, defaultOption.SIZE.grid)
+  //标题默认在图表下居中 ,使用远程数据 yzTitle 字段
+  const title = {
+    show: true,
+    text: getOption(_chart, "title", "") || getOption(_chart, "yzTitle", ""),
+  }
+  const grid = Object.assign({}, defaultOption.SIZE.grid)
 
-    let seriesOption = {}
-    switch (serieType) {
-      case "bar":
-      case "line":
-        seriesOption = lineBarSeriesOption(_chart, _series, serieType ?? "bar", chartOptions)
-        break
-      case "pie":
-        seriesOption = pieSeriesOption(_chart, _series, serieType ?? "pie", chartOptions)
-        break
-    }
-    const option = extendDeep({ title, grid }, seriesOption, chartOptions)
-    return option
-    //return Object.assign(true, {}, opt, options || {})
-  },
+  let seriesOption = {}
+  switch (serieType) {
+    case "bar":
+    case "line":
+      seriesOption = lineBarSeriesOption(_chart, _series, serieType ?? "bar", chartOptions)
+      break
+    case "pie":
+      seriesOption = pieSeriesOption(_chart, _series, serieType ?? "pie", chartOptions)
+      break
+  }
+  const option = extendDeep({ title, grid }, seriesOption, chartOptions)
+  return option
+  //return Object.assign(true, {}, opt, options || {})
+}
+
+// const doubleTypes = (data: any, options: any, types = ["bar", "line"]) => {
+//   if (data == null) {
+//     throw new Error("no data")
+//   }
+//   const chartOptions = extendDeep({}, options)
+//   const _charts = compatibleMultChartData(data)
+
+//   const grid = Object.assign({}, defaultOption.SIZE.grid)
+//   _charts.forEach((_chart: any, i: number) => {
+//     const _series = compatibleSeries(_chart)
+//     //标题默认在图表下居中 ,使用远程数据 title , yzTitle 字段
+//     const title = {
+//       show: true,
+//       text: getOption(_chart, "title", "") || getOption(_chart, "yzTitle", ""),
+//     }
+//   })
+// }
+
+export default {
   bar(data: any, options: any) {
-    return this.chart(data, options, "bar")
+    return chart(data, options, "bar")
   },
   line(data: any, options: any) {
-    return this.chart(data, options, "line")
+    return chart(data, options, "line")
   },
   pie(data: any, options: any) {
-    return this.chart(data, options, "pie")
+    return chart(data, options, "pie")
   },
   ring(data: any, options: any) {
     options = Object.assign({ pieRadius: defaultOption.SIZE.ringRadius }, options)
-    return this.chart(data, options, "pie")
+    return chart(data, options, "pie")
   },
 }

+ 20 - 0
src/router/statictRouter.ts

@@ -7,6 +7,26 @@ export const staticRotuer: Array<RouteRecordRaw> = [
       {
         path: "/overAnalysis/overTime/detail",
         component: () => import("@/views/overAnalysis/overTimeDetail.vue"),
+        meta: {
+          pageTitle: "超标商户列表",
+          breadcrumbs: ["告警分析", "告警综合分析", "超标商户列表"],
+        },
+      },
+      {
+        path: "/overAnalysis/overTime/detail_company",
+        component: () => import("@/views/overAnalysis/overTimeDetail_Company.vue"),
+        meta: {
+          pageTitle: "超标商户列表",
+          breadcrumbs: ["告警分析", "告警综合分析", "超标商户列表", "商户超标分析"],
+        },
+      },
+      {
+        path: "/overAnalysis/overTime/concentrationWarn",
+        component: () => import("@/views/overAnalysis/concentrationWarn.vue"),
+        meta: {
+          pageTitle: "超标详情",
+          breadcrumbs: ["告警分析", "告警综合分析", "超标商户列表", "商户超标分析", "超标详情"],
+        },
       },
     ],
   },

+ 71 - 0
src/views/overAnalysis/concentrationWarn.vue

@@ -0,0 +1,71 @@
+<!-- 浓度警告 -->
+<script setup lang="ts">
+import { ref, onMounted } from "vue"
+import { useRoute } from "vue-router"
+import Rs from "@/core/services/RequestService"
+const route = useRoute()
+const cols = ref([
+  {
+    name: "浓度最大值(mg/m3)",
+    field: "warn_max_value",
+  },
+  {
+    name: "排放限值",
+    field: "warn_threshold",
+  },
+  {
+    name: "排放占比(%)",
+    field: "discharge_percent",
+  },
+  {
+    name: "发生时间",
+    field: "warn_max_time",
+  },
+])
+
+const title = ref("")
+const chartData = ref<any>([])
+const queryParams = ref({
+  warn_id: route.query.id,
+  warn_type: route.query.type,
+})
+const getTitle = () => {
+  Rs.post("sys/warn/getWarnTitle", { data: queryParams.value }).then((res) => {
+    title.value = res.data
+  })
+}
+
+const getChartData = () => {
+  Rs.post("sys/warn/getWarnChartDataById", { data: queryParams.value }).then((res) => {
+    chartData.value = res.data
+  })
+}
+
+onMounted(() => {
+  getTitle()
+  getChartData()
+})
+</script>
+
+<template>
+  <div class="card">
+    <div class="card-header">
+      <div class="card-title text-info fw-bolder">{{ title }}</div>
+    </div>
+    <div class="card-body p-5">
+      <div class="w-100">
+        <BaseChart :h="300" :data="chartData" type="line"></BaseChart>
+      </div>
+    </div>
+  </div>
+  <div class="w-100 my-5">
+    <VbDataTable
+      :header="cols"
+      url="sys/warn/getWarntableDataById"
+      method="post"
+      :query-params="queryParams"
+      :no-page="true"
+      :has-checkbox="false"
+    ></VbDataTable>
+  </div>
+</template>

+ 269 - 3
src/views/overAnalysis/overTimeDetail.vue

@@ -1,7 +1,273 @@
 <script setup lang="ts">
-import { ref, computed, onMounted } from "vue"
-</script>
+import { ref, onMounted } from "vue"
+import type { Header, Sort } from "@/components/Table/table-partials/models"
+import moment from "moment"
+import { useRouter, useRoute } from "vue-router"
+const route = useRoute()
+const router = useRouter()
+
+const table = ref()
+const columns = ref<Array<Header>>([
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "商户名称",
+    field: "company_name",
+  },
+  {
+    name: "菜系",
+    field: "catering_style_name",
+  },
+  {
+    name: "规模",
+    field: "catering_scale_name",
+  },
+  {
+    name: "类型",
+    field: "type_name",
+  },
+  {
+    name: "超标总次数",
+    field: "exceed_total",
+    isSort: true,
+  },
+  {
+    name: "油烟超标次数",
+    field: "exceed_smoke_num",
+    isSort: true,
+  },
+  {
+    name: "非甲烷超标次数",
+    field: "exceed_voc_num",
+    isSort: true,
+  },
+  {
+    name: "颗粒物超标次数",
+    field: "exceed_pm25_num",
+    isSort: true,
+  },
+  {
+    name: "操作",
+    width: 150,
+    align: "center",
+    field: "action",
+  },
+])
+const queryParams = ref<any>({
+  catering_scale: "",
+  catering_style: "",
+  name: "",
+  order_type: 0,
+  org_id: "",
+  time_type: "",
+  type: "",
+  query_end_time: moment().format("YYYYMMDD"),
+  query_start_time: moment().format("YYYYMMDD"),
+})
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "130px" }
+
+const companyName = ref("")
+const orgId = ref<string | null>()
+const type = ref("")
+const cateringScale = ref("")
+const cateringStyle = ref("")
+const timeType = ref(0)
+const orderType = ref(0)
 
+const startDate = ref(new Date())
+const endDate = ref(new Date())
+const dateValue = ref<[Date, Date]>([startDate.value, endDate.value])
+//const selectValue = ref(0)
+function changeDate(v: Date[]) {
+  startDate.value = v[0]
+  endDate.value = v[1]
+}
+
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      comName: v.company_name,
+      company_id: v.company_id,
+      backNeed: 1,
+    },
+  })
+}
+
+function init() {
+  const queryType = Number(route.query.query_type)
+  timeType.value = Number(route.query.time_type) ?? 0
+  const nameCode = route.query.name_code as string
+  if (queryType == 0) {
+    orgId.value = nameCode
+  } else if (queryType == 1) {
+    cateringStyle.value = nameCode
+  } else if (queryType == 2) {
+    cateringScale.value = nameCode
+  } else if (queryType == 3) {
+    type.value = nameCode
+  } else if (queryType == 4) {
+    companyName.value = nameCode
+  }
+  startDate.value = moment(route.query.query_start_time as string, "YYYYMMDD").toDate()
+  endDate.value = moment(route.query.query_end_time as string, "YYYYMMDD").toDate()
+  query()
+}
+function onSort(sort: Sort) {
+  orderType.value = 0
+  switch (sort.label) {
+    case "exceed_total":
+      orderType.value = sort.order == "desc" ? 0 : 1
+      break
+    case "exceed_smoke_num":
+      orderType.value = sort.order == "desc" ? 2 : 3
+      break
+    case "exceed_voc_num":
+      orderType.value = sort.order == "desc" ? 4 : 5
+      break
+    case "exceed_pm25_num":
+      orderType.value = sort.order == "desc" ? 6 : 7
+      break
+  }
+}
+function query() {
+  queryParams.value = {
+    catering_scale: cateringScale.value,
+    catering_style: cateringStyle.value,
+    name: companyName.value,
+    order_type: orderType.value,
+    org_id: orgId.value,
+    type: type.value,
+    time_type: timeType.value,
+    query_start_time: moment(startDate.value).format("YYYYMMDD"),
+    query_end_time: moment(endDate.value).format("YYYYMMDD"),
+  }
+}
+function reset() {
+  timeType.value = 0
+  cateringScale.value = ""
+  cateringStyle.value = ""
+  companyName.value = ""
+  orgId.value = ""
+  orderType.value = 0
+  startDate.value = new Date()
+  endDate.value = new Date()
+  dateValue.value = [startDate.value, endDate.value]
+  init()
+}
+function exported() {
+  const params = {
+    time_type: timeType.value,
+    order_type: orderType.value,
+    catering_style: cateringStyle.value,
+    catering_scale: cateringScale.value,
+    type: type.value,
+    name: companyName.value,
+    org_id: orgId.value,
+    query_start_time: moment(startDate.value).format("YYYYMMDD"),
+    query_end_time: moment(endDate.value).format("YYYYMMDD"),
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "" && params[key] !== 0) {
+      delete params[key]
+    }
+  })
+  let str = ""
+  for (const key in params) {
+    str += `${key}=${params[key]}&`
+  }
+  //console.log("/api/sys/overStandardAnalysis/exportAnalysis?" + str)
+  window.open("/api/sys/overStandardAnalysis/exportCompanyExceedTime?" + str)
+}
+function detail(row: any) {
+  router.push({
+    path: "/overAnalysis/overTime/detail_company",
+    query: {
+      company_id: row.company_id,
+      name: row.name,
+      type: "0",
+      time_type: timeType.value,
+      warn_type: "000100004",
+      query_start_time: moment(startDate.value).format("YYYYMMDD"),
+      query_end_time: moment(endDate.value).format("YYYYMMDD"),
+    },
+  })
+}
+onMounted(init)
+</script>
 <template>
-  <div>OT detail</div>
+  <VbDataTable
+    ref="table"
+    :header="columns"
+    url="sys/overStandardAnalysis/selectCompanyExceedTime"
+    method="post"
+    :query-params="queryParams"
+    :has-checkbox="false"
+    @on-sort="onSort"
+  >
+    <template v-slot:table-tool="">
+      <el-form class="align-items-center" :inline="true">
+        <el-form-item class="mb-0 me-5 align-items-center" label="商户名称">
+          <el-input class="" style="width: 180px" v-model="companyName" placeholder="请输入商户名称" :size="size" />
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="区域">
+          <OrgSelectTree v-model:value="orgId" style="width: 180px" :size="size"></OrgSelectTree>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="菜系">
+          <DySelect
+            v-model="cateringStyle"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getList?code=000010001&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择菜系"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="规模">
+          <DySelect
+            v-model="cateringScale"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getList?code=000090001&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择规模"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="类型">
+          <DySelect
+            v-model="type"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getList?code=000200000&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择类型"
+          ></DySelect>
+        </el-form-item>
+        <DateRangeSelect
+          v-model:date-value="dateValue"
+          v-model:select-value="timeType"
+          :select-class="'mb-0 align-items-center'"
+          :date-class="'mb-0 mt-3 align-items-center'"
+          :style="dySearchSelectStyle"
+          :size="size"
+          @change="changeDate"
+        />
+        <el-form-item class="mb-0 me-0 align-items-center" :class="timeType == 4 ? `mt-3` : ''">
+          <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="query">查询</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-primary btn-outline" @click="reset">重置</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-info btn-outline" @click="exported">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </template>
+    <template #company_name="{ row }">
+      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["company_name"] }}</span>
+    </template>
+    <template #action="{ row }">
+      <div class="text-danger text-center">
+        <span class="table-action" @click="detail(row)">查看详情</span>
+      </div>
+    </template>
+  </VbDataTable>
 </template>

+ 217 - 0
src/views/overAnalysis/overTimeDetail_Company.vue

@@ -0,0 +1,217 @@
+<script setup lang="ts">
+import { ref, onMounted } from "vue"
+import type { Header } from "@/components/Table/table-partials/models"
+import moment from "moment"
+import { useRouter, useRoute } from "vue-router"
+const route = useRoute()
+const router = useRouter()
+
+const table = ref()
+const columns = ref<Array<Header>>([
+  {
+    name: "商户名称",
+    field: "company_name",
+  },
+  {
+    name: "监控仪",
+    field: "device_name",
+  },
+  {
+    name: "报警类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "发生时间",
+    field: "warn_starttime",
+  },
+  {
+    name: "恢复时间",
+    field: "warn_endtime",
+  },
+  {
+    name: "持续时间",
+    field: "stay_time",
+  },
+  {
+    name: "操作",
+    width: 150,
+    align: "center",
+    field: "action",
+  },
+])
+
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "150px" }
+
+const companyId = ref(route.query.company_id as string)
+const warnType = ref("")
+const timeType = ref(0)
+
+const queryParams = ref<any>({
+  company_id: companyId.value,
+  warn_type: warnType.value,
+  time_type: timeType.value,
+  query_end_time: moment().format("YYYYMMDD"),
+  query_start_time: moment().format("YYYYMMDD"),
+})
+const startDate = ref(new Date())
+const endDate = ref(new Date())
+const dateValue = ref<[Date, Date]>([startDate.value, endDate.value])
+//const selectValue = ref(0)
+function changeDate(v: Date[]) {
+  startDate.value = v[0]
+  endDate.value = v[1]
+}
+
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      comName: v.company_name,
+      company_id: v.company_id,
+      backNeed: 1,
+    },
+  })
+}
+const getStayTimes = (item: any) => {
+  if (!!item.warn_endtime && !!item.warn_starttime) {
+    const stayTime = moment(item.warn_endtime, "YYYYMMDDHHmmss").diff(
+      moment(item.warn_starttime, "YYYYMMDDHHmmss"),
+      "milliseconds"
+    )
+    if (stayTime !== 0) {
+      const hours = Math.floor((stayTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
+      const minutes = Math.floor((stayTime % (1000 * 60 * 60)) / (1000 * 60))
+      const seconds = Math.floor((stayTime % (1000 * 60)) / 1000)
+      return `${hours < 10 ? "0" + hours : hours}:${minutes < 10 ? "0" + minutes : minutes}:${
+        seconds < 10 ? "0" + seconds : seconds
+      }`
+    }
+  }
+  return ""
+}
+function init() {
+  timeType.value = Number(route.query.time_type) ?? 0
+  startDate.value = moment(route.query.query_start_time as string, "YYYYMMDD").toDate()
+  endDate.value = moment(route.query.query_end_time as string, "YYYYMMDD").toDate()
+  query()
+}
+
+function query() {
+  const params = {
+    company_id: companyId.value,
+    warn_type: warnType.value,
+    time_type: timeType.value,
+    query_start_time: moment(startDate.value).format("YYYYMMDD"),
+    query_end_time: moment(endDate.value).format("YYYYMMDD"),
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "" && params[key] !== 0) {
+      delete params[key]
+    }
+  })
+
+  queryParams.value = params
+}
+function reset() {
+  timeType.value = 0
+  warnType.value = ""
+  startDate.value = new Date()
+  endDate.value = new Date()
+  dateValue.value = [startDate.value, endDate.value]
+  init()
+}
+function exported() {
+  const params = {
+    company_id: companyId.value,
+    warn_type: warnType.value,
+    time_type: timeType.value,
+    query_start_time: moment(startDate.value).format("YYYYMMDD"),
+    query_end_time: moment(endDate.value).format("YYYYMMDD"),
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "" && params[key] !== 0) {
+      delete params[key]
+    }
+  })
+  let str = ""
+  for (const key in params) {
+    str += `${key}=${params[key]}&`
+  }
+  window.open("/api/sys/overStandardAnalysis/exportWarnTable?" + str)
+}
+function detail(row: any) {
+  const path =
+    row.warn_type == "000100003" || row.warn_type == "000100005" || row.warn_type == "000100007"
+      ? "/overAnalysis/overTime/concentrationWarn"
+      : row.warn_type == "000100006"
+      ? "/overAnalysis/overTime/offlineWarn"
+      : "/overAnalysis/overTime/overdueWarn"
+
+  router.push({
+    path: path,
+    query: {
+      id: row.id,
+      type: row.warn_type,
+      needback: 1,
+    },
+  })
+}
+onMounted(init)
+</script>
+<template>
+  <VbDataTable
+    ref="table"
+    :header="columns"
+    url="sys/overStandardAnalysis/warnTable"
+    method="post"
+    :query-params="queryParams"
+    :has-checkbox="false"
+  >
+    <template v-slot:table-tool="">
+      <el-form class="align-items-center" :inline="true">
+        <el-form-item class="mb-0 me-5 align-items-center" label="类型">
+          <DySelect
+            v-model="warnType"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getExceedWarnType'"
+            :style="`width:180px`"
+            placeholder="请选择报警类型"
+          ></DySelect>
+        </el-form-item>
+        <DateRangeSelect
+          v-model:date-value="dateValue"
+          v-model:select-value="timeType"
+          :select-class="'mb-0 align-items-center'"
+          :date-class="'mb-0 mt-3 align-items-center'"
+          :style="dySearchSelectStyle"
+          :size="size"
+          @change="changeDate"
+        />
+        <el-form-item class="mb-0 me-0 align-items-center" :class="timeType == 4 ? `mt-3` : ''">
+          <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="query">查询</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-primary btn-outline" @click="reset">重置</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-info btn-outline" @click="exported">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </template>
+    <template #company_name="{ row }">
+      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["company_name"] }}</span>
+    </template>
+    <template #warn_starttime="{ row }">
+      {{ moment(row.warn_starttime, "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") }}
+    </template>
+    <template #warn_endtime="{ row }">
+      {{ moment(row.warn_endtime, "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") }}
+    </template>
+    <template #stay_time="{ row }">{{ getStayTimes(row) }}</template>
+    <template #action="{ row }">
+      <div class="text-danger text-center">
+        <span class="table-action" @click="detail(row)">查看详情</span>
+      </div>
+    </template>
+  </VbDataTable>
+</template>