zhangwy 2 年之前
父節點
當前提交
1aca32779f
共有 69 個文件被更改,包括 5515 次插入308 次删除
  1. 2 0
      .gitignore
  2. 0 63
      auto-imports.d.ts
  3. 0 72
      components.d.ts
  4. 1 0
      src/api/_auth.ts
  5. 1 0
      src/assets/sass/_common.scss
  6. 3 3
      src/components/Modals/VbModal.vue
  7. 5 1
      src/components/Table/VbDataTable.vue
  8. 7 0
      src/components/Table/table-partials/table-content/table-body/TableBodyRow.vue
  9. 8 0
      src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue
  10. 7 0
      src/components/Table/table-partials/table-content/table-body/TableTreeRowChild.vue
  11. 13 2
      src/components/Tree/OrgCompany.vue
  12. 84 0
      src/components/Upload/VbUpload.vue
  13. 12 5
      src/core/charts/chartHelper.ts
  14. 1 0
      src/core/config/Index.ts
  15. 16 16
      src/core/config/rules.ts
  16. 4 4
      src/layouts/main-layout/footer/Footer.vue
  17. 62 59
      src/layouts/main-layout/header/PageTitle.vue
  18. 12 5
      src/layouts/main-layout/menus/NotificationsMenu.vue
  19. 0 3
      src/layouts/main-layout/sidebar/Sidebar.vue
  20. 7 24
      src/layouts/main-layout/sidebar/SidebarMenu.vue
  21. 4 1
      src/router/Index.ts
  22. 8 0
      src/router/menuMap/_goLineData.ts
  23. 4 0
      src/router/menuMap/_mobile.ts
  24. 18 0
      src/router/menuMap/_purify.ts
  25. 16 0
      src/router/menuMap/_warn.ts
  26. 18 0
      src/router/menuMap/_workNotice.ts
  27. 3 0
      src/router/menuMap/index.ts
  28. 106 0
      src/router/statictRouter.ts
  29. 3 0
      src/stores/_auth.ts
  30. 23 8
      src/stores/_menu.ts
  31. 226 0
      src/views/_component/_WarnNoProcess.vue
  32. 247 0
      src/views/_component/_WarnProcessed.vue
  33. 435 0
      src/views/_component/_WarnProcessing.vue
  34. 130 0
      src/views/_component/__WarnProcessDetail.vue
  35. 1 1
      src/views/account/SignIn.vue
  36. 16 5
      src/views/analysisInfo/_Overdue.vue
  37. 7 4
      src/views/analysisInfo/_WeekClean.vue
  38. 2 2
      src/views/analysisInfo/clean.vue
  39. 70 0
      src/views/analysisInfo/overdueCompany.vue
  40. 190 0
      src/views/analysisInfo/overdueWarn.vue
  41. 90 0
      src/views/analysisInfo/weekCleanList.vue
  42. 1 1
      src/views/dashboard/Screen/ChartOver.vue
  43. 1 1
      src/views/dashboard/Screen/Map.vue
  44. 328 2
      src/views/examApproval/appReview.vue
  45. 320 2
      src/views/examApproval/reptNotice.vue
  46. 10 8
      src/views/goLineData/_ViewDevice_General.vue
  47. 1 1
      src/views/goLineData/timeData.vue
  48. 18 0
      src/views/mobile/inspector.vue
  49. 17 2
      src/views/mobile/supervision.vue
  50. 1 1
      src/views/overAnalysis/oveParamDetail.vue
  51. 2 1
      src/views/overAnalysis/overMerchants.vue
  52. 1 1
      src/views/overAnalysis/overTime.vue
  53. 2 1
      src/views/overAnalysis/overTimeDetail.vue
  54. 2 1
      src/views/overAnalysis/overTimeDetail_Company.vue
  55. 107 0
      src/views/purify/qxyw.vue
  56. 261 0
      src/views/purify/qxyw_detail.vue
  57. 220 2
      src/views/purify/washInfo.vue
  58. 183 0
      src/views/purify/washInfo_company.vue
  59. 265 0
      src/views/purify/washInfo_details.vue
  60. 115 0
      src/views/warn/abnormal.vue
  61. 72 0
      src/views/warn/abnormalList/disConnect.vue
  62. 191 0
      src/views/warn/abnormalList/overdueWarn.vue
  63. 72 0
      src/views/warn/abnormalList/strongWarn.vue
  64. 296 0
      src/views/warn/abnormal_declare.vue
  65. 124 0
      src/views/warn/companyWarnList.vue
  66. 266 2
      src/views/warn/ouput.vue
  67. 323 4
      src/views/warn/warnList.vue
  68. 232 0
      src/views/workNotice/washNotice.vue
  69. 222 0
      src/views/workNotice/worryNotice.vue

+ 2 - 0
.gitignore

@@ -16,3 +16,5 @@ selenium-debug.log
 *.ntvs*
 *.njsproj
 *.sln
+/components.d.ts
+/auto-imports.d.ts

+ 0 - 63
auto-imports.d.ts

@@ -1,63 +0,0 @@
-/* eslint-disable */
-/* prettier-ignore */
-// @ts-nocheck
-// Generated by unplugin-auto-import
-export {}
-declare global {
-  const 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'
-}

+ 0 - 72
components.d.ts

@@ -1,72 +0,0 @@
-/* eslint-disable */
-/* prettier-ignore */
-// @ts-nocheck
-// Generated by unplugin-vue-components
-// Read more: https://github.com/vuejs/core/pull/3399
-import '@vue/runtime-core'
-
-export {}
-
-declare module '@vue/runtime-core' {
-  export interface GlobalComponents {
-    AreaCascade: typeof import('./src/components/select/AreaCascade.vue')['default']
-    AreaCascadeSelect: typeof import('./src/components/select/AreaCascadeSelect.vue')['default']
-    AreaSelectCascade: typeof import('./src/components/select/AreaSelectCascade.vue')['default']
-    BaseChart: typeof import('./src/components/charts/BaseChart.vue')['default']
-    CodeHighlighter: typeof import('./src/components/highlighters/CodeHighlighter.vue')['default']
-    CodeHighlighter2: typeof import('./src/components/highlighters/CodeHighlighter2.vue')['default']
-    copy: typeof import('./src/components/Table/table-partials/table-content/table-body/TableTreeRow copy.vue')['default']
-    DateRangeSelect: typeof import('./src/components/select/DateRangeSelect.vue')['default']
-    DynamicTreeSelect: typeof import('./src/components/select/DynamicTreeSelect.vue')['default']
-    DySearchSelect: typeof import('./src/components/select/DySearchSelect.vue')['default']
-    DySelect: typeof import('./src/components/select/DySelect.vue')['default']
-    DySelectCascade: typeof import('./src/components/select/DySelectCascade.vue')['default']
-    DySelectTree: typeof import('./src/components/select/DySelectTree.vue')['default']
-    ElButton: typeof import('element-plus/es')['ElButton']
-    ElCascader: typeof import('element-plus/es')['ElCascader']
-    ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
-    ElCol: typeof import('element-plus/es')['ElCol']
-    ElCols: typeof import('element-plus/es')['ElCols']
-    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
-    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
-    ElElFormItem: typeof import('element-plus/es')['ElElFormItem']
-    ElForm: typeof import('element-plus/es')['ElForm']
-    ElFormItem: typeof import('element-plus/es')['ElFormItem']
-    ElInput: typeof import('element-plus/es')['ElInput']
-    ElOption: typeof import('element-plus/es')['ElOption']
-    ElPagination: typeof import('element-plus/es')['ElPagination']
-    ElRadio: typeof import('element-plus/es')['ElRadio']
-    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
-    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
-    ElRow: typeof import('element-plus/es')['ElRow']
-    ElRows: typeof import('element-plus/es')['ElRows']
-    ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElTabPane: typeof import('element-plus/es')['ElTabPane']
-    ElTabs: typeof import('element-plus/es')['ElTabs']
-    ElTree: typeof import('element-plus/es')['ElTree']
-    Form: typeof import('./src/components/Forms/Form.vue')['default']
-    FormItem: typeof import('./src/components/Forms/FormItem.vue')['default']
-    Loading: typeof import('./src/components/Table/table-partials/Loading.vue')['default']
-    MenuComponent: typeof import('./src/components/menu/MenuComponent.vue')['default']
-    OrgCompany: typeof import('./src/components/Tree/OrgCompany.vue')['default']
-    OrgSelectTree: typeof import('./src/components/Tree/OrgSelectTree.vue')['default']
-    'PreviewCode ': typeof import('./src/components/prismjs/PreviewCode .vue')['default']
-    RouterLink: typeof import('vue-router')['RouterLink']
-    RouterView: typeof import('vue-router')['RouterView']
-    TableBodyRow: typeof import('./src/components/Table/table-partials/table-content/table-body/TableBodyRow.vue')['default']
-    TableContent: typeof import('./src/components/Table/table-partials/table-content/TableContent.vue')['default']
-    TableFixed: typeof import('./src/components/Table/table-partials/table-content/table-fixed/TableFixed.vue')['default']
-    TableFooter: typeof import('./src/components/Table/table-partials/TableFooter.vue')['default']
-    TableHeadRow: typeof import('./src/components/Table/table-partials/table-content/table-head/TableHeadRow.vue')['default']
-    TableItemsPerPageSelect: typeof import('./src/components/Table/table-partials/table-content/table-footer/TableItemsPerPageSelect.vue')['default']
-    TablePagination: typeof import('./src/components/Table/table-partials/table-content/table-footer/TablePagination.vue')['default']
-    TableRightFixed: typeof import('./src/components/Table/table-partials/table-content/table-fixed/TableRightFixed.vue')['default']
-    TableTreeRow: typeof import('./src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue')['default']
-    TableTreeRowChild: typeof import('./src/components/Table/table-partials/table-content/table-body/TableTreeRowChild.vue')['default']
-    VbDataTable: typeof import('./src/components/Table/VbDataTable.vue')['default']
-    VbForm: typeof import('./src/components/Forms/VbForm.vue')['default']
-    VbFormItem: typeof import('./src/components/Forms/VbFormItem.vue')['default']
-    VbModal: typeof import('./src/components/Modals/VbModal.vue')['default']
-    VbTreeTable: typeof import('./src/components/Table/VbTreeTable.vue')['default']
-  }
-}

+ 1 - 0
src/api/_auth.ts

@@ -23,6 +23,7 @@ class AuthApi {
     return Rs.post({
       loading: false,
       url: "auth/menus/findByParentID",
+      successAlert: false,
     })
   }
 }

+ 1 - 0
src/assets/sass/_common.scss

@@ -1,4 +1,5 @@
 dl{
+  display: flex;
   margin-bottom: 8px;
   dt{
     white-space: nowrap;

+ 3 - 3
src/components/Modals/VbModal.vue

@@ -82,7 +82,7 @@ const props = withDefaults(
     confirmBtnText: "确认",
     confirmBtnClass: "btn btn-primary",
     confirmBtnStyle: "",
-    modalClass: "",
+    modalClass: "modal-lg ",
     modalDialogClass: "modal-dialog-centered",
     modalContentClass: "",
     modalHeaderClass: "",
@@ -210,7 +210,7 @@ onMounted(() => {
 defineExpose({ show })
 </script>
 <template>
-  <div ref="modalEl" class="modal modal-lg fade" :class="modalClass" :style="modalStyle" tabindex="-1" :id="id">
+  <div ref="modalEl" class="modal fade" :class="modalClass" :style="modalStyle" tabindex="-1" :id="id">
     <div class="modal-dialog" :class="modalDialogClass" :style="modalDialogStyle">
       <div class="modal-content" :class="modalContentClass" :style="modalContentStyle">
         <div class="modal-header py-3" :class="modalHeaderClass" :style="modalHeaderStyle">
@@ -233,7 +233,7 @@ defineExpose({ show })
           <template v-if="$slots.body">
             <slot name="body" />
           </template>
-          <template v-else-if="$slots.form">
+          <template v-if="$slots.form">
             <el-form
               ref="modalFormEl"
               :model="formData"

+ 5 - 1
src/components/Table/VbDataTable.vue

@@ -13,6 +13,7 @@ const props = withDefaults(
     method?: string
     configs?: AxiosRequestConfig
     queryParams?: any
+    autoSearch?: boolean //页面加载时自动查询
     data?: Array<any>
     total?: number //总数
     selectedItems?: Array<any>
@@ -61,6 +62,7 @@ const props = withDefaults(
     pageArray: () => {
       return [10, 25, 50]
     },
+    autoSearch: true,
     hasCheckbox: true,
     checkMultiple: false,
     sortOrder: "asc",
@@ -237,7 +239,9 @@ watch(
 //   }
 // })
 onMounted(() => {
-  search()
+  if (props.autoSearch) {
+    search()
+  }
 })
 defineExpose({ search })
 </script>

+ 7 - 0
src/components/Table/table-partials/table-content/table-body/TableBodyRow.vue

@@ -2,6 +2,7 @@
 import { ref, withDefaults, watch, onMounted } from "vue"
 import type { Header } from "@/components/Table/table-partials/models"
 import TableTreeRow from "./TableTreeRow.vue"
+import configs from "@/core/config/Index"
 
 const props = withDefaults(
   defineProps<{
@@ -171,6 +172,9 @@ onMounted(init)
                 {{ row }}
               </slot>
             </template>
+            <template v-else-if="column.field == configs.TABLE_INDEX_FIELD">
+              {{ i + 1 }}
+            </template>
             <template v-else>
               {{ row[column.field] }}
             </template>
@@ -185,6 +189,9 @@ onMounted(init)
                 {{ row }}
               </slot>
             </template>
+            <template v-else-if="column.field == configs.TABLE_INDEX_FIELD">
+              {{ i + 1 }}
+            </template>
             <template v-else>
               {{ row[column.field] }}
             </template>

+ 8 - 0
src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue

@@ -2,6 +2,8 @@
 import { ref, withDefaults, onMounted, computed } from "vue"
 import type { Header } from "@/components/Table/table-partials/models"
 import TableTreeRowChild from "./TableTreeRowChild.vue"
+import configs from "@/core/config/Index"
+
 const props = withDefaults(
   defineProps<{
     header: Array<Header>
@@ -118,6 +120,9 @@ onMounted(() => {
             {{ row }}
           </slot>
         </template>
+        <template v-else-if="column.field == configs.TABLE_INDEX_FIELD">
+          {{ i + 1 }}
+        </template>
         <template v-else>
           {{ row[column.field] }}
         </template>
@@ -133,6 +138,9 @@ onMounted(() => {
             {{ row }}
           </slot>
         </template>
+        <template v-else-if="column.field == configs.TABLE_INDEX_FIELD">
+          {{ i + 1 }}
+        </template>
         <template v-else>
           {{ row[column.field] }}
         </template>

+ 7 - 0
src/components/Table/table-partials/table-content/table-body/TableTreeRowChild.vue

@@ -2,6 +2,7 @@
 import { ref, withDefaults, onMounted, computed } from "vue"
 import type { Header } from "@/components/Table/table-partials/models"
 import TableTreeRow from "./TableTreeRow.vue"
+import configs from "@/core/config/Index"
 
 const props = withDefaults(
   defineProps<{
@@ -122,6 +123,9 @@ onMounted(() => {
             {{ row }}
           </slot>
         </template>
+        <template v-else-if="column.field == configs.TABLE_INDEX_FIELD">
+          {{ i + 1 }}
+        </template>
         <template v-else>
           {{ row[column.field] }}
         </template>
@@ -137,6 +141,9 @@ onMounted(() => {
             {{ row }}
           </slot>
         </template>
+        <template v-else-if="column.field == configs.TABLE_INDEX_FIELD">
+          {{ i + 1 }}
+        </template>
         <template v-else>
           {{ row[column.field] }}
         </template>

+ 13 - 2
src/components/Tree/OrgCompany.vue

@@ -12,6 +12,7 @@ const emits = defineEmits<{
   (e: "clear"): void
   (e: "selected", type: string, key: string): void
 }>()
+const companyId = ref("")
 const expandedKeys = ref<Array<string>>([])
 const selectedKeys = ref<Array<string>>([])
 const searchWord = ref("")
@@ -27,6 +28,15 @@ function loadData(fun: () => void) {
     (res) => {
       data.value = res.data
       fun && fun()
+      if (!companyId.value && data.value[0]) {
+        const key = data.value[0].key
+        const arr = key.split("_")
+        emits("selected", arr[0], key)
+        setTimeout(() => {
+          expandedKeys.value = [key]
+          selectedKeys.value = [key]
+        }, 100)
+      }
     }
   )
 }
@@ -48,7 +58,7 @@ function findCompany(hasKey = true) {
   let key = ""
   //console.log(searchWord)
   data.value.forEach((v: any) => {
-    if ((hasKey && v.title == searchWord.value) || v.title.search(searchWord.value) >= 0) {
+    if ((hasKey && v.title == searchWord.value) || (searchWord.value && v.title.search(searchWord.value) >= 0)) {
       key = v.key
     } else {
       if (v.children) {
@@ -59,7 +69,7 @@ function findCompany(hasKey = true) {
   function _findCompany(list: Array<any>, key: string, fatherKey: string) {
     list.forEach((v) => {
       if (
-        (hasKey && v.key == "10_" + route.query.company_id) ||
+        (hasKey && v.key == "10_" + companyId.value) ||
         (v.key.search("10_") >= 0 && v.title.search(searchWord.value) >= 0)
       ) {
         key = v.key
@@ -118,6 +128,7 @@ function onNodeClick(nodeData: any) {
   emits("node-click", nodeData)
 }
 function init() {
+  companyId.value = route.query.company_id as string
   const name = route.query.comName as string
   if (name) {
     searchWord.value = name

+ 84 - 0
src/components/Upload/VbUpload.vue

@@ -0,0 +1,84 @@
+<script setup lang="ts">
+import type { UploadFile, UploadProps } from "element-plus"
+import { ref, withDefaults, onMounted, computed } from "vue"
+import { Plus } from "@element-plus/icons-vue"
+
+const props = withDefaults(
+  defineProps<{
+    modelValue: string
+    uploadUrl?: string
+    fileList?: Array<UploadFile>
+    listType?: "picture" | "picture-card" | "text"
+  }>(),
+  {
+    uploadUrl: "/api/file/upload/putObject",
+    listType: "picture-card",
+    fileList: () => {
+      return []
+    },
+  }
+)
+const emits = defineEmits<{
+  (e: "update:modelValue", v: string): void
+  (e: "update:fileList", v: Array<UploadFile>): void
+  (e: "remove", uploadFile: UploadFile, uploadFiles: Array<UploadFile>): void
+}>()
+const fileList = ref(props.fileList)
+const dialogImageUrl = ref("")
+const dialogVisible = ref(false)
+const fileUrls = computed(() => {
+  const result: string[] = []
+  fileList.value.forEach(function (item: any) {
+    result.push(item.response.data.filePath)
+  })
+  return result.join(",")
+})
+
+const onRemove: UploadProps["onRemove"] = (uploadFile: UploadFile, uploadFiles: Array<UploadFile>) => {
+  emits("remove", uploadFile, uploadFiles)
+  emits("update:modelValue", fileUrls.value)
+  emits("update:fileList", fileList.value)
+}
+
+const onPreview: UploadProps["onPreview"] = (uploadFile: UploadFile) => {
+  if (uploadFile.url) {
+    dialogImageUrl.value = uploadFile.url
+    dialogVisible.value = true
+  }
+}
+
+const onChange: UploadProps["onChange"] = () => {
+  //console.log("CHANGE____", fileList.value, fileUrls.value)
+  emits("update:modelValue", fileUrls.value)
+  emits("update:fileList", fileList.value)
+}
+const uploadEl = ref()
+function clearFiles() {
+  uploadEl.value.clearFiles()
+}
+function init() {
+  //
+}
+
+onMounted(() => {
+  init()
+})
+defineExpose({ clearFiles })
+</script>
+<template>
+  <el-upload
+    ref="uploadEl"
+    v-model:file-list="fileList"
+    :action="uploadUrl"
+    :list-type="listType"
+    v-bind="$attrs"
+    :on-preview="onPreview"
+    :on-remove="onRemove"
+    :on-change="onChange"
+  >
+    <el-icon><Plus /></el-icon>
+  </el-upload>
+  <el-dialog v-model="dialogVisible">
+    <img w-full :src="dialogImageUrl" alt="Preview Image" />
+  </el-dialog>
+</template>

+ 12 - 5
src/core/charts/chartHelper.ts

@@ -137,8 +137,8 @@ const lineBarSeriesOption = (chart: any, serieData: Array<any>, serieType: strin
     const type = has(v, "type") ? v.type : serieType
     let seriesOption: any = {
       type: type,
-      name: v.name,
-      data: v.data,
+      //name: v.name,
+      //data: v.data,
       stack: has(v, "stack") ? v.stack : null,
       itemStyle: getOption(v, "itemStyle", {}),
       markPoint,
@@ -169,12 +169,19 @@ const lineBarSeriesOption = (chart: any, serieData: Array<any>, serieType: strin
             areaStyle: {
               color: areaStyleColors[i],
             },
-          }
+          },
+      v
     )
     if (v.data && v.data.length) {
       v.data.forEach((vv: any) => {
-        if (vv > maxData) {
-          maxData = vv
+        if (typeof vv == "object") {
+          if (vv.value > maxData) {
+            maxData = vv.value
+          }
+        } else {
+          if (vv > maxData) {
+            maxData = vv
+          }
         }
       })
       yAxisMax = Math.floor(maxData * 1.2)

+ 1 - 0
src/core/config/Index.ts

@@ -4,6 +4,7 @@ const configs = {
   AUTH_EXPIRE_MINUTES: 60, //登录时效
   MAIN_ROUTER_NAME: "dashboard",
   SIGN_ROUTE_NAME: "sign-in",
+  TABLE_INDEX_FIELD: "$index$",
 }
 
 export default configs

+ 16 - 16
src/core/config/rules.ts

@@ -44,36 +44,36 @@ export const RULE_KEYS = {
 }
 export const rules = {
   VB_REQUIRED: { required: true, message: "必填", trigger: "blur" },
-  VB_NUMBER: { type: "number", message: "请输入数字", trigger: ["blur"] },
-  VB_INT: { type: "integer", message: "请输入整数", trigger: ["blur"] },
-  VB_FLOAT: { type: "float", message: "请输入浮点数", trigger: ["blur"] },
-  VB_HEX: { type: "hex", message: "请输入16进制数字", trigger: ["blur"] },
-  VB_URL: { type: "url", message: "请输入正确的URL地址", trigger: ["blur"] },
-  VB_EMAIL: { type: "email", message: "请输入正确的电子邮箱", trigger: ["blur"] },
+  VB_NUMBER: { type: "number", message: "请填写数字", trigger: ["blur"] },
+  VB_INT: { type: "integer", message: "请填写整数", trigger: ["blur"] },
+  VB_FLOAT: { type: "float", message: "请填写浮点数", trigger: ["blur"] },
+  VB_HEX: { type: "hex", message: "请填写16进制数字", trigger: ["blur"] },
+  VB_URL: { type: "url", message: "请填写正确的URL地址", trigger: ["blur"] },
+  VB_EMAIL: { type: "email", message: "请填写正确的电子邮箱", trigger: ["blur"] },
   VB_DATE: { type: "date", message: "请选择日期", trigger: ["blur"] },
   VB_DATETIME: { type: "date", message: "请选择时间", trigger: ["blur"] },
-  VB_REQUIRED_NUMBER: { type: "number", required: true, message: "请输入数字", trigger: ["blur"] },
-  VB_REQUIRED_INT: { type: "integer", required: true, message: "请输入整数", trigger: ["blur"] },
-  VB_REQUIRED_FLOAT: { type: "float", required: true, message: "请输入浮点数", trigger: ["blur"] },
-  VB_REQUIRED_HEX: { type: "hex", required: true, message: "请输入16进制数字", trigger: ["blur"] },
+  VB_REQUIRED_NUMBER: { type: "number", required: true, message: "请填写数字", trigger: ["blur"] },
+  VB_REQUIRED_INT: { type: "integer", required: true, message: "请填写整数", trigger: ["blur"] },
+  VB_REQUIRED_FLOAT: { type: "float", required: true, message: "请填写浮点数", trigger: ["blur"] },
+  VB_REQUIRED_HEX: { type: "hex", required: true, message: "请填写16进制数字", trigger: ["blur"] },
   VB_REQUIRED_URL: [
-    { type: "string", required: true, message: "请输入URL地址", trigger: "blur" },
-    { type: "url", message: "请输入正确的URL地址", trigger: ["blur"] },
+    { type: "string", required: true, message: "请填写URL地址", trigger: "blur" },
+    { type: "url", message: "请填写正确的URL地址", trigger: ["blur"] },
   ],
   VB_REQUIRED_EMAIL: [
-    { type: "string", required: true, message: "请输入电子邮箱", trigger: "blur" },
-    { type: "email", message: "请输入正确的电子邮箱", trigger: ["blur"] },
+    { type: "string", required: true, message: "请填写电子邮箱", trigger: "blur" },
+    { type: "email", message: "请填写正确的电子邮箱", trigger: ["blur"] },
   ],
   VB_REQUIRED_DATE: { type: "date", required: true, message: "请选择日期", trigger: ["blur"] },
   VB_REQUIRED_DATETIME: { type: "date", required: true, message: "请选择时间", trigger: ["blur"] },
 }
 export const rulesFormat = {
-  VB_REQUIRED: '{ "required": true, "message": "请输入{0}", "trigger": ["blur"] }',
+  VB_REQUIRED: '{ "required": true, "message": "请填写{0}", "trigger": ["blur"] }',
   VB_MIN: '{"type": "string","min": {0}, "message": "至少{0}个字符", "trigger": ["blur"] }',
   VB_MAX: '{"type": "string","max": {0}, "message": "最多{0}个字符", "trigger": ["blur" ]}',
   VB_MIN_MAX: '{"type": "string","min": {0}, "max": {1}, "message": "长度在 {0} 到 {1} 个字符", "trigger": ["blur"]}',
   VB_N_MIN: '{"type": "number","min": {0}, "message": "最小{0}", "trigger": ["blur"] }',
   VB_N_MAX: '{"type": "number","max": {0}, "message": "最大{0}", "trigger": ["blur" ]}',
-  VB_N_MIN_MAX: '{"type": "number","min": {0}, "max": {1}, "message": "请输入 {0} 到 {1} 的数字", "trigger": ["blur"]}',
+  VB_N_MIN_MAX: '{"type": "number","min": {0}, "max": {1}, "message": "请填写 {0} 到 {1} 的数字", "trigger": ["blur"]}',
   VB_PATTERN: '{"pattern": {0}, "message": "{1}", "trigger": ["blur"]}',
 }

+ 4 - 4
src/layouts/main-layout/footer/Footer.vue

@@ -11,12 +11,12 @@
     >
       <!--begin::Copyright-->
       <div class="text-dark order-2 order-md-1">
-        <span class="text-muted fw-semibold me-1">2022©</span>
-        <a href="https://keenthemes.com" target="_blank" class="text-gray-800 text-hover-primary">Keenthemes</a>
+        <span class="text-muted fw-semibold me-1">{{ new Date().getFullYear() }}©</span>
+        <a href="https://keenthemes.com" target="_blank" class="text-gray-800 text-hover-primary">VBER</a>
       </div>
       <!--end::Copyright-->
       <!--begin::Menu-->
-      <ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1">
+      <!-- <ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1">
         <li class="menu-item">
           <a href="https://keenthemes.com" target="_blank" class="menu-link px-2">About</a>
         </li>
@@ -26,7 +26,7 @@
         <li class="menu-item">
           <a href="https://1.envato.market/EA4JP" target="_blank" class="menu-link px-2">Purchase</a>
         </li>
-      </ul>
+      </ul> -->
       <!--end::Menu-->
     </div>
     <!--end::Footer container-->

+ 62 - 59
src/layouts/main-layout/header/PageTitle.vue

@@ -1,76 +1,79 @@
 <template>
   <!--begin::Page title-->
-  <div
-    v-if="pageTitleDisplay"
-    :class="`page-title d-flex flex-${pageTitleDirection} justify-content-center flex-wrap me-3`"
-  >
-    <template v-if="pageTitle">
-      <!--begin::Title-->
-      <h1 class="page-heading d-flex text-dark fw-bold fs-3 flex-column justify-content-center my-0">
-        {{ pageTitle }}
-      </h1>
-      <!--end::Title-->
+  <div class="d-flex">
+    <div
+      v-if="showBackBtn"
+      class="d-flex h-100 justify-content-center align-items-center pe-4"
+      style="cursor: pointer"
+      @click="router.go(-1)"
+    >
+      <span class="svg-icon svg-icon-2tx" v-tooltip title="返回" data-bs-trigger="hover" data-bs-placement="bottom">
+        <inline-svg :src="getAssetPath('media/icons/duotune/general/gen058.svg')" />
+      </span>
+    </div>
+    <div
+      v-if="pageTitleDisplay"
+      :class="`page-title d-flex flex-${pageTitleDirection} justify-content-center flex-wrap me-3`"
+    >
+      <template v-if="pageTitle">
+        <!--begin::Title-->
+        <h1 class="page-heading d-flex text-dark fw-bold fs-3 flex-column justify-content-center my-0">
+          {{ pageTitle }}
+        </h1>
+        <!--end::Title-->
 
-      <span
-        v-if="pageTitleDirection === 'row' && pageTitleBreadcrumbDisplay"
-        class="h-20px border-gray-200 border-start mx-3"
-      ></span>
+        <span
+          v-if="pageTitleDirection === 'row' && pageTitleBreadcrumbDisplay"
+          class="h-20px border-gray-200 border-start mx-3"
+        ></span>
 
-      <!--begin::Breadcrumb-->
-      <ul
-        v-if="breadcrumbs && pageTitleBreadcrumbDisplay"
-        class="breadcrumb breadcrumb-separatorless fw-semibold fs-7 my-0 pt-1"
-      >
-        <!--begin::Item-->
-        <!-- <li class="breadcrumb-item text-muted">
+        <!--begin::Breadcrumb-->
+        <ul
+          v-if="breadcrumbs && pageTitleBreadcrumbDisplay"
+          class="breadcrumb breadcrumb-separatorless fw-semibold fs-7 my-0 pt-1"
+        >
+          <!--begin::Item-->
+          <!-- <li class="breadcrumb-item text-muted">
           <router-link to="/" class="text-muted text-hover-primary">Home</router-link>
         </li> -->
-        <!--end::Item-->
-        <template v-for="(item, i) in breadcrumbs" :key="i">
-          <!--begin::Item-->
-
-          <li v-if="i != 0" class="breadcrumb-item">
-            <span class="bullet bg-gray-400 w-5px h-2px"></span>
-          </li>
-          <!--end::Item-->
-          <!--begin::Item-->
-          <li class="breadcrumb-item text-muted">{{ item }}</li>
           <!--end::Item-->
-        </template>
-      </ul>
-      <!--end::Breadcrumb-->
-    </template>
+          <template v-for="(item, i) in breadcrumbs" :key="i">
+            <!--begin::Item-->
+
+            <li v-if="i != 0" class="breadcrumb-item">
+              <span class="bullet bg-gray-400 w-5px h-2px"></span>
+            </li>
+            <!--end::Item-->
+            <!--begin::Item-->
+            <li class="breadcrumb-item text-muted">{{ item }}</li>
+            <!--end::Item-->
+          </template>
+        </ul>
+        <!--end::Breadcrumb-->
+      </template>
+    </div>
+    <div v-else class="align-items-stretch"></div>
   </div>
-  <div v-else class="align-items-stretch"></div>
   <!--end::Page title-->
 </template>
 
-<script lang="ts">
-import { defineComponent, computed } from "vue"
+<script lang="ts" setup>
+import { computed } from "vue"
+import { getAssetPath } from "@/core/helpers/assets"
 import { pageTitleDisplay, pageTitleBreadcrumbDisplay, pageTitleDirection } from "@/core/helpers/config"
-import { useRoute } from "vue-router"
-
-export default defineComponent({
-  name: "layout-page-title",
-  components: {},
-  setup() {
-    const route = useRoute()
+import { useRoute, useRouter } from "vue-router"
+const route = useRoute()
+const router = useRouter()
 
-    const pageTitle = computed(() => {
-      return route.meta.pageTitle
-    })
+const showBackBtn = computed(() => {
+  return route.query.back && Number(route.query.back) != 0
+})
 
-    const breadcrumbs = computed(() => {
-      return route.meta.breadcrumbs
-    })
+const pageTitle = computed(() => {
+  return route.meta.pageTitle
+})
 
-    return {
-      pageTitle,
-      breadcrumbs,
-      pageTitleDisplay,
-      pageTitleBreadcrumbDisplay,
-      pageTitleDirection,
-    }
-  },
+const breadcrumbs = computed(() => {
+  return route.meta.breadcrumbs
 })
 </script>

+ 12 - 5
src/layouts/main-layout/menus/NotificationsMenu.vue

@@ -4,6 +4,7 @@ import { getAssetPath } from "@/core/helpers/assets"
 import Rs from "@/core/services/RequestService"
 import appStore from "@/stores"
 import router from "@/router"
+
 const total = ref(0)
 const curPage = ref(0)
 const perPage = ref(10)
@@ -17,6 +18,7 @@ const params = computed(() => {
 let reLoadTimre: any
 const listBox = ref<HTMLElement>()
 const dataList = ref<Array<any>>([])
+const isShow = ref(true)
 function jump(item: any) {
   read(item.id)
   switch (appStore.authStore.user?.userType) {
@@ -92,11 +94,15 @@ function readAll() {
 }
 function load() {
   curPage.value++
-  Rs.post("sys/pushEvent/getList", { data: params.value, successAlert: false }).then((res: any) => {
-    console.log("NOTIFI", res.data)
-    dataList.value.push(...res.data)
-    total.value = res.total
-  })
+  Rs.post("sys/pushEvent/getList", { data: params.value, successAlert: false, errorAlert: false })
+    .then((res: any) => {
+      console.log("NOTIFI", res.data)
+      dataList.value.push(...res.data)
+      total.value = res.total
+    })
+    .catch(() => {
+      isShow.value = false
+    })
 }
 function reLoad() {
   curPage.value = 0
@@ -126,6 +132,7 @@ onMounted(() => {
 
 <template>
   <div
+    v-if="isShow"
     class="btn btn-icon btn-custom btn-icon-muted btn-active-light btn-active-color-primary w-30px h-30px w-md-40px h-md-40px position-relative"
     data-kt-menu-trigger="click"
     data-kt-menu-attach="parent"

+ 0 - 3
src/layouts/main-layout/sidebar/Sidebar.vue

@@ -14,7 +14,6 @@
   >
     <KTSidebarLogo></KTSidebarLogo>
     <KTSidebarMenu></KTSidebarMenu>
-    <KTSidebarFooter></KTSidebarFooter>
   </div>
   <!--end::sidebar-->
 </template>
@@ -24,14 +23,12 @@ import { defineComponent } from "vue"
 import { displaySidebar } from "@/core/helpers/config"
 import KTSidebarLogo from "@/layouts/main-layout/sidebar/SidebarLogo.vue"
 import SidebarMenu from "@/layouts/main-layout/sidebar/SidebarMenu.vue"
-import KTSidebarFooter from "@/layouts/main-layout/sidebar/SidebarFooter.vue"
 
 export default defineComponent({
   name: "theme-sidebar",
   components: {
     KTSidebarLogo,
     KTSidebarMenu: SidebarMenu,
-    KTSidebarFooter,
   },
   setup() {
     return {

+ 7 - 24
src/layouts/main-layout/sidebar/SidebarMenu.vue

@@ -19,7 +19,7 @@
           <div v-if="item.heading" class="menu-item pt-5">
             <div class="menu-content">
               <span class="menu-heading fw-bold text-uppercase fs-7">
-                {{ translate(item.heading) }}
+                {{ item.heading }}
               </span>
             </div>
           </div>
@@ -33,7 +33,7 @@
                       <inline-svg :src="menuItem.svgIcon" />
                     </span>
                   </span>
-                  <span class="menu-title">{{ translate(menuItem.heading) }}</span>
+                  <span class="menu-title">{{ menuItem.heading }}</span>
                 </router-link>
               </div>
             </template>
@@ -51,7 +51,7 @@
                     <inline-svg :src="menuItem.svgIcon" />
                   </span>
                 </span>
-                <span class="menu-title">{{ translate(menuItem.sectionTitle) }}</span>
+                <span class="menu-title">{{ menuItem.sectionTitle }}</span>
                 <span class="menu-arrow"></span>
               </span>
               <div :class="{ show: hasActiveChildren(menuItem.route) }" class="menu-sub menu-sub-accordion">
@@ -61,7 +61,7 @@
                       <span class="menu-bullet">
                         <span class="bullet bullet-dot"></span>
                       </span>
-                      <span class="menu-title">{{ translate(item2.heading) }}</span>
+                      <span class="menu-title">{{ item2.heading }}</span>
                     </router-link>
                   </div>
                   <div
@@ -75,7 +75,7 @@
                       <span class="menu-bullet">
                         <span class="bullet bullet-dot"></span>
                       </span>
-                      <span class="menu-title">{{ translate(item2.sectionTitle) }}</span>
+                      <span class="menu-title">{{ item2.sectionTitle }}</span>
                       <span class="menu-arrow"></span>
                     </span>
                     <div :class="{ show: hasActiveChildren(item2.route) }" class="menu-sub menu-sub-accordion">
@@ -85,7 +85,7 @@
                             <span class="menu-bullet">
                               <span class="bullet bullet-dot"></span>
                             </span>
-                            <span class="menu-title">{{ translate(item3.heading) }}</span>
+                            <span class="menu-title">{{ item3.heading }}</span>
                           </router-link>
                         </div>
                       </template>
@@ -106,32 +106,15 @@
 
 <script lang="ts" setup>
 //import { getAssetPath } from "@/core/helpers/assets"
-import { onMounted, ref } from "vue"
+import { ref } from "vue"
 import { useRoute } from "vue-router"
 import { sidebarMenuIcons } from "@/core/helpers/config"
-import { useI18n } from "vue-i18n"
 import appStore from "@/stores"
 
-const { t, te } = useI18n()
 const route = useRoute()
-const scrollElRef = ref<null | HTMLElement>(null)
 
 const MainMenu = ref(appStore.menuStore.getSidebarMenus())
 
-onMounted(() => {
-  if (scrollElRef.value) {
-    scrollElRef.value.scrollTop = 0
-  }
-})
-
-const translate = (text: string) => {
-  if (te(text)) {
-    return t(text)
-  } else {
-    return text
-  }
-}
-
 const hasActiveChildren = (match: string) => {
   return route.path.indexOf(match) !== -1
 }

+ 4 - 1
src/router/Index.ts

@@ -105,7 +105,10 @@ const hasNecessaryRoute = (to: RouteLocationNormalized) => {
 let isReload = true
 router.beforeEach((to, from, next) => {
   const configStore = appStore.bodyConfigStore
-
+  //解决切换路由tooltip不消失
+  document.querySelectorAll(".tooltip").forEach((v) => {
+    v.remove()
+  })
   // current page view title
   document.title = `${to.meta.pageTitle ?? ""} - ${import.meta.env.VITE_APP_NAME}`
 

+ 8 - 0
src/router/menuMap/_goLineData.ts

@@ -13,6 +13,14 @@ export const menus: Array<MenuRouteMap> = [
     path: "/goLineData/oilFumeConcentration",
     component: () => import("@/views/goLineData/oilFumeConcentration.vue"),
   },
+  {
+    path: "/goLineDataMonitor",
+    icon: "fas fa-rss-square",
+  },
+  {
+    path: "/goLineDataMonitor/dischargeMonitor",
+    component: () => import("@/views/goLineData/oilFumeConcentration.vue"),
+  },
 ]
 
 export default menus

+ 4 - 0
src/router/menuMap/_mobile.ts

@@ -9,6 +9,10 @@ export const menus: Array<MenuRouteMap> = [
     path: "/mobile/supervision",
     component: () => import("@/views/mobile/supervision.vue"),
   },
+  {
+    path: "/mobile/inspector",
+    component: () => import("@/views/mobile/inspector.vue"),
+  },
 ]
 
 export default menus

+ 18 - 0
src/router/menuMap/_purify.ts

@@ -5,10 +5,28 @@ export const menus: Array<MenuRouteMap> = [
     path: "/purify",
     icon: "fas fa-shower",
   },
+
   {
     path: "/purify/washInfo",
     component: () => import("@/views/purify/washInfo.vue"),
   },
+  {
+    path: "/purifyMonitor",
+    icon: "fas fa-shower",
+  },
+  {
+    path: "/purifyMonitor/TWP",
+    component: () => import("@/views/purify/washInfo_company.vue"),
+  },
+  //清洗运维
+  {
+    path: "/qxyw",
+    icon: "fas fa-shower",
+  },
+  {
+    path: "/qxyw/showlist",
+    component: () => import("@/views/purify/qxyw.vue"),
+  },
 ]
 
 export default menus

+ 16 - 0
src/router/menuMap/_warn.ts

@@ -13,5 +13,21 @@ export const menus: Array<MenuRouteMap> = [
     path: "/warn/ouput",
     component: () => import("@/views/warn/ouput.vue"),
   },
+  {
+    path: "/warn/qWarnList",
+    component: () => import("@/views/warn/companyWarnList.vue"),
+  },
+  {
+    path: "/Abnormal",
+    icon: "fas fa-exclamation-triangle",
+  },
+  {
+    path: "/Abnormal/AbnormalList",
+    component: () => import("@/views/warn/abnormal.vue"),
+  },
+  {
+    path: "/Abnormal/AbnormalTable",
+    component: () => import("@/views/warn/abnormal_declare.vue"),
+  },
 ]
 export default menus

+ 18 - 0
src/router/menuMap/_workNotice.ts

@@ -0,0 +1,18 @@
+import type { MenuRouteMap } from "@/core/config/MainMenuConfig"
+//purify 清洗管理
+export const menus: Array<MenuRouteMap> = [
+  {
+    path: "/workNotice",
+    icon: "fas fa-shower",
+  },
+  {
+    path: "/workNotice/worryNotice",
+    component: () => import("@/views/workNotice/worryNotice.vue"),
+  },
+  {
+    path: "/workNotice/washNotice",
+    component: () => import("@/views/workNotice/washNotice.vue"),
+  },
+]
+
+export default menus

+ 3 - 0
src/router/menuMap/index.ts

@@ -7,6 +7,7 @@ import goLineData from "./_goLineData"
 import mobile from "./_mobile"
 import examApproval from "./_examApproval"
 import system from "./_system"
+import workNotice from "./_workNotice"
 export const MenuRouteMaps: Array<MenuRouteMap> = [
   {
     path: "/DataBoard",
@@ -32,6 +33,8 @@ export const MenuRouteMaps: Array<MenuRouteMap> = [
   ...warn,
   //server 系统管理
   ...system,
+  //workNotice
+  ...workNotice,
 ]
 
 export default MenuRouteMaps

+ 106 - 0
src/router/statictRouter.ts

@@ -54,4 +54,110 @@ export const staticRotuer: Array<RouteRecordRaw> = [
       },
     ],
   },
+  {
+    path: "/",
+    component: () => import("@/layouts/main-layout/MainLayout.vue"),
+    children: [
+      {
+        path: "/analysisInfo/overdue/company",
+        component: () => import("@/views/analysisInfo/overdueCompany.vue"),
+        meta: {
+          pageTitle: "逾期报警情况",
+          breadcrumbs: ["综合分析", "清洗分析", "逾期报警"],
+        },
+      },
+      {
+        path: "/analysisInfo/overdue/warn",
+        component: () => import("@/views/analysisInfo/overdueWarn.vue"),
+        meta: {
+          pageTitle: "清洗报警详情",
+          breadcrumbs: ["综合分析", "清洗分析", "逾期报警", "清洗报警详情"],
+        },
+      },
+      {
+        path: "/analysisInfo/weekCleanList",
+        component: () => import("@/views/analysisInfo/weekCleanList.vue"),
+        meta: {
+          pageTitle: "清洗报警详情",
+          breadcrumbs: ["综合分析", "清洗分析", "清洗情况"],
+        },
+      },
+    ],
+  },
+  {
+    path: "/",
+    component: () => import("@/layouts/main-layout/MainLayout.vue"),
+    children: [
+      {
+        path: "/purify/washInfo/details",
+        component: () => import("@/views/purify/washInfo_details.vue"),
+        meta: {
+          pageTitle: "净化器清洗情况",
+          breadcrumbs: ["清洗管理", "清洗台账", "净化器清洗情况"],
+        },
+      },
+      {
+        path: "/qxyw/details",
+        component: () => import("@/views/purify/qxyw_detail.vue"),
+        meta: {
+          pageTitle: "清洗运维",
+          breadcrumbs: ["清洗运维"],
+        },
+      },
+    ],
+  },
+  {
+    path: "/",
+    component: () => import("@/layouts/main-layout/MainLayout.vue"),
+    children: [
+      {
+        path: "/AbnormalList/strongWarn",
+        component: () => import("@/views/warn/abnormalList/strongWarn.vue"),
+        meta: {
+          pageTitle: "超标告警详情",
+          breadcrumbs: ["异常申报", "异常列表", "超标告警详情"],
+        },
+      },
+      {
+        path: "/AbnormalList/disConnect",
+        component: () => import("@/views/warn/abnormalList/disConnect.vue"),
+        meta: {
+          pageTitle: "超标告警详情",
+          breadcrumbs: ["异常申报", "异常列表", "设备离线告警详情"],
+        },
+      },
+      {
+        path: "/AbnormalList/overdueWarn",
+        component: () => import("@/views/warn/abnormalList/overdueWarn.vue"),
+        meta: {
+          pageTitle: "超标告警详情",
+          breadcrumbs: ["异常申报", "异常列表", "清洗逾期告警详情"],
+        },
+      },
+      {
+        path: "/warnList/strongWarn",
+        component: () => import("@/views/warn/abnormalList/strongWarn.vue"),
+        meta: {
+          pageTitle: "超标告警详情",
+          breadcrumbs: ["告警管理", "告警列表", "超标告警详情"],
+        },
+      },
+      {
+        path: "/warnList/disConnect",
+        component: () => import("@/views/warn/abnormalList/disConnect.vue"),
+        meta: {
+          pageTitle: "超标告警详情",
+          breadcrumbs: ["告警管理", "告警列表", "设备离线告警详情"],
+        },
+      },
+      {
+        path: "/warnList/overdueWarn",
+        component: () => import("@/views/warn/abnormalList/overdueWarn.vue"),
+        meta: {
+          pageTitle: "超标告警详情",
+          breadcrumbs: ["告警管理", "告警列表", "清洗逾期告警详情"],
+        },
+      },
+    ],
+  },
 ]

+ 3 - 0
src/stores/_auth.ts

@@ -3,6 +3,7 @@ import { defineStore } from "pinia"
 import JwtService from "@/core/services/JwtService"
 import apis from "@/api"
 import storages from "@/core/utils/storages"
+import appStore from "."
 
 export interface User {
   userName: string
@@ -21,6 +22,7 @@ export const useAuthStore = defineStore("auth", () => {
     isAuthenticated.value = true
     errors.value = {}
     user.value = authUser
+    console.log("AUTHUSER", authUser)
     storages.setItem(USER_KEY, JSON.stringify(authUser))
     JwtService.saveToken(authUser.token)
   }
@@ -34,6 +36,7 @@ export const useAuthStore = defineStore("auth", () => {
     user.value = null
     errors.value = []
     storages.removeItem(USER_KEY)
+    appStore.menuStore.removeMenus()
     JwtService.destroyToken()
   }
 

+ 23 - 8
src/stores/_menu.ts

@@ -7,7 +7,11 @@ import { MenuRouteMaps } from "@/router/menuMap"
 import router, { to404 } from "@/router"
 import apis from "@/api"
 import configs from "@/core/config/Index"
-
+enum UserType {
+  System = "0",
+  ZhengFu = "1",
+  Company = "2",
+}
 export const useMenuStore = defineStore("meun", () => {
   const MENU_KEY = "fun_menu"
   let menuData: Array<FunctionMenu> = JSON.parse(Stroage.getItem(MENU_KEY) || "[]")
@@ -81,20 +85,20 @@ export const useMenuStore = defineStore("meun", () => {
   const formatterRoutes = (funs: Array<FunctionMenu>, withChild = true) => {
     //router.addRoute(configs.MAIN_ROUTER_NAME, homeRouter)
     if (funs && funs.length) {
-      funs.forEach((v) => {
-        formatterRouteItem(v, withChild)
-      })
+      for (let i = 0; i < funs.length; i++) {
+        formatterRouteItem(funs[i], withChild, i == 0)
+      }
     }
     router.addRoute(to404)
     //console.log("===========", router.getRoutes());
   }
-  const formatterRouteItem = (fun: FunctionMenu, withChild = true) => {
+  const formatterRouteItem = (fun: FunctionMenu, withChild = true, isFirst = false) => {
     const menu: MenuRouteMap | undefined = MenuRouteMaps.find((v) => v.path == fun.url)
     if (menu?.component == null) {
       if (withChild && fun.children && fun.children.length) {
-        fun.children.forEach((v) => {
-          formatterRouteItem(v)
-        })
+        for (let i = 0; i < fun.children.length; i++) {
+          formatterRouteItem(fun.children[i], withChild, i == 0 && isFirst)
+        }
       }
       return
     }
@@ -108,6 +112,17 @@ export const useMenuStore = defineStore("meun", () => {
         breadcrumbs: getBreadcrumbs(fun),
       },
     }
+
+    if (isFirst) {
+      //动态配置首页跳转路由 默认菜单第一项下的第一个页面
+      const mainRouter = router.getRoutes().find((v) => {
+        return v.name == configs.MAIN_ROUTER_NAME
+      })
+      //console.log("MR", mainRouter)
+      if (mainRouter) {
+        mainRouter.redirect = item.path
+      }
+    }
     routes.value.push(item)
 
     router.addRoute(configs.MAIN_ROUTER_NAME, item)

+ 226 - 0
src/views/_component/_WarnNoProcess.vue

@@ -0,0 +1,226 @@
+<script setup lang="ts">
+import { ref, computed, onMounted } from "vue"
+import moment from "moment"
+import router from "@/router"
+import Rs from "@/core/services/RequestService"
+const props = withDefaults(
+  defineProps<{
+    type?: number
+    columns?: any
+  }>(),
+  { type: 0 }
+)
+const tableUrl = computed(() => {
+  return props.type == 0 || props.type == 1
+    ? "sys/workOrderOrg/getNoOrderWarnByOrgForPage"
+    : "sys/workOrderCom/getNoOrderWarnByComForPage"
+})
+
+const cols = ref<Array<any>>([
+  {
+    name: "公司名称",
+    field: "company_name",
+  },
+  {
+    name: "区域",
+    field: "org_name",
+    width: 150,
+  },
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "报警类型",
+    field: "warn_type_name",
+  },
+
+  {
+    name: "报警时间",
+    field: "warn_time",
+  },
+  {
+    name: "操作",
+    width: 150,
+    field: "action",
+  },
+])
+
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const dateRange = ref<[Date, Date]>([moment(new Date()).add(-7, "d").toDate(), new Date()])
+const companyName = ref("")
+const warnType = ref("")
+//const orgId = ref("0")
+//const monitoringType = ref("")
+//const abnormalState = ref("")
+const queryParams = ref<any>({
+  query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+  query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+})
+const table = ref()
+
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      back: 1,
+      comName: v.company_name,
+      company_id: v.company_id,
+    },
+  })
+}
+
+function query() {
+  const params = {
+    //monitoring_type: monitoringType.value,
+    //org_id: orgId.value,
+    //abnormal_state: abnormalState.value,
+    company_name: companyName.value,
+    warn_type: warnType.value,
+    query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+    query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "" && params[key] !== 0) {
+      delete params[key]
+    }
+  })
+  queryParams.value = params
+}
+function reset() {
+  //orgId.value = "0"
+  //monitoringType.value = ""
+  //abnormalState.value = ""
+  companyName.value = ""
+  warnType.value = ""
+  dateRange.value = [moment(new Date()).add(-7, "d").toDate(), new Date()]
+  query()
+}
+const modal = ref()
+const noticeDetails = ref<any>({})
+const formData = ref({ content: "" })
+function Urging(row: any) {
+  formData.value.content = ""
+  noticeDetails.value = Object.assign({}, row)
+  modal.value.show()
+}
+function onSave() {
+  Rs.post("sys/workOrderOrg/startWorkOrderByOrg", {
+    data: {
+      warn_id: noticeDetails.value.id,
+      content: formData.value.content,
+    },
+  })
+}
+function init() {
+  if (props.type != 0 && props.type != 1) {
+    cols.value = props.columns
+  }
+}
+onMounted(init)
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    :url="tableUrl"
+    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="商户名称" v-if="props.type == 0 || props.type == 1">
+          <el-input
+            class=""
+            :style="dySearchSelectStyle"
+            v-model="companyName"
+            placeholder="请输入商户名称"
+            :size="size"
+          />
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="日期">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="~"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            :size="size"
+          />
+        </el-form-item>
+
+        <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?type=1'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择告警类型"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-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 }">
+      <span class="table-action" @click="Urging(row)">发起催办</span>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    title="发起催办"
+    :form-data="formData"
+    :form-items="[
+      {
+        label: '催办内容:',
+        field: 'content',
+        component: 'slot',
+        rules: [{ required: true, message: '请填写催办内容', trigger: ['blur'] }],
+      },
+    ]"
+    @confirm="onSave"
+  >
+    <template #body>
+      <el-row>
+        <el-col :span="12">
+          <dl>
+            <dt>企业名称:</dt>
+            <dd>{{ noticeDetails.company_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>异常类型:</dt>
+            <dd>{{ noticeDetails.warn_type_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>告警设备:</dt>
+            <dd>{{ noticeDetails.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>告警时间:</dt>
+            <dd>{{ noticeDetails.warn_time }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <div class="separator mt-2 mb-5"></div>
+    </template>
+    <template #content_form>
+      <el-input v-model="formData.content" type="textarea" placeholder="请输入催办内容"></el-input>
+    </template>
+  </VbModal>
+</template>

+ 247 - 0
src/views/_component/_WarnProcessed.vue

@@ -0,0 +1,247 @@
+<script setup lang="ts">
+import { ref, computed, onMounted } from "vue"
+import moment from "moment"
+import router from "@/router"
+import ProcessDetail from "./__WarnProcessDetail.vue"
+import Rs from "@/core/services/RequestService"
+const props = withDefaults(
+  defineProps<{
+    type?: number
+    columns?: any
+  }>(),
+  { type: 0 }
+)
+const tableUrl = computed(() => {
+  return props.type == 0
+    ? "sys/workOrderOrg/getFinishedWorkOrderByOrgAdmin"
+    : props.type == 0
+    ? "sys/workOrderOrg/getFinishedWorkOrderByOrgLaw"
+    : props.type == 2
+    ? "sys/workOrderCom/getFinishedWorkOrderByCom"
+    : ""
+})
+const cols = ref([
+  {
+    name: "公司名称",
+    field: "company_name",
+  },
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "报警类型",
+    field: "warn_type_name",
+  },
+
+  {
+    name: "创建时间",
+    field: "work_order_start_time",
+  },
+  {
+    name: "完结时间",
+    field: "work_order_end_time",
+  },
+  {
+    name: "流程耗时",
+    field: "work_order_continue_time",
+  },
+  {
+    name: "操作",
+    width: 150,
+    field: "action",
+  },
+])
+
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const dateRange = ref<[Date, Date]>([moment(new Date()).add(-7, "d").toDate(), new Date()])
+const companyName = ref("")
+const warnType = ref("")
+//const orgId = ref("0")
+//const monitoringType = ref("")
+//const abnormalState = ref("")
+const queryParams = ref<any>(
+  props.type == 0
+    ? {
+        query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+        query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+      }
+    : {}
+)
+const table = ref()
+// const getStayTimes = (item: any) => {
+//   if (!!item.warn_endtime && !!item.work_order_start_time) {
+//     const stayTime = moment(item.work_order_end_time, "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 ""
+// }
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      back: 1,
+      comName: v.company_name,
+      company_id: v.company_id,
+    },
+  })
+}
+
+function query() {
+  const params: any = {
+    //monitoring_type: monitoringType.value,
+    //org_id: orgId.value,
+    //abnormal_state: abnormalState.value,
+    company_name: companyName.value,
+    warn_type: warnType.value,
+  }
+  if (props.type == 0) {
+    params.query_start_time = moment(dateRange.value[0]).format("YYYYMMDD")
+    params.query_end_time = moment(dateRange.value[1]).format("YYYYMMDD")
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "" && params[key] !== 0) {
+      delete params[key]
+    }
+  })
+  queryParams.value = params
+}
+function reset() {
+  //orgId.value = "0"
+  //monitoringType.value = ""
+  //abnormalState.value = ""
+  companyName.value = ""
+  warnType.value = ""
+  dateRange.value = [moment(new Date()).add(-7, "d").toDate(), new Date()]
+  query()
+}
+const modal = ref()
+const warnDetails = ref<any>({})
+function detail(row: any) {
+  const url =
+    props.type == 0
+      ? "sys/workOrderOrg/getWorkOrderDetailByOrg"
+      : props.type == 1
+      ? "sys/workOrderOrg/getWorkOrderDetailByOrgLaw"
+      : props.type == 2
+      ? "sys/workOrderCom/getWorkOrderDetailByCom"
+      : ""
+  Rs.post(url, {
+    data: {
+      work_order_id: row.work_order_id,
+    },
+  }).then((res) => {
+    console.log("WARN", res.data)
+    res.data.processInfoList.forEach((item: any) => {
+      if (item.picture_url != null) {
+        const picList = item.picture_url.split(",")
+        const picReall: Array<any> = [] //去除空的
+        picList.forEach((pic: string, index: number) => {
+          if (pic != "") {
+            pic = pic.substring(pic.indexOf("/api/file/"))
+            picReall.push({
+              uid: index,
+              name: "image.png",
+              status: "done",
+              url: pic,
+            })
+          }
+        })
+        item.fileList = picReall
+      } else {
+        item.fileList = []
+      }
+    })
+    warnDetails.value = res.data
+    modal.value.show()
+  })
+}
+function init() {
+  if (props.type != 0 && props.type != 1) {
+    cols.value = props.columns
+  }
+}
+onMounted(init)
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    :url="tableUrl"
+    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="商户名称" v-if="props.type == 0 || props.type == 1">
+          <el-input
+            class=""
+            :style="dySearchSelectStyle"
+            v-model="companyName"
+            placeholder="请输入商户名称"
+            :size="size"
+          />
+        </el-form-item>
+        <el-form-item v-if="props.type == 0" class="mb-0 me-5 align-items-center" label="日期">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="~"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            :size="size"
+          />
+        </el-form-item>
+
+        <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?type=1'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择告警类型"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-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 }">
+      <span class="table-action" @click="detail(row)">查看详情</span>
+    </template>
+    <template #work_order_start_time="{ row }">
+      {{ moment(row.work_order_start_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") }}
+    </template>
+    <template #work_order_end_time="{ row }">
+      {{ moment(row.work_order_end_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") }}
+    </template>
+    <!-- <template #work_order_continue_time="{ row }">
+      {{ getStayTimes(row) }}
+    </template> -->
+  </VbDataTable>
+  <VbModal v-model:modal="modal" title="详情">
+    <template #body>
+      <ProcessDetail :data="warnDetails"></ProcessDetail>
+    </template>
+  </VbModal>
+</template>

+ 435 - 0
src/views/_component/_WarnProcessing.vue

@@ -0,0 +1,435 @@
+<script setup lang="ts">
+import { ref, computed, withDefaults, onMounted } from "vue"
+import moment from "moment"
+import router from "@/router"
+import Rs from "@/core/services/RequestService"
+import { ElInput, ElSelect, ElRadioGroup } from "element-plus"
+import VbUpload from "@/components/Upload/VbUpload.vue"
+import ProcessDetail from "./__WarnProcessDetail.vue"
+const props = withDefaults(
+  defineProps<{
+    type?: number
+    columns?: any
+  }>(),
+  { type: 0 }
+)
+const tableUrl = computed(() => {
+  return props.type == 0 //政府账户
+    ? "sys/workOrderOrg/getHandingWorkOrderByOrgAdmin"
+    : props.type == 1 //督察账户
+    ? "sys/workOrderOrg/getHandingWorkOrderByOrgLaw"
+    : props.type == 2 //企业账户
+    ? "sys/workOrderCom/getHandingWorkOrderByCom"
+    : ""
+})
+const cols = ref([
+  {
+    name: "公司名称",
+    field: "company_name",
+  },
+  {
+    name: "区域",
+    field: "org_name",
+  },
+
+  {
+    name: "报警类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "状态",
+    field: "work_order_stats_name",
+  },
+
+  {
+    name: "操作",
+    width: 150,
+    field: "action",
+  },
+])
+
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const dateRange = ref<[Date, Date]>([moment(new Date()).add(-7, "d").toDate(), new Date()])
+const companyName = ref("")
+const warnType = ref("")
+const workOrderState = ref("")
+//const orgId = ref("0")
+//const monitoringType = ref("")
+//const abnormalState = ref("")
+const queryParams = ref<any>(
+  props.type == 0
+    ? {
+        query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+        query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+      }
+    : {}
+)
+const table = ref()
+
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      back: 1,
+      comName: v.company_name,
+      company_id: v.company_id,
+    },
+  })
+}
+
+function query() {
+  const params: any = {
+    //monitoring_type: monitoringType.value,
+    //org_id: orgId.value,
+    //abnormal_state: abnormalState.value,
+    company_name: companyName.value,
+    warn_type: warnType.value,
+    work_order_state: workOrderState.value,
+  }
+  if (props.type == 0) {
+    params.query_start_time = moment(dateRange.value[0]).format("YYYYMMDD")
+    params.query_end_time = moment(dateRange.value[1]).format("YYYYMMDD")
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "" && params[key] !== 0) {
+      delete params[key]
+    }
+  })
+  queryParams.value = params
+}
+function reset() {
+  //orgId.value = "0"
+  //monitoringType.value = ""
+  //abnormalState.value = ""
+  companyName.value = ""
+  warnType.value = ""
+  workOrderState.value = ""
+  dateRange.value = [moment(new Date()).add(-7, "d").toDate(), new Date()]
+  query()
+}
+const modal = ref()
+const warnDetails = ref<any>({})
+const userList = ref<Array<any>>([])
+const operationType = ref<"D" | "A" | "T" | "R">("D")
+
+const title = computed(() => {
+  return operationType.value == "D"
+    ? "详情"
+    : operationType.value == "A"
+    ? "审核"
+    : operationType.value == "T"
+    ? "转交"
+    : operationType.value == "R"
+    ? "上报"
+    : ""
+})
+const auditFormData = ref<any>({
+  audit_state: "1",
+  content: "",
+})
+const transforFormData = ref<any>({
+  law_user_id: "",
+  content: "",
+})
+const reportFormData = ref({
+  picture_url: "",
+  content: "",
+})
+const formData = computed(() => {
+  return props.type == 0
+    ? operationType.value == "A"
+      ? auditFormData.value
+      : operationType.value == "T"
+      ? transforFormData.value
+      : {}
+    : reportFormData.value
+})
+const validatePic = (rule: any, value: any, callback: any) => {
+  if (!value) {
+    callback(new Error("请选择附件"))
+  } else {
+    callback()
+  }
+}
+const uploadEl = ref()
+const formItems: any = computed(() => {
+  return props.type == 0
+    ? operationType.value == "A"
+      ? [
+          {
+            label: "审核意见",
+            field: "audit_state",
+            required: true,
+            component: ElRadioGroup,
+            data: [
+              { label: "审核通过", value: "1" },
+              { label: "审核不通过", value: "2" },
+            ],
+          },
+          {
+            label: "审核备注",
+            placeholder: "请输入备注",
+            field: "content",
+            required: true,
+            type: "textarea",
+            component: ElInput,
+          },
+        ]
+      : operationType.value == "T"
+      ? [
+          {
+            label: "转交人员",
+            field: "law_user_id",
+            placeholder: "请选择转交人员",
+            required: true,
+            component: ElSelect,
+            data: userList.value.map((v: any) => {
+              return {
+                label: v.name,
+                value: v.code,
+              }
+            }),
+          },
+          {
+            label: "转交备注",
+            placeholder: "请输入备注",
+            field: "content",
+            required: true,
+            type: "textarea",
+            component: ElInput,
+          },
+        ]
+      : []
+    : props.type == 1
+    ? operationType.value == "D"
+      ? []
+      : [
+          {
+            label: "上报备注",
+            placeholder: "请输入备注",
+            field: "content",
+            required: true,
+            type: "textarea",
+            component: ElInput,
+          },
+          {
+            label: "上传图片",
+            field: "picture_url",
+            component: VbUpload,
+            rules: [{ validator: validatePic, trigger: "change" }],
+          },
+        ]
+    : props.type == 2
+    ? operationType.value == "D"
+      ? []
+      : [
+          {
+            label: "申诉备注",
+            placeholder: "请输入备注",
+            field: "content",
+            required: true,
+            type: "textarea",
+            component: ElInput,
+          },
+          {
+            label: "申诉附件",
+            field: "picture_url",
+            component: "slot",
+            rules: [{ validator: validatePic, trigger: "change" }],
+          },
+        ]
+    : []
+})
+const currentId = ref("")
+function detail(row: any) {
+  operationType.value = "D"
+  queryDetail(row.work_order_id)
+}
+function audit(row: any) {
+  operationType.value = "A"
+  auditFormData.value.audit_state = "1"
+  auditFormData.value.content = ""
+  queryDetail(row.work_order_id)
+}
+function transfer(row: any) {
+  operationType.value = "T"
+  transforFormData.value.law_user_id = ""
+  transforFormData.value.content = ""
+  Rs.post("sys/workOrderOrg/getLawList", {
+    data: {
+      work_order_id: row.work_order_id,
+    },
+  }).then((res) => {
+    userList.value = res.data.list
+    queryDetail(row.work_order_id)
+  })
+}
+function report(row: any) {
+  operationType.value = "R"
+  reportFormData.value.picture_url = ""
+  reportFormData.value.content = ""
+  uploadEl.value?.clearFiles()
+  queryDetail(row.work_order_id)
+}
+function queryDetail(id: string) {
+  currentId.value = id
+  const url =
+    props.type == 0
+      ? "sys/workOrderOrg/getWorkOrderDetailByOrg"
+      : props.type == 1
+      ? "sys/workOrderOrg/getWorkOrderDetailByOrgLaw"
+      : props.type == 2
+      ? "sys/workOrderCom/getWorkOrderDetailByCom"
+      : ""
+  Rs.post(url, {
+    data: {
+      work_order_id: id,
+    },
+  }).then((res) => {
+    //console.log("WARN", res.data)
+    res.data.processInfoList.forEach((item: any) => {
+      if (item.picture_url != null) {
+        const picList = item.picture_url.split(",")
+        const picReall: Array<any> = [] //去除空的
+        picList.forEach((pic: string, index: number) => {
+          if (pic != "") {
+            pic = pic.substring(pic.indexOf("/api/file/"))
+            picReall.push({
+              uid: index,
+              name: "image.png",
+              status: "done",
+              url: pic,
+            })
+          }
+        })
+        item.fileList = picReall
+      } else {
+        item.fileList = []
+      }
+    })
+    warnDetails.value = res.data
+    modal.value.show()
+  })
+}
+function onSave() {
+  const url =
+    props.type == 0
+      ? operationType.value == "A"
+        ? "sys/workOrderOrg/auditByOrg"
+        : operationType.value == "T"
+        ? "sys/workOrderOrg/transferByOrg"
+        : ""
+      : props.type == 1
+      ? "sys/workOrderOrg/uploadByOrg"
+      : props.type == 2
+      ? "sys/workOrderCom/uploadByCom"
+      : ""
+  if (url) {
+    const data: any = Object.assign({ work_order_id: currentId.value }, formData.value)
+    console.log("DATA____", data)
+    Rs.post(url, { data: data }).then(() => {
+      table.value.search()
+    })
+  }
+}
+function init() {
+  if (props.type != 0 && props.type != 1) {
+    cols.value = props.columns
+  }
+}
+onMounted(init)
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    :url="tableUrl"
+    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="商户名称" v-if="props.type == 0 || props.type == 1">
+          <el-input
+            class=""
+            :style="dySearchSelectStyle"
+            v-model="companyName"
+            placeholder="请输入商户名称"
+            :size="size"
+          />
+        </el-form-item>
+        <el-form-item v-if="props.type != 1" class="mb-0 me-5 align-items-center" label="日期">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="~"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            :size="size"
+          />
+        </el-form-item>
+
+        <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?type=1'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择告警类型"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="状态:">
+          <el-select v-model="workOrderState" placeholder="请选择状态" clearable>
+            <el-option value="0" label="待审核"></el-option>
+            <el-option value="1" label="待反馈"></el-option>
+            <el-option value="2" label="已转交"></el-option>
+            <el-option value="3" label="已完结"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-form-item>
+      </el-form>
+    </template>
+    <template #company_name="{ row }" v-if="props.type == 0 || props.type == 1">
+      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["company_name"] }}</span>
+    </template>
+    <template #action="{ row }">
+      <template v-if="props.type == 0">
+        <template v-if="row.work_order_stats == 0">
+          <span class="table-action" @click="audit(row)">审核</span>
+          <span class="table-action" @click="transfer(row)">转交</span>
+        </template>
+        <template v-else>
+          <span class="table-action" @click="detail(row)">详情</span>
+        </template>
+      </template>
+      <template v-else-if="props.type == 1">
+        <span class="table-action" @click="report(row)">上报</span>
+      </template>
+      <template v-else-if="props.type == 2">
+        <template v-if="row.work_order_stats == 0">
+          <span class="table-action" @click="detail(row)">详情</span>
+        </template>
+        <template v-else>
+          <span class="table-action" @click="detail(row)">详情</span>
+          <span class="table-action" @click="report(row)">上报</span>
+        </template>
+      </template>
+    </template>
+  </VbDataTable>
+  <VbModal v-model:modal="modal" :title="title" :form-data="formData" :form-items="formItems" @confirm="onSave">
+    <template #body>
+      <ProcessDetail :data="warnDetails"></ProcessDetail>
+    </template>
+    <template #picture_url_form>
+      <VbUpload ref="uploadEl" v-model="formData.picture_url"></VbUpload>
+    </template>
+  </VbModal>
+</template>

+ 130 - 0
src/views/_component/__WarnProcessDetail.vue

@@ -0,0 +1,130 @@
+<script setup lang="ts">
+defineProps<{ data: any }>()
+
+function getOrderTypeName1(type: number) {
+  switch (type) {
+    case 0:
+    case 1:
+      return "发起人"
+    case 2:
+      return "督查审核"
+    case 3:
+      return "督查转交"
+    case 4:
+      return "现场督查"
+    case 5:
+      return "企业上传"
+  }
+}
+
+function getOrderTypeName2(type: number) {
+  switch (type) {
+    case 0:
+    case 2:
+    case 3:
+    case 4:
+      return "督查部门"
+    case 1:
+    case 5:
+      return "企业用户"
+  }
+}
+
+function getOrderTypeName3(type: number) {
+  switch (type) {
+    case 0:
+      return "催办内容"
+    case 1:
+      return "上报备注"
+    case 2:
+      return "审核人"
+    case 3:
+      return "备注"
+    case 4:
+    case 5:
+      return "上报备注"
+  }
+}
+</script>
+
+<template>
+  <el-row>
+    <el-col :span="12">
+      <dl>
+        <dt>企业名称:</dt>
+        <dd>{{ data.warnDetail?.company_name }}</dd>
+      </dl>
+    </el-col>
+    <el-col :span="12">
+      <dl>
+        <dt>异常类型:</dt>
+        <dd>{{ data.warnDetail?.warn_type_name }}</dd>
+      </dl>
+    </el-col>
+    <el-col :span="12">
+      <dl>
+        <dt>告警设备:</dt>
+        <dd>{{ data.warnDetail?.device_name }}</dd>
+      </dl>
+    </el-col>
+    <el-col :span="12">
+      <dl>
+        <dt>告警时间:</dt>
+        <dd>{{ data.warnDetail?.warn_start_time }}</dd>
+      </dl>
+    </el-col>
+  </el-row>
+  <div class="separator mt-2 mb-5"></div>
+  <div class="mh-500px overflow-auto">
+    <template v-for="(item, i) in data.processInfoList" :key="i">
+      <el-row>
+        <el-col :span="12">
+          <dl class="text-primary">
+            <dt>{{ getOrderTypeName1(item.work_order_type) }}:</dt>
+            <dd>{{ getOrderTypeName2(item.work_order_type) }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl class="text-primary">
+            <dt>时间:</dt>
+            <dd>{{ item.work_order_process_time }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>{{ getOrderTypeName3(item.work_order_type) }}:</dt>
+            <dd v-if="item.work_order_type == 2">{{ item.audit_state_name }}</dd>
+            <dd v-else>{{ item.content }}</dd>
+          </dl>
+        </el-col>
+        <el-col v-if="item.work_order_type == 2" :span="24">
+          <dl>
+            <dt>审核意见:</dt>
+            <dd>{{ item.content }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <el-row v-if="item.work_order_type == 1 || item.work_order_type == 4 || item.work_order_type == 5">
+        <el-col :span="6">
+          <dl>
+            <dt>上报文件:</dt>
+          </dl>
+        </el-col>
+        <el-col :span="4" v-for="(pic, j) in item.fileList" :key="j">
+          <el-image
+            style="width: 150px; height: 100px"
+            :src="pic.url"
+            :initial-index="j"
+            :preview-src-list="
+          item.fileList.map((v:any) => {
+                  return v.url
+                })
+              "
+            fit="scale-down"
+          ></el-image>
+        </el-col>
+      </el-row>
+      <div class="separator mt-2 mb-5"></div>
+    </template>
+  </div>
+</template>

+ 1 - 1
src/views/account/SignIn.vue

@@ -49,7 +49,7 @@ const onSubmitLogin = async (values: any) => {
       // Go to page after successfully login
       //router.push({ name: "dashboard" })
       appStore.menuStore.reLoadMenus()?.then(() => {
-        let queryRedirectPath = "/DataBoard/index"
+        let queryRedirectPath = "/"
         if (router.currentRoute.value.redirectedFrom?.fullPath) {
           queryRedirectPath = router.currentRoute.value.redirectedFrom?.fullPath
         }

+ 16 - 5
src/views/analysisInfo/_OverDate.vue → src/views/analysisInfo/_Overdue.vue

@@ -3,6 +3,7 @@ import { ref, onMounted } from "vue"
 import moment from "moment"
 import Rs from "@/core/services/RequestService"
 import { useRouter } from "vue-router"
+import configs from "@/core/config/Index"
 const router = useRouter()
 const ringOptions = {
   legend: { top: "center", bottom: "auto", right: "20%", orient: "vertical" },
@@ -55,7 +56,8 @@ const queryParams = ref<any>({
 const cols = ref([
   {
     name: "序号",
-    field: "index",
+    width: 60,
+    field: configs.TABLE_INDEX_FIELD,
   },
   {
     name: "区域",
@@ -90,7 +92,7 @@ const jump = function (v: any) {
     query: {
       comName: v.company_name,
       company_id: v.company_id,
-      backNeed: 1,
+      back: 1,
     },
   })
 }
@@ -148,8 +150,17 @@ function exported() {
 }
 const detail = function (row: any) {
   router.push({
-    path: "/purify/washInfo/details",
-    query: { company_id: row.company_id },
+    path: "/analysisInfo/overdue/company",
+    query: {
+      back: 1,
+      company_id: row.company_id,
+      name: row.name,
+      type: "0",
+      time_type: timeType.value,
+      warn_type: "000100004",
+      query_start_time: moment(date.value[0]).format("YYYYMMDD"),
+      query_end_time: moment(date.value[1]).format("YYYYMMDD"),
+    },
   })
 }
 function init() {
@@ -252,7 +263,7 @@ onMounted(init)
       </el-form>
     </template>
     <template #name="{ row }">
-      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["company_name"] }}</span>
+      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["name"] }}</span>
     </template>
 
     <template #action="{ row }">

+ 7 - 4
src/views/analysisInfo/_WeekClean.vue

@@ -3,6 +3,7 @@ import { ref, onMounted } from "vue"
 import moment from "moment"
 import Rs from "@/core/services/RequestService"
 import { useRouter } from "vue-router"
+import configs from "@/core/config/Index"
 const router = useRouter()
 const ringOptions = {
   legend: { top: "center", bottom: "auto", right: "20%", orient: "vertical" },
@@ -47,7 +48,8 @@ const queryParams = ref<any>({
 const cols = ref([
   {
     name: "序号",
-    field: "index",
+    width: 60,
+    field: configs.TABLE_INDEX_FIELD,
   },
   {
     name: "区域",
@@ -82,7 +84,7 @@ const jump = function (v: any) {
     query: {
       comName: v.company_name,
       company_id: v.company_id,
-      backNeed: 1,
+      back: 1,
     },
   })
 }
@@ -134,8 +136,9 @@ function exported() {
 }
 const detail = function (row: any) {
   router.push({
-    path: "/DataBoard/index/cleanList",
+    path: "/analysisInfo/weekCleanList",
     query: {
+      back: 1,
       company_id: row.company_id,
       type: "1",
     },
@@ -226,7 +229,7 @@ onMounted(init)
       </el-form>
     </template>
     <template #name="{ row }">
-      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["company_name"] }}</span>
+      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["name"] }}</span>
     </template>
 
     <template #action="{ row }">

+ 2 - 2
src/views/analysisInfo/clean.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { ref } from "vue"
-import OverDate from "./_OverDate.vue"
+import Overdue from "./_Overdue.vue"
 import WeekClean from "./_WeekClean.vue"
 const queryType = ref(0)
 </script>
@@ -12,6 +12,6 @@ const queryType = ref(0)
       <el-radio-button label="1">一周待清洗</el-radio-button>
     </el-radio-group>
   </div>
-  <OverDate v-if="queryType == 0"></OverDate>
+  <Overdue v-if="queryType == 0"></Overdue>
   <WeekClean v-if="queryType == 1"></WeekClean>
 </template>

+ 70 - 0
src/views/analysisInfo/overdueCompany.vue

@@ -0,0 +1,70 @@
+<script setup lang="ts">
+import configs from "@/core/config/Index"
+import { ref } from "vue"
+import router from "@/router"
+import { useRoute } from "vue-router"
+const route = useRoute()
+const companyName = ref(route.query.name)
+const queryParams = ref({
+  company_id: route.query.company_id,
+  time_type: route.query.time_type,
+  warn_type: route.query.warn_type,
+  query_start_time: route.query.query_start_time,
+  query_end_time: route.query.query_end_time,
+})
+const cols = ref([
+  {
+    name: "序号",
+    width: 60,
+    field: configs.TABLE_INDEX_FIELD,
+  },
+  {
+    name: "净化设施",
+    field: "device_name",
+  },
+  {
+    name: "告警类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "报警时间",
+    field: "warn_starttime",
+  },
+  {
+    name: "操作",
+    width: 120,
+    field: "action",
+  },
+])
+
+function detail(row: any) {
+  router.push({
+    path: "/analysisInfo/overdue/warn",
+    query: {
+      back: 1,
+      id: row.id,
+      type: row.warn_type,
+    },
+  })
+}
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/overdueCompany/getWarnForPage"
+    method="post"
+    :query-params="queryParams"
+    :has-checkbox="false"
+    :pagination="false"
+  >
+    <template v-slot:table-tool="">
+      <h2>{{ companyName }}</h2>
+    </template>
+
+    <template #action="{ row }">
+      <span class="table-action" @click="detail(row)">查看详情</span>
+    </template>
+  </VbDataTable>
+</template>

+ 190 - 0
src/views/analysisInfo/overdueWarn.vue

@@ -0,0 +1,190 @@
+<script setup lang="ts">
+import { ref, computed, onMounted } from "vue"
+import Rs from "@/core/services/RequestService"
+import { useRoute } from "vue-router"
+import configs from "@/core/config/Index"
+import moment from "moment"
+const route = useRoute()
+const title = ref("")
+const type = ref(0)
+const tableData = ref([])
+const chartData = computed(() => {
+  const data: any = Object.assign({}, tableData.value[0])
+  if (!data) {
+    return {}
+  }
+  const period = type.value == 0 ? data.day_condition : data.hour_condition
+  const overdue =
+    type.value == 0
+      ? data.run_day - data.day_condition > 0
+        ? data.run_day - data.day_condition
+        : 0
+      : data.run_hour - data.hour_condition > 0
+      ? data.run_hour - data.hour_condition
+      : 0
+
+  return {
+    chartData: [
+      {
+        categories: ["逾期时间", "周期时间"],
+        series: [
+          {
+            name: "辅助",
+            type: "bar",
+            stack: "总量",
+            barWidth: 80,
+            barMaxWidth: 80,
+            itemStyle: {
+              barBorderColor: "rgba(0,0,0,0)",
+              color: "rgba(0,0,0,0)",
+            },
+            data: [period, 0],
+          },
+          {
+            name: "时间",
+            type: "bar",
+            stack: "总量",
+            barWidth: 80,
+            barMaxWidth: 80,
+            label: {
+              show: true,
+              position: "right",
+            },
+            data: [
+              {
+                value: overdue,
+                itemStyle: {
+                  color: "rgba(242,99,123,0.75)",
+                },
+              },
+              {
+                value: period,
+                itemStyle: {
+                  color: "rgba(24,144,255,0.75)",
+                },
+              },
+            ],
+          },
+        ],
+        yzTitle: `${type.value == 0 ? "天" : "小时"}`,
+      },
+    ],
+  }
+})
+
+function getTitle() {
+  const options = {
+    warn_id: route.query.id,
+    warn_type: route.query.type,
+  }
+  Rs.post("sys/warn/getWarnTitle", { data: options, successAlert: false }).then((res) => {
+    title.value = res.data
+  })
+}
+const barOption = {
+  grid: { top: 10, right: 40 },
+  legend: { show: false },
+  titleText: " ",
+  direction: "y",
+  tooltip: {
+    formatter: "{b1}: {c1}",
+  },
+}
+function getData() {
+  const options = {
+    warn_id: route.query.id,
+    warn_type: route.query.type,
+  }
+  Rs.post("sys/warn/getCleanWarntableById", { data: options, successAlert: false }).then((res) => {
+    tableData.value = res.data
+  })
+}
+
+const cols = ref([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+  },
+  {
+    name: "净化器名称",
+    field: "monitor_name",
+  },
+  {
+    name: "天数",
+    field: "day_condition",
+  },
+  {
+    name: "开机小时",
+    field: "hour_condition",
+  },
+  {
+    name: "最后清洗时间",
+    field: "last_clean_time",
+  },
+  {
+    name: "经过天数",
+    field: "run_day",
+  },
+  {
+    name: "开机小时数",
+    field: "run_hour",
+  },
+  {
+    name: "剩余天数",
+    field: "surplus_day",
+  },
+  {
+    name: "剩余小时数",
+    field: "surplus_hour",
+  },
+])
+
+onMounted(() => {
+  getTitle()
+  getData()
+})
+</script>
+
+<template>
+  <div class="card mb-8">
+    <div class="card-header">
+      <div class="card-title text-info fw-bolder">{{ title }}</div>
+      <div class="card-toolbar">
+        <el-radio-group v-model="type">
+          <el-radio-button :label="0">按天数</el-radio-button>
+          <el-radio-button :label="1">按小时数</el-radio-button>
+        </el-radio-group>
+      </div>
+    </div>
+    <div class="card-body p-5">
+      <div class="w-100">
+        <BaseChart :h="350" :data="chartData" type="bar" :options="barOption"></BaseChart>
+      </div>
+    </div>
+  </div>
+  <VbDataTable :data="tableData" :header="cols" :has-checkbox="false" :pagination="false">
+    <template #tableHeader>
+      <thead>
+        <tr class="text-center">
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">序号</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化器名称</th>
+          <th class="bg-light-primary" colspan="2">清洗周期</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">最后清洗时间</th>
+          <th class="bg-light-primary" colspan="2">上次清洗至今</th>
+          <th class="bg-light-primary" colspan="2">距下次清洗</th>
+        </tr>
+        <tr class="text-center">
+          <th class="bg-light-primary">天数</th>
+          <th class="bg-light-primary">开机小时</th>
+          <th class="bg-light-primary">经过天数</th>
+          <th class="bg-light-primary">开机小时数</th>
+          <th class="bg-light-primary">剩余天数</th>
+          <th class="bg-light-primary">剩余小时数</th>
+        </tr>
+      </thead>
+    </template>
+    <template #last_clean_time="{ row }">
+      {{ moment(row.last_clean_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+    </template>
+  </VbDataTable>
+</template>

+ 90 - 0
src/views/analysisInfo/weekCleanList.vue

@@ -0,0 +1,90 @@
+<script setup lang="ts">
+import moment from "moment"
+import { ref, computed, onMounted } from "vue"
+import { useRoute } from "vue-router"
+import configs from "@/core/config/Index"
+const route = useRoute()
+const cols = ref([
+  {
+    name: "序号",
+    width: 60,
+    field: configs.TABLE_INDEX_FIELD,
+  },
+  {
+    name: "净化器名称",
+    field: "monitor_name",
+  },
+  {
+    name: "清洁度",
+    field: "clean_name",
+  },
+  {
+    name: "天数",
+    field: "day_condition",
+  },
+  {
+    name: "开机小时",
+    field: "hour_condition",
+  },
+  {
+    name: "最后清洗时间",
+    field: "last_clean_time",
+  },
+  {
+    name: "经过天数",
+    field: "run_day",
+  },
+  {
+    name: "开机小时数",
+    field: "run_hour",
+  },
+  {
+    name: "剩余天数",
+    field: "surplus_day",
+  },
+  {
+    name: "剩余小时数",
+    field: "surplus_hour",
+  },
+])
+const queryParams = ref({
+  company_id: route.query.company_id,
+})
+</script>
+
+<template>
+  <VbDataTable
+    url="sys/purifierCondition/getPurifierList"
+    :header="cols"
+    :query-params="queryParams"
+    method="post"
+    :has-checkbox="false"
+    :pagination="false"
+  >
+    <template #tableHeader>
+      <thead>
+        <tr class="text-center">
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">序号</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化器名称</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">清洁度</th>
+          <th class="bg-light-primary" colspan="2">清洗周期</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">最后清洗时间</th>
+          <th class="bg-light-primary" colspan="2">上次清洗至今</th>
+          <th class="bg-light-primary" colspan="2">距下次清洗</th>
+        </tr>
+        <tr class="text-center">
+          <th class="bg-light-primary">天数</th>
+          <th class="bg-light-primary">开机小时</th>
+          <th class="bg-light-primary">经过天数</th>
+          <th class="bg-light-primary">开机小时数</th>
+          <th class="bg-light-primary">剩余天数</th>
+          <th class="bg-light-primary">剩余小时数</th>
+        </tr>
+      </thead>
+    </template>
+
+    <template #last_clean_time="{ row }">
+      {{ moment(row.last_clean_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+    </template>
+  </VbDataTable>
+</template>

+ 1 - 1
src/views/dashboard/Screen/ChartOver.vue

@@ -82,7 +82,7 @@ const jump = (it: any) => {
   router.push({
     path: "/overAnalysis/overTime/detail",
     query: {
-      needPase: 1,
+      back: 1,
       name_code: it.name,
       time_type: 0,
       query_type: 4, //首页跳转

+ 1 - 1
src/views/dashboard/Screen/Map.vue

@@ -327,7 +327,7 @@ const jumpCompanyDetail = (item: any) => {
     query: {
       comName: item.name,
       company_id: item.company_id,
-      //backNeed: 1,
+      back: 1,
     },
   })
 }

+ 328 - 2
src/views/examApproval/appReview.vue

@@ -1,7 +1,333 @@
 <script setup lang="ts">
-import { reactive, ref, toRefs } from "vue"
+import { ref, computed } from "vue"
+import configs from "@/core/config/Index"
+import router from "@/router"
+import Rs from "@/core/services/RequestService"
+import { ElInput, ElSelect } from "element-plus"
+import type { VbFormItem } from "@/components/Forms/models"
+const table = ref()
+const cols = ref([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+    width: 60,
+  },
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "企业名称",
+    field: "company_name",
+  },
+  {
+    name: "监测类型",
+    field: "monitoring_type_name",
+  },
+  {
+    name: "申报类型",
+    field: "declare_type_name",
+  },
+  {
+    name: "申报状态",
+    field: "audit_state_name",
+  },
+  {
+    name: "申报时间",
+    field: "abnormal_date",
+  },
+  {
+    name: "操作",
+    field: "action",
+    width: 100,
+  },
+])
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const companyName = ref("")
+const orgId = ref<string | null>(null)
+const monitoringType = ref("")
+const auditState = ref("")
+const declareType = ref("")
+const queryParams = ref({})
+
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      comName: v.company_name,
+      company_id: v.company_id,
+      back: 1,
+    },
+  })
+}
+
+function query() {
+  const params = {
+    org_id: orgId.value,
+    company_name: companyName.value,
+    monitoring_type: monitoringType.value,
+    audit_state: auditState.value,
+    declare_type: declareType.value,
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "") {
+      delete params[key]
+    }
+  })
+  queryParams.value = params
+}
+function reset() {
+  companyName.value = ""
+  orgId.value = "0"
+  monitoringType.value = ""
+  auditState.value = ""
+  declareType.value = ""
+  query()
+}
+const modal = ref<any>()
+const operationType = ref<"D" | "A">("D")
+
+const modalTitle = computed(() => {
+  return operationType.value == "D" ? "申报详情" : operationType.value == "A" ? "审核申报" : ""
+})
+const formData = ref({
+  declare_id: "",
+  audit_user: "",
+  audit_desc: "",
+  audit_state: "",
+  declare_type: "",
+})
+const formItems = computed(() => {
+  return (
+    operationType.value == "A"
+      ? [
+          {
+            field: "audit_org_name",
+            label: "审核机构:",
+            component: "innerText",
+            innerText: details.value.audit_org_name,
+          },
+          {
+            field: "audit_state",
+            label: "审核信息",
+            required: true,
+            component: ElSelect,
+            data: [
+              {
+                label: "审核通过",
+                value: "1",
+              },
+              {
+                label: "审核不通过",
+                value: "2",
+              },
+            ],
+            span: 12,
+          },
+          {
+            field: "audit_user",
+            label: "审核人",
+            required: true,
+            component: ElInput,
+            span: 12,
+          },
+          {
+            field: "audit_desc",
+            label: "审核意见",
+            required: true,
+            type: "textarea",
+            component: ElInput,
+          },
+        ]
+      : []
+  ) as Array<VbFormItem>
+})
+const details = ref<any>({})
+const detail = function (row: any) {
+  operationType.value = "D"
+  getDetail(row)
+}
+const audit = function (row: any) {
+  operationType.value = "A"
+
+  getDetail(row)
+}
+const download = () => {
+  if (details.value.annex_data_down_url) {
+    window.open(details.value.annex_data_down_url)
+  }
+}
+function getDetail(row: any) {
+  formData.value.declare_id = row.id
+  formData.value.declare_type = row.declare_type
+  formData.value.audit_state = "2"
+  Rs.get(`sys/orgDeclare/selectDeclare?id=${row.id}`).then((res) => {
+    details.value = Object.assign({}, res.data)
+    formData.value.audit_user = res.data.audit_user
+    formData.value.audit_desc = res.data.audit_desc
+    modal.value.show()
+  })
+}
+function onSave() {
+  if (operationType.value == "D") {
+    return
+  }
+  Rs.post("sys/orgDeclare/updateAbnormalDeclare", { data: formData.value }).then(() => {
+    table.value.search()
+  })
+}
 </script>
 
 <template>
-  <div>appReview</div>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/orgDeclare/selectForMaintainDeclare"
+    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="商户名称">
+          <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"></OrgSelectTree>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="申报类型">
+          <el-select v-model="declareType" :style="dySearchSelectStyle" clearable placeholder="请选择申报类型">
+            <el-option value="0" label="异常申报"></el-option>
+            <el-option value="1" label="维护申报"></el-option>
+            <el-option value="2" label="清洗填报"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="审核状态">
+          <el-select v-model="auditState" :style="dySearchSelectStyle" clearable placeholder="请选择审核状态">
+            <el-option value="0" label="未审核"></el-option>
+            <el-option value="1" label="审核通过"></el-option>
+            <el-option value="2" label="未审核通过"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="监测类型">
+          <DySelect
+            v-model="monitoringType"
+            :url="'sys/dict/getCompanyMonitoringType'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择监测类型"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-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 #audit_state_name="{ row }">
+      <span
+        class="badge"
+        :class="`badge-${row.audit_state == 0 ? 'primary' : row.audit_state == 1 ? 'success' : 'danger'}`"
+      >
+        {{ row["audit_state_name"] }}
+      </span>
+    </template>
+    <template #action="{ row }">
+      <span v-if="row.audit_state == 1" class="table-action" @click="detail(row)">详情</span>
+      <span v-else class="table-action" @click="audit(row)">审核</span>
+    </template>
+  </VbDataTable>
+  <VbModal v-model:modal="modal" :title="modalTitle" :form-data="formData" :form-items="formItems" @confirm="onSave">
+    <template #body>
+      <el-row v-if="details.declare_type">
+        <el-col :span="12">
+          <dl>
+            <dt>异常信息:</dt>
+            <dd>{{ details.maintain_type }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>时间:</dt>
+            <dd>{{ details.time }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>异常内容:</dt>
+            <dd>{{ details.content }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <el-row v-else>
+        <el-col :span="12">
+          <dl>
+            <dt>设备名称:</dt>
+            <dd>{{ details.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>维护类型:</dt>
+            <dd>{{ details.maintain_type }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>维护周期:</dt>
+            <dd>{{ details.time }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <div class="separator mt-2 mb-5"></div>
+
+      <el-row>
+        <el-col :span="12">
+          <dl>
+            <dt>企业名称:</dt>
+            <dd>{{ details.company_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>联系方式:</dt>
+            <dd>{{ details.user_phone }}</dd>
+          </dl>
+        </el-col>
+        <!-- <el-col :span="12">
+          <dl>
+            <dt>企业申报信息:</dt>
+            <dd>{{ details.content }}</dd>
+          </dl>
+        </el-col> -->
+        <el-col :span="12">
+          <dl>
+            <dt>情况描述:</dt>
+            <dd>{{ details.content }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>附件:</dt>
+            <dd>
+              <a v-if="details.annex_data_down_url" @click="download">点击下载</a>
+              <span v-else>暂无</span>
+            </dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>申报人:</dt>
+            <dd>{{ details.declare_user }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <div v-if="formItems.length" class="separator mt-2 mb-5"></div>
+    </template>
+  </VbModal>
 </template>

+ 320 - 2
src/views/examApproval/reptNotice.vue

@@ -1,7 +1,325 @@
 <script setup lang="ts">
-import { defineProps, reactive, ref, toRefs } from "vue"
+import { ref } from "vue"
+import configs from "@/core/config/Index"
+import router from "@/router"
+import Rs from "@/core/services/RequestService"
+import moment from "moment"
+const table = ref()
+const cols = ref([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+    width: 60,
+  },
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "企业名称",
+    field: "company_name",
+  },
+  {
+    name: "监测类型",
+    field: "monitoring_type_name",
+  },
+  {
+    name: "通知类型",
+    field: "notice_type_name",
+  },
+  {
+    name: "通知时间",
+    field: "notice_time",
+  },
+  {
+    name: "状态",
+    field: "state",
+  },
+  {
+    name: "操作",
+    field: "action",
+    width: 160,
+  },
+])
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "160px" }
+const companyName = ref("")
+const orgId = ref<string | null>(null)
+const monitoringType = ref("")
+const state = ref("")
+const type = ref("")
+const currentDate = ref<Date>(new Date())
+const queryParams = ref({
+  date: moment(currentDate.value).format("YYYYMMDD"),
+})
+
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      comName: v.company_name,
+      company_id: v.company_id,
+      back: 1,
+    },
+  })
+}
+
+function query() {
+  const params = {
+    org_id: orgId.value,
+    company_name: companyName.value,
+    monitoring_type: monitoringType.value,
+    state: state.value,
+    type: type.value,
+    date: moment(currentDate.value).format("YYYYMMDD"),
+  }
+
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "") {
+      delete params[key]
+    }
+  })
+  queryParams.value = params
+}
+function reset() {
+  companyName.value = ""
+  orgId.value = "0"
+  monitoringType.value = ""
+  state.value = ""
+  type.value = ""
+  currentDate.value = new Date()
+  query()
+}
+const modal = ref<any>()
+const operationType = ref<"R" | "N">("R")
+
+const modalTitle = ref("")
+
+const details = ref<any>({})
+const reply = function (row: any) {
+  operationType.value = "R"
+  modalTitle.value = "告警通知"
+  getDetail(row)
+}
+const notity = function (row: any) {
+  operationType.value = "N"
+  if (row.type == 0) {
+    modalTitle.value = "告警通知"
+  } else if (row.type == 1) {
+    modalTitle.value = "清洗通知"
+  }
+  getDetail(row)
+}
+
+function getDetail(row: any) {
+  Rs.post(`sys/notice/getNoticeDetail`, { params: row.id }).then((res) => {
+    details.value = Object.assign({}, res.data)
+    details.value.clear_date = moment(res.data.clear_date, "YYYYMMDD").format("YYYY-MM-DD")
+    modal.value.show()
+  })
+}
 </script>
 
 <template>
-  <div>reptNotice</div>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/notice/selectForPage"
+    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="商户名称">
+          <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"></OrgSelectTree>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="通知类型">
+          <el-select v-model="type" :style="dySearchSelectStyle" clearable placeholder="请选择通知类型">
+            <el-option value="0" label="告警通知"></el-option>
+            <el-option value="1" label="清洗通知"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="状态">
+          <el-select v-model="state" :style="dySearchSelectStyle" clearable placeholder="请选择状态">
+            <el-option value="0" label="未回复"></el-option>
+            <el-option value="1" label="已回复"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="时间">
+          <el-date-picker
+            v-model="currentDate"
+            type="date"
+            placeholder="请选择时间"
+            :size="size"
+            :style="dySearchSelectStyle"
+            :clearable="false"
+          />
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="监测类型">
+          <DySelect
+            v-model="monitoringType"
+            :url="'sys/dict/getCompanyMonitoringType'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择监测类型"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-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 #state="{ row }">
+      <span class="badge" :class="`badge-${row.state == 0 ? 'primary' : row.state == 1 ? 'success' : 'danger'}`">
+        {{ row["state"] == 0 ? "未回复" : "已回复" }}
+      </span>
+    </template>
+    <template #notice_time="{ row }">
+      {{ moment(row["notice_time"], "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") }}
+    </template>
+    <template #action="{ row }">
+      <span v-if="row.state == 1" class="table-action" @click="reply(row)">查看回复</span>
+      <span class="table-action" @click="notity(row)">查看通知</span>
+    </template>
+  </VbDataTable>
+  <VbModal v-model:modal="modal" :title="modalTitle">
+    <template #body>
+      <el-row v-if="operationType == 'R' && details.type == 1">
+        <el-col :span="12">
+          <dl>
+            <dt>是否清洗:</dt>
+            <dd>{{ details.is_clear ? "已清洗" : "未清洗" }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>清洗时间:</dt>
+            <dd>{{ details.clear_date }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>备注:</dt>
+            <dd>{{ details.remark }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <el-row v-if="operationType == 'R' && details.type == 0">
+        <el-col :span="12">
+          <dl>
+            <dt>设备名称:</dt>
+            <dd>{{ details.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>异常类型:</dt>
+            <dd>{{ details.warn_type_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>告警原因:</dt>
+            <dd>{{ details.content }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>异常原因:</dt>
+            <dd>{{ details.remark }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+
+      <el-row v-if="operationType == 'N' && details.type == 1">
+        <el-col :span="12">
+          <dl>
+            <dt>企业名称:</dt>
+            <dd>{{ details.company_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>清洗周期(天):</dt>
+            <dd>{{ details.clean_cycle_day }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>设施名称:</dt>
+            <dd>{{ details.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>清洗周期(小时):</dt>
+            <dd>{{ details.clean_cycle_hour }}</dd>
+          </dl>
+        </el-col>
+
+        <el-col :span="12">
+          <dl>
+            <dt>是否完成清洗:</dt>
+            <dd>{{ details.is_clear ? "已清洗" : "未清洗" }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <el-row v-if="operationType == 'N' && details.type == 0">
+        <el-col :span="12">
+          <dl>
+            <dt>企业名称:</dt>
+            <dd>{{ details.company_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>异常类型:</dt>
+            <dd>{{ details.warn_type_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>告警设备:</dt>
+            <dd>{{ details.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>告警时间:</dt>
+            <dd>{{ details.warn_time }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>通知内容:</dt>
+            <dd>{{ details.content }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <div class="separator mt-2 mb-5"></div>
+      <el-row v-if="operationType == 'N'">
+        <el-col :span="12">
+          <dl>
+            <dt>部门:</dt>
+            <dd>{{ details.org_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>通知人:</dt>
+            <dd>{{ details.notifier }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+    </template>
+  </VbModal>
 </template>

+ 10 - 8
src/views/goLineData/_ViewDevice_General.vue

@@ -1,12 +1,11 @@
 <script setup lang="ts">
 import { ref, withDefaults, onMounted, watch } from "vue"
-import { getAssetPath } from "@/core/helpers/assets"
 import Rs from "@/core/services/RequestService"
+import configs from "@/core/config/Index"
+import moment from "moment"
 
 const props = withDefaults(defineProps<{ id: string }>(), { id: "" })
-const emptyTableText = "未查询到数据"
 const deviceInfo = ref<any>({})
-const cleanInfo = ref<Array<any>>([])
 function getDeviceInfo() {
   Rs.post("sys/onlineData/getMonitorData", { data: { id: props.id } }).then((res) => {
     if (res.data) {
@@ -20,10 +19,10 @@ function getDeviceInfo() {
   })
 }
 const cols = ref([
-  // {
-  //   name: "序号",
-  //   field: "index",
-  // },
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+  },
   {
     name: "净化器名称",
     field: "monitor_name",
@@ -107,7 +106,7 @@ onMounted(() => {
       <template #tableHeader>
         <thead>
           <tr class="text-center">
-            <!-- <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">序号</th> -->
+            <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">序号</th>
             <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化器名称</th>
             <th class="bg-light-primary" colspan="2">清洗周期</th>
             <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">最后清洗时间</th>
@@ -124,6 +123,9 @@ onMounted(() => {
           </tr>
         </thead>
       </template>
+      <template #last_clean_time="{ row }">
+        {{ moment(row.last_clean_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+      </template>
     </VbDataTable>
   </div>
 </template>

+ 1 - 1
src/views/goLineData/timeData.vue

@@ -176,7 +176,7 @@ const jump = function (v: any) {
     query: {
       comName: v.company_name,
       company_id: v.company_id,
-      backNeed: 1,
+      back: 1,
     },
   })
 }

+ 18 - 0
src/views/mobile/inspector.vue

@@ -0,0 +1,18 @@
+<script setup lang="ts">
+import { ref } from "vue"
+import Processing from "@/views/_component/_WarnProcessing.vue"
+import Processed from "@/views/_component/_WarnProcessed.vue"
+
+const active = ref(0)
+</script>
+
+<template>
+  <el-tabs v-model="active" class="demo-tabs">
+    <el-tab-pane label="处理中" :name="0">
+      <Processing v-if="active == 0" :type="1"></Processing>
+    </el-tab-pane>
+    <el-tab-pane label="已完结" :name="1">
+      <Processed v-if="active == 1" :type="1"></Processed>
+    </el-tab-pane>
+  </el-tabs>
+</template>

+ 17 - 2
src/views/mobile/supervision.vue

@@ -1,7 +1,22 @@
 <script setup lang="ts">
-import { defineProps, reactive, ref, toRefs } from "vue"
+import { ref } from "vue"
+import NoProcess from "@/views/_component/_WarnNoProcess.vue"
+import Processing from "@/views/_component/_WarnProcessing.vue"
+import Processed from "@/views/_component/_WarnProcessed.vue"
+
+const active = ref(0)
 </script>
 
 <template>
-  <div>supervision</div>
+  <el-tabs v-model="active" class="demo-tabs">
+    <el-tab-pane label="未处理" :name="0">
+      <NoProcess v-if="active == 0"></NoProcess>
+    </el-tab-pane>
+    <el-tab-pane label="处理中" :name="1">
+      <Processing v-if="active == 1"></Processing>
+    </el-tab-pane>
+    <el-tab-pane label="已完结" :name="2">
+      <Processed v-if="active == 2"></Processed>
+    </el-tab-pane>
+  </el-tabs>
 </template>

+ 1 - 1
src/views/overAnalysis/oveParamDetail.vue

@@ -58,9 +58,9 @@ const jump = function (v: any) {
   router.push({
     path: "/goLineData/oilFumeConcentration",
     query: {
+      back: 1,
       comName: v.company_name,
       company_id: v.company_id,
-      backNeed: 1,
     },
   })
 }

+ 2 - 1
src/views/overAnalysis/overMerchants.vue

@@ -75,7 +75,7 @@ const jump = function (v: any) {
     query: {
       comName: v.company_name,
       company_id: v.company_id,
-      backNeed: 1,
+      back: 1,
     },
   })
 }
@@ -131,6 +131,7 @@ const detail = function (row: any) {
   router.push({
     path: "/overAnalysis/overTime/over_company",
     query: {
+      back: 1,
       type: 0,
       time_type: timeType.value,
       company_id: row.company_id || "",

+ 1 - 1
src/views/overAnalysis/overTime.vue

@@ -102,7 +102,7 @@ const detail = function (v: any) {
   router.push({
     path: "/overAnalysis/overTime/detail",
     query: {
-      needPase: 1,
+      back: 1,
       query_type: queryType.value,
       name_code: v.name_code,
       time_type: timeType.value,

+ 2 - 1
src/views/overAnalysis/overTimeDetail.vue

@@ -92,7 +92,7 @@ const jump = function (v: any) {
     query: {
       comName: v.company_name,
       company_id: v.company_id,
-      backNeed: 1,
+      back: 1,
     },
   })
 }
@@ -187,6 +187,7 @@ function detail(row: any) {
   router.push({
     path: "/overAnalysis/overTime/detail_company",
     query: {
+      back: 1,
       company_id: row.company_id,
       name: row.name,
       type: "0",

+ 2 - 1
src/views/overAnalysis/overTimeDetail_Company.vue

@@ -70,7 +70,7 @@ const jump = function (v: any) {
     query: {
       comName: v.company_name,
       company_id: v.company_id,
-      backNeed: 1,
+      back: 1,
     },
   })
 }
@@ -156,6 +156,7 @@ function detail(row: any) {
   router.push({
     path: path,
     query: {
+      back: 1,
       id: row.id,
       type: row.warn_type,
       needback: 1,

+ 107 - 0
src/views/purify/qxyw.vue

@@ -0,0 +1,107 @@
+<script setup lang="ts">
+import { ref, computed, onMounted } from "vue"
+import router from "@/router"
+const cols = ref([
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "商户",
+    field: "name",
+  },
+  {
+    name: "净化器数量",
+    field: "purifier_num",
+  },
+  {
+    name: "清洗次数",
+    field: "clean_num",
+  },
+  {
+    name: "操作",
+    field: "action",
+    width: 120,
+  },
+])
+const size = ref<any>("default")
+const companyName = ref("")
+const orgId = ref<string | null>(null)
+const queryParams = ref<any>({
+  cleaned: 0, //0,普通查询1.一周待清洗
+  name: "",
+  org_id: 0,
+})
+
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      comName: v.company_name,
+      company_id: v.company_id,
+      back: 1,
+    },
+  })
+}
+
+function query(type: number) {
+  const params = {
+    org_id: orgId.value,
+    name: companyName.value,
+    cleaned: type, //0,普通查询1.一周待清洗
+  }
+
+  queryParams.value = params
+}
+function reset() {
+  companyName.value = ""
+  orgId.value = "0"
+  query(0)
+}
+const detail = function (row: any) {
+  router.push({
+    path: "/qxyw/details",
+    query: {
+      back: 1,
+      company_id: row.company_id,
+      company_name: row.name,
+    },
+  })
+}
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/operationMerchant/getoperationMerchant"
+    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="商户名称">
+          <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"></OrgSelectTree>
+        </el-form-item>
+
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="query(0)">查询</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-info" @click="query(1)">一周待清洗</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-primary btn-outline" @click="reset">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </template>
+    <template #name="{ row }">
+      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["name"] }}</span>
+    </template>
+
+    <template #action="{ row }">
+      <span class="table-action" @click="detail(row)">查看详情</span>
+    </template>
+  </VbDataTable>
+</template>

+ 261 - 0
src/views/purify/qxyw_detail.vue

@@ -0,0 +1,261 @@
+<script setup lang="ts">
+import configs from "@/core/config/Index"
+import { ElDatePicker } from "element-plus"
+import moment from "moment"
+import { ref } from "vue"
+import VbUpload from "@/components/Upload/VbUpload.vue"
+import { useRoute } from "vue-router"
+import type { VbFormItem } from "@/components/Forms/models"
+import RequestService from "@/core/services/RequestService"
+const route = useRoute()
+const table = ref()
+const cols = ref([
+  {
+    name: "序号",
+    width: 60,
+    field: configs.TABLE_INDEX_FIELD,
+  },
+  {
+    name: "净化器名称",
+    field: "monitor_name",
+  },
+  {
+    name: "天数",
+    field: "day_condition",
+  },
+  {
+    name: "开机小时",
+    field: "hour_condition",
+  },
+  {
+    name: "最后清洗时间",
+    field: "last_clean_time",
+  },
+  {
+    name: "经过天数",
+    field: "run_day",
+  },
+  {
+    name: "开机小时数",
+    field: "run_hour",
+  },
+  {
+    name: "剩余天数",
+    field: "surplus_day",
+  },
+  {
+    name: "剩余小时数",
+    field: "surplus_hour",
+  },
+  {
+    name: "操作",
+    field: "action",
+  },
+])
+const queryParams = ref({
+  company_id: route.query.company_id as string,
+})
+const modal = ref()
+const modalTable = ref()
+const dateRange = ref<any>("")
+const col2s = ref<Array<any>>([
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "清洗时间",
+    field: "clean_start",
+  },
+  {
+    name: "清洗照片",
+    field: "pictures",
+  },
+])
+const queryParam2s = ref<any>({
+  device_id: "0",
+})
+const deviceId = ref("")
+function cleanList(row: any) {
+  deviceId.value = row.device_id
+  query()
+  modal.value.show()
+}
+function query() {
+  const params: any = {
+    device_id: deviceId.value + "",
+  }
+  if (dateRange.value[0]) {
+    params.clean_start = moment(dateRange.value[0]).format("YYYYMMDD")
+    params.clean_end = moment(dateRange.value[1]).format("YYYYMMDD")
+  }
+  //[moment(new Date()).add(-1, "M").toDate(), new Date()]
+  queryParam2s.value = params
+}
+function reset() {
+  dateRange.value = ""
+  query()
+}
+
+const uploadEl = ref()
+function clean(row: any) {
+  uploadEl.value?.clearFiles()
+  formData.value = Object.assign({}, row)
+  formData.value.picture_name = ""
+  formData.value.clean_end = new Date()
+
+  modal2.value.show()
+}
+const validatePic = (rule: any, value: any, callback: any) => {
+  if (!value) {
+    callback(new Error("请选择附件"))
+  } else {
+    callback()
+  }
+}
+const modal2 = ref()
+const formData = ref<any>({})
+const formItems = ref<Array<VbFormItem>>([
+  {
+    label: "设施名称:",
+    field: "monitor_name",
+    component: "innerText",
+  },
+  {
+    label: "清洗时间:",
+    field: "clean_end",
+    placeholder: "请选择清洗时间",
+    class: "w-100",
+    required: true,
+    component: ElDatePicker,
+    props: {
+      type: "date",
+      disabledDate: (v: any) => {
+        return (
+          (v && v > moment().endOf("day")) ||
+          v < moment(formData.value.last_clean_time, "YYYYMMDD").add(1, "days").toDate()
+        )
+      },
+    },
+  },
+  {
+    label: "清洗图片:",
+    field: "picture_name",
+    component: VbUpload,
+    rules: [{ validator: validatePic, trigger: "change" }],
+  },
+])
+function onSave() {
+  const data = {
+    company_id: route.query.company_id,
+    device_id: formData.value.device_id,
+    clean_end: moment(formData.value.clean_end).format("YYYYMMDD"),
+    picture_name: formData.value.picture_name,
+  }
+  RequestService.post("sys/operationMerchant/newCleanLog", { data }).then(() => {
+    table.value.search()
+  })
+}
+</script>
+
+<template>
+  <h2 class="text-primary mb-3">{{ route.query.company_name }}</h2>
+  <VbDataTable
+    ref="table"
+    url="sys/operationMerchant/getPurifierList"
+    :header="cols"
+    :query-params="queryParams"
+    method="post"
+    :has-checkbox="false"
+    :pagination="true"
+  >
+    <template #tableHeader>
+      <thead>
+        <tr class="text-center">
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">序号</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化器名称</th>
+          <th class="bg-light-primary" colspan="2">清洗周期</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">最后清洗时间</th>
+          <th class="bg-light-primary" colspan="2">上次清洗至今</th>
+          <th class="bg-light-primary" colspan="2">距下次清洗</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">清洗记录</th>
+        </tr>
+        <tr class="text-center">
+          <th class="bg-light-primary">天数</th>
+          <th class="bg-light-primary">开机小时</th>
+          <th class="bg-light-primary">经过天数</th>
+          <th class="bg-light-primary">开机小时数</th>
+          <th class="bg-light-primary">剩余天数</th>
+          <th class="bg-light-primary">剩余小时数</th>
+        </tr>
+      </thead>
+    </template>
+
+    <template #last_clean_time="{ row }">
+      {{ moment(row.last_clean_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+    </template>
+    <template #action="{ row }">
+      <span class="table-action" @click="cleanList(row)">清洗记录</span>
+      <span class="table-action" @click="clean(row)">填报记录</span>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    title="清洗记录"
+    close-btn-class="btn btn-primary"
+    :confirm-btn="false"
+    modal-dialog-style="max-width: 1100px;width: 1100px;"
+  >
+    <template #body>
+      <VbDataTable
+        ref="modalTable"
+        :header="col2s"
+        url="sys/operationMerchant/selectCleanLog"
+        method="post"
+        :auto-search="false"
+        :query-params="queryParam2s"
+        :has-checkbox="false"
+      >
+        <template v-slot:table-tool="">
+          <el-form class="align-items-center" :inline="true">
+            <el-form-item class="mb-0 me-0 align-items-center" label="日期">
+              <el-date-picker
+                v-model="dateRange"
+                type="daterange"
+                range-separator="~"
+                start-placeholder="开始时间"
+                end-placeholder="结束时间"
+                size="default"
+              />
+            </el-form-item>
+            <el-form-item class="mb-0 me-0 align-items-center">
+              <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-form-item>
+          </el-form>
+        </template>
+        <template #pictures="{ row }">
+          <div class="d-flex h-100 justify-content-center align-items-center p-3">
+            <el-image
+              v-for="(pic, i) in row.pictures?.split(',')"
+              :key="i"
+              :src="`/api/file/upload/res/${pic}`"
+              :preview-src-list="row.pictures.split(',').map((v:any)=>{
+                return `/api/file/upload/res/${v}`
+              })"
+              :hide-on-click-modal="true"
+              fit="cover"
+              style="height: 50px; width: 75px; max-width: none; border-radius: 0.475rem; margin-left: 5px"
+              alt=""
+            />
+          </div>
+        </template>
+      </VbDataTable>
+    </template>
+  </VbModal>
+  <VbModal v-model:modal="modal2" title="清洗填报" :form-data="formData" :form-items="formItems" @confirm="onSave">
+    <template #picture_name_form>
+      <VbUpload ref="uploadEl" v-model="formData.picture_name"></VbUpload>
+    </template>
+  </VbModal>
+</template>

+ 220 - 2
src/views/purify/washInfo.vue

@@ -1,7 +1,225 @@
 <script setup lang="ts">
-import { defineProps, reactive, ref, toRefs } from "vue"
+import { ref, onMounted } from "vue"
+import Rs from "@/core/services/RequestService"
+import { useRouter } from "vue-router"
+import configs from "@/core/config/Index"
+const router = useRouter()
+const ringOptions = {
+  legend: { top: "center", bottom: "auto", right: "20%", orient: "vertical" },
+  pieCenter: ["40%", "50%"],
+  titleText: "",
+}
+const titles = ref<Array<string>>(["", ""])
+const chartDatas = ref<Array<any>>([{}, {}])
+function queryChart(_type: number) {
+  let url = ""
+  const params = {
+    org_id: orgId.value,
+    name: companyName.value,
+    type: type.value,
+    catering_scale: cateringScale.value,
+    catering_style: cateringStyle.value,
+  }
+  switch (_type) {
+    case 0:
+      url = "sys/purifierCondition/getPie"
+
+      break
+    case 1:
+      url = "sys/purifierCondition/getStatePie"
+      break
+  }
+  Rs.post(url, { data: params, successAlert: false }).then((res) => {
+    chartDatas.value[_type] = res.data
+    titles.value[_type] = res.data.chartData.title
+    chartDatas.value[_type].chartData.title = ""
+  })
+}
+
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "120px" }
+const companyName = ref("")
+const orgId = ref<string | null>(null)
+const type = ref("")
+const cateringScale = ref("")
+const cateringStyle = ref("")
+const queryParams = ref<any>({})
+const cols = ref([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+  },
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "商户名称",
+    field: "name",
+  },
+  {
+    name: "菜系",
+    field: "catering_style_name",
+  },
+  {
+    name: "规模",
+    field: "catering_scale_name",
+  },
+  {
+    name: "净化器数量",
+    field: "purifier_num",
+  },
+  {
+    name: "清洗次数",
+    field: "clean_num",
+  },
+  {
+    name: "操作",
+    width: 150,
+    field: "action",
+  },
+])
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      comName: v.company_name,
+      company_id: v.company_id,
+      back: 1,
+    },
+  })
+}
+
+function query() {
+  const params = {
+    org_id: orgId.value,
+    name: companyName.value,
+    type: type.value,
+    catering_scale: cateringScale.value,
+    catering_style: cateringStyle.value,
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "" && key != "clean_flag" && key != "exceed_company_flag") {
+      delete params[key]
+    }
+  })
+  queryParams.value = params
+}
+function reset() {
+  companyName.value = ""
+  orgId.value = "0"
+  type.value = ""
+  cateringScale.value = ""
+  cateringStyle.value = ""
+  query()
+}
+const detail = function (row: any) {
+  router.push({
+    path: "/purify/washInfo/details",
+    query: {
+      back: 1,
+      company_id: row.company_id,
+    },
+  })
+}
+
+function init() {
+  queryChart(0)
+  queryChart(1)
+}
+onMounted(init)
 </script>
 
 <template>
-  <div>wash</div>
+  <el-row :gutter="0" class="mb-8">
+    <el-col :span="12">
+      <div
+        class="card card-bordered border-primary h-325px"
+        style="border-top-right-radius: 0; border-bottom-right-radius: 0"
+      >
+        <div class="card-header bg-light-primary min-h-50px">
+          <div class="card-title">
+            <h3 class="text-primary">{{ titles[0] }}</h3>
+          </div>
+        </div>
+        <div class="card-body p-7 h-100">
+          <BaseChart :data="chartDatas[0]" type="pie" h="100%" :options="ringOptions"></BaseChart>
+        </div>
+      </div>
+    </el-col>
+    <el-col :span="12">
+      <div
+        class="card card-bordered border-primary h-325px"
+        style="border-left: 0; border-top-left-radius: 0; border-bottom-left-radius: 0"
+      >
+        <div class="card-header bg-light-primary min-h-50px">
+          <div class="card-title">
+            <h3 class="text-primary">{{ titles[1] }}</h3>
+          </div>
+        </div>
+        <div class="card-body p-7 h-100">
+          <BaseChart :data="chartDatas[1]" type="pie" h="100%" :options="ringOptions"></BaseChart>
+        </div>
+      </div>
+    </el-col>
+  </el-row>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/purifierCondition/getComCleanInfo"
+    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="商户名称">
+          <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"></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>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-form-item>
+      </el-form>
+    </template>
+    <template #name="{ row }">
+      <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["name"] }}</span>
+    </template>
+
+    <template #action="{ row }">
+      <span class="table-action" @click="detail(row)">查看详情</span>
+    </template>
+  </VbDataTable>
 </template>

+ 183 - 0
src/views/purify/washInfo_company.vue

@@ -0,0 +1,183 @@
+<script setup lang="ts">
+import configs from "@/core/config/Index"
+import moment from "moment"
+import { ref } from "vue"
+
+const cols = ref([
+  {
+    name: "序号",
+    width: 60,
+    field: configs.TABLE_INDEX_FIELD,
+  },
+  {
+    name: "净化器名称",
+    field: "monitor_name",
+  },
+  {
+    name: "天数",
+    field: "day_condition",
+  },
+  {
+    name: "开机小时",
+    field: "hour_condition",
+  },
+  {
+    name: "最后清洗时间",
+    field: "last_clean_time",
+  },
+  {
+    name: "经过天数",
+    field: "run_day",
+  },
+  {
+    name: "开机小时数",
+    field: "run_hour",
+  },
+  {
+    name: "剩余天数",
+    field: "surplus_day",
+  },
+  {
+    name: "剩余小时数",
+    field: "surplus_hour",
+  },
+  {
+    name: "清洗记录",
+    field: "action",
+  },
+])
+const queryParams = ref({})
+const modal = ref()
+const modalTable = ref()
+const dateRange = ref<any>("")
+const col2s = ref<Array<any>>([
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "清洗时间",
+    field: "clean_start",
+  },
+  {
+    name: "清洗照片",
+    field: "pictures",
+  },
+])
+const queryParam2s = ref<any>({
+  device_id: "0",
+})
+const deviceId = ref("")
+function cleanList(row: any) {
+  deviceId.value = row.device_id
+  query()
+  modal.value.show()
+}
+function query() {
+  const params: any = {
+    device_id: deviceId.value + "",
+  }
+  if (dateRange.value[0]) {
+    params.clean_start = moment(dateRange.value[0]).format("YYYYMMDD")
+    params.clean_end = moment(dateRange.value[1]).format("YYYYMMDD")
+  }
+  //[moment(new Date()).add(-1, "M").toDate(), new Date()]
+  queryParam2s.value = params
+}
+function reset() {
+  dateRange.value = ""
+  query()
+}
+</script>
+
+<template>
+  <VbDataTable
+    url="sys/purifierCondition/getCompanyCleanList"
+    :header="cols"
+    :query-params="queryParams"
+    method="post"
+    :has-checkbox="false"
+    :pagination="false"
+  >
+    <template #tableHeader>
+      <thead>
+        <tr class="text-center">
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">序号</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化器名称</th>
+          <th class="bg-light-primary" colspan="2">清洗周期</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">最后清洗时间</th>
+          <th class="bg-light-primary" colspan="2">上次清洗至今</th>
+          <th class="bg-light-primary" colspan="2">距下次清洗</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">清洗记录</th>
+        </tr>
+        <tr class="text-center">
+          <th class="bg-light-primary">天数</th>
+          <th class="bg-light-primary">开机小时</th>
+          <th class="bg-light-primary">经过天数</th>
+          <th class="bg-light-primary">开机小时数</th>
+          <th class="bg-light-primary">剩余天数</th>
+          <th class="bg-light-primary">剩余小时数</th>
+        </tr>
+      </thead>
+    </template>
+
+    <template #last_clean_time="{ row }">
+      {{ moment(row.last_clean_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+    </template>
+    <template #action="{ row }">
+      <div class="text-danger text-center">
+        <span class="table-action" @click="cleanList(row)">清洗记录</span>
+      </div>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    title="清洗记录"
+    close-btn-class="btn btn-primary"
+    :confirm-btn="false"
+    modal-dialog-style="max-width: 1100px;width: 1100px;"
+  >
+    <template #body>
+      <VbDataTable
+        ref="modalTable"
+        :header="col2s"
+        url="sys/purifierCondition/getEnterpriseCleanLog"
+        method="post"
+        :auto-search="false"
+        :query-params="queryParam2s"
+        :has-checkbox="false"
+      >
+        <template v-slot:table-tool="">
+          <el-form class="align-items-center" :inline="true">
+            <el-form-item class="mb-0 me-0 align-items-center" label="日期">
+              <el-date-picker
+                v-model="dateRange"
+                type="daterange"
+                range-separator="~"
+                start-placeholder="开始时间"
+                end-placeholder="结束时间"
+                size="default"
+              />
+            </el-form-item>
+            <el-form-item class="mb-0 me-0 align-items-center">
+              <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-form-item>
+          </el-form>
+        </template>
+        <template #pictures="{ row }">
+          <div class="d-flex h-100 justify-content-center align-items-center p-3">
+            <el-image
+              :src="`/api/file/upload/res/${row.pictures}`"
+              :preview-src-list="[`/api/file/upload/res/${row.pictures}`]"
+              :hide-on-click-modal="true"
+              fit="cover"
+              style="height: 50px; width: 75px; max-width: none; border-radius: 0.475rem"
+              alt=""
+            />
+          </div>
+        </template>
+      </VbDataTable>
+    </template>
+  </VbModal>
+</template>

+ 265 - 0
src/views/purify/washInfo_details.vue

@@ -0,0 +1,265 @@
+<script setup lang="ts">
+import { ref } from "vue"
+import configs from "@/core/config/Index"
+import moment from "moment"
+import Rs from "@/core/services/RequestService"
+import { ElInput } from "element-plus"
+import { useRoute } from "vue-router"
+const route = useRoute()
+const active = ref(0)
+const cols = ref<Array<any>>([
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "清洗时间",
+    field: "clean_start",
+  },
+  {
+    name: "清洗照片",
+    field: "pictures",
+  },
+])
+const dateRange = ref<[Date, Date]>([moment(new Date()).add(-1, "M").toDate(), new Date()])
+const queryParams = ref<any>({
+  company_id: route.query.company_id,
+})
+
+function query() {
+  queryParams.value = {
+    company_id: route.query.company_id,
+    clean_end: moment(dateRange.value[0]).format("YYYYMMDD"),
+    clean_start: moment(dateRange.value[1]).format("YYYYMMDD"),
+  }
+}
+
+function reset() {
+  dateRange.value = [moment(new Date()).add(-1, "M").toDate(), new Date()]
+  query()
+}
+
+const col2s = ref([
+  {
+    name: "序号",
+    width: 60,
+    field: configs.TABLE_INDEX_FIELD,
+  },
+  {
+    name: "净化器名称",
+    field: "monitor_name",
+  },
+  {
+    name: "净化效能",
+    field: "clean_name",
+  },
+  {
+    name: "天数",
+    field: "day_condition",
+  },
+  {
+    name: "开机小时",
+    field: "hour_condition",
+  },
+  {
+    name: "最后清洗时间",
+    field: "last_clean_time",
+  },
+  {
+    name: "经过天数",
+    field: "run_day",
+  },
+  {
+    name: "开机小时数",
+    field: "run_hour",
+  },
+  {
+    name: "剩余天数",
+    field: "surplus_day",
+  },
+  {
+    name: "剩余小时数",
+    field: "surplus_hour",
+  },
+  {
+    name: "操作",
+    field: "action",
+  },
+])
+const queryParam2s = ref({
+  company_id: route.query.company_id,
+})
+const colorMapping = (d: string) => {
+  let ret = ""
+  if (d == "清洁") {
+    ret = "#4DCB73"
+  } else if (d == "较脏") {
+    ret = "#FAD337"
+  } else if (d == "过脏") {
+    ret = "#F2637B"
+  }
+  return ret
+}
+const modal = ref()
+const formData = ref<any>({
+  notifier: "",
+})
+const noticeDetails = ref<any>({})
+function notice(row: any) {
+  modal.value.show()
+  noticeDetails.value = Object.assign({}, row)
+}
+function onSaveNotice() {
+  const params = {
+    clean_cycle_day: noticeDetails.value.day_condition,
+    clean_cycle_hour: noticeDetails.value.hour_condition,
+    content: "您有新的清洗通知",
+    name: "000100004",
+    notifier: formData.value.notifier,
+    type: 1,
+    company_id: route.query.company_id,
+    device_id: noticeDetails.value.device_id,
+  }
+  Rs.post("sys/notice/sendNotice", { data: params })
+}
+</script>
+
+<template>
+  <div class="mb-3">
+    <el-radio-group v-model="active" size="large">
+      <el-radio-button label="0">清洗记录</el-radio-button>
+      <el-radio-button label="1">清洗周期</el-radio-button>
+    </el-radio-group>
+  </div>
+  <VbDataTable
+    v-if="active == 0"
+    :header="cols"
+    url="sys/purifierCondition/selectCompanyCleanLog"
+    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-0 align-items-center" label="日期">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="~"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            size="default"
+          />
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-form-item>
+      </el-form>
+    </template>
+    <template #pictures="{ row }">
+      <div class="d-flex h-100 justify-content-center align-items-center p-3">
+        <el-image
+          v-for="(pic, i) in row.pictures?.split(',')"
+          :key="i"
+          :src="`/api/file/upload/res/${pic}`"
+          :preview-src-list="row.pictures.split(',').map((v:any)=>{
+                return `/api/file/upload/res/${v}`
+              })"
+          :hide-on-click-modal="true"
+          fit="cover"
+          style="height: 50px; width: 75px; max-width: none; border-radius: 0.475rem; margin-left: 5px"
+          alt=""
+        />
+      </div>
+    </template>
+  </VbDataTable>
+  <VbDataTable
+    v-if="active == 1"
+    url="sys/purifierCondition/selectCleanPage"
+    :header="col2s"
+    :query-params="queryParam2s"
+    method="post"
+    :has-checkbox="false"
+    :pagination="false"
+  >
+    <template #tableHeader>
+      <thead>
+        <tr class="text-center">
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">序号</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化器名称</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化效能</th>
+          <th class="bg-light-primary" colspan="2">清洗周期</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">最后清洗时间</th>
+          <th class="bg-light-primary" colspan="2">上次清洗至今</th>
+          <th class="bg-light-primary" colspan="2">距下次清洗</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">操作</th>
+        </tr>
+        <tr class="text-center">
+          <th class="bg-light-primary">天数</th>
+          <th class="bg-light-primary">开机小时</th>
+          <th class="bg-light-primary">经过天数</th>
+          <th class="bg-light-primary">开机小时数</th>
+          <th class="bg-light-primary">剩余天数</th>
+          <th class="bg-light-primary">剩余小时数</th>
+        </tr>
+      </thead>
+    </template>
+    <template #clean_name="{ row }">
+      <span :style="`color:${colorMapping(row.clean_name)}`">{{ row.clean_name }}</span>
+    </template>
+    <template #last_clean_time="{ row }">
+      {{ moment(row.last_clean_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+    </template>
+    <template #action="{ row }">
+      <div class="text-danger text-center">
+        <span class="table-action" @click="notice(row)">下发通知</span>
+      </div>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    title="清洗通知"
+    :form-items="[
+      {
+        label: '通知人:',
+        field: 'notifier',
+        component: ElInput,
+        placeholder: '请输入通知人',
+        rules: [{ required: true, message: '请填写通知人', trigger: ['blur'] }],
+      },
+    ]"
+    @confirm="onSaveNotice"
+    :form-data="formData"
+  >
+    <template #body>
+      <el-row class="px-10">
+        <el-col :span="24">
+          <dl>
+            <dt>设施名称:</dt>
+            <dd>{{ noticeDetails.monitor_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>清洗周期(天):</dt>
+            <dd>{{ noticeDetails.day_condition }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>清洗周期(小时):</dt>
+            <dd>{{ noticeDetails.hour_condition }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <div class="separator mt-2 mb-5"></div>
+      <el-col :span="24">
+        <dl>
+          <dt class="me-5" style="width: 90px; text-align: right">通知单位:</dt>
+          <dd>{{ noticeDetails.notice_org }}环保局</dd>
+        </dl>
+      </el-col>
+    </template>
+  </VbModal>
+</template>

+ 115 - 0
src/views/warn/abnormal.vue

@@ -0,0 +1,115 @@
+<script setup lang="ts">
+import { ref } from "vue"
+import moment from "moment"
+import router from "@/router"
+const cols = ref([
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "异常类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "告警时间",
+    field: "warn_time",
+  },
+  {
+    name: "持续时间(分钟)",
+    field: "continue_time",
+  },
+  {
+    name: "操作",
+    field: "action",
+    width: 100,
+  },
+])
+const warnType = ref(null)
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const dateRange = ref<any>([moment(new Date()).add(-1, "M").toDate(), new Date()])
+const queryParams = ref({
+  warn_type: warnType.value,
+  abnormal_state: null,
+  query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+  query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+})
+
+function query() {
+  queryParams.value = {
+    warn_type: warnType.value,
+    abnormal_state: null,
+    query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+    query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+  }
+}
+function reset() {
+  warnType.value = null
+  dateRange.value = [moment(new Date()).add(-1, "M").toDate(), new Date()]
+  query()
+}
+function detail(row: any) {
+  if (row.warn_type == "000100003" || row.warn_type == "000100005" || row.warn_type == "000100007") {
+    router.push({
+      path: "/AbnormalList/strongWarn",
+      query: { back: 1, id: row.id, type: row.warn_type },
+    })
+  } else if (row.warn_type == "000100006") {
+    router.push({
+      path: "/AbnormalList/disConnect",
+      query: { back: 1, id: row.id, type: row.warn_type },
+    })
+  } else {
+    router.push({
+      path: "/AbnormalList/overdueWarn",
+      query: { back: 1, id: row.id, type: row.warn_type },
+    })
+  }
+}
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/companyDeclare/getCompanyWarnList"
+    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"
+            :url="'sys/dict/getList?code=000100001&key=temp'"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :style="dySearchSelectStyle"
+            :size="size"
+            @clear="warnType = null"
+            placeholder="请选择异常类型"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center" label="日期">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="~"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            size="default"
+          />
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-form-item>
+      </el-form>
+    </template>
+
+    <template #action="{ row }">
+      <span class="table-action" @click="detail(row)">查看</span>
+    </template>
+  </VbDataTable>
+</template>

+ 72 - 0
src/views/warn/abnormalList/disConnect.vue

@@ -0,0 +1,72 @@
+<script setup lang="ts">
+import Rs from "@/core/services/RequestService"
+import { ref, onMounted } from "vue"
+import { useRoute } from "vue-router"
+const route = useRoute()
+const options = ref({
+  warn_id: route.query.id,
+  warn_type: route.query.type,
+})
+const chartTitle = ref("")
+const chartData = ref({})
+function getTitle() {
+  Rs.post("sys/warn/getWarnTitle", { data: options.value }).then((res) => {
+    chartTitle.value = res.data
+  })
+}
+function getChart() {
+  Rs.post("sys/warn/getWarnChartDataById", { data: options.value }).then((res) => {
+    chartData.value = res.data
+  })
+}
+const cols = ref([
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "启停阈值(A)",
+    field: "run_threshold",
+  },
+  {
+    name: "当天运行时间",
+    field: "run_time",
+  },
+  {
+    name: "当前设备状态",
+    field: "run_status",
+  },
+])
+const queryParams = ref({
+  warn_id: route.query.id,
+  warn_type: route.query.type,
+})
+const lineOptions = { titleText: " " }
+function init() {
+  getTitle()
+  getChart()
+}
+
+onMounted(init)
+</script>
+
+<template>
+  <div class="card card-bordered mb-8">
+    <div class="card-header bg-light-primary min-h-50px">
+      <div class="card-title">
+        <h3 class="text-primary">{{ chartTitle }}</h3>
+      </div>
+    </div>
+    <div class="card-body p-5 h-325px">
+      <BaseChart :data="chartData" type="line" h="100%" :options="lineOptions"></BaseChart>
+    </div>
+  </div>
+  <VbDataTable
+    :header="cols"
+    url="sys/warn/getWarntableDataById"
+    method="post"
+    :no-page="true"
+    :query-params="queryParams"
+    :has-checkbox="false"
+  ></VbDataTable>
+</template>

+ 191 - 0
src/views/warn/abnormalList/overdueWarn.vue

@@ -0,0 +1,191 @@
+<script setup lang="ts">
+import Rs from "@/core/services/RequestService"
+import moment from "moment"
+import { ref, onMounted, computed } from "vue"
+import { useRoute } from "vue-router"
+const route = useRoute()
+const options = ref({
+  warn_id: route.query.id,
+  warn_type: route.query.type,
+})
+const type = ref(0)
+const chartTitle = ref("")
+const _chartData = ref({})
+const chartData = computed(() => {
+  const data: any = Object.assign({}, _chartData.value)
+  if (!data) {
+    return {}
+  }
+  const period = type.value == 0 ? data.day_condition : data.hour_condition
+  const overdue =
+    type.value == 0
+      ? data.run_day - data.day_condition > 0
+        ? data.run_day - data.day_condition
+        : 0
+      : data.run_hour - data.hour_condition > 0
+      ? data.run_hour - data.hour_condition
+      : 0
+
+  return {
+    chartData: [
+      {
+        categories: ["逾期时间", "周期时间"],
+        series: [
+          {
+            name: "辅助",
+            type: "bar",
+            stack: "总量",
+            barWidth: 80,
+            barMaxWidth: 80,
+            itemStyle: {
+              barBorderColor: "rgba(0,0,0,0)",
+              color: "rgba(0,0,0,0)",
+            },
+            data: [period, 0],
+          },
+          {
+            name: "时间",
+            type: "bar",
+            stack: "总量",
+            barWidth: 80,
+            barMaxWidth: 80,
+            label: {
+              show: true,
+              position: "right",
+            },
+            data: [
+              {
+                value: overdue,
+                itemStyle: {
+                  color: "rgba(242,99,123,0.75)",
+                },
+              },
+              {
+                value: period,
+                itemStyle: {
+                  color: "rgba(24,144,255,0.75)",
+                },
+              },
+            ],
+          },
+        ],
+        yzTitle: `${type.value == 0 ? "天" : "小时"}`,
+      },
+    ],
+  }
+})
+function getTitle() {
+  Rs.post("sys/warn/getWarnTitle", { data: options.value }).then((res) => {
+    chartTitle.value = res.data
+  })
+}
+function getChart() {
+  Rs.post("sys/warn/getCleanWarntableById", { data: options.value }).then((res) => {
+    _chartData.value = Object.assign({}, res.data[0])
+  })
+}
+const cols = ref([
+  {
+    name: "净化器名称",
+    field: "monitor_name",
+  },
+  {
+    name: "天数",
+    field: "day_condition",
+  },
+  {
+    name: "开机小时",
+    field: "hour_condition",
+  },
+  {
+    name: "上次清洗时间",
+    field: "last_clean_time",
+  },
+  {
+    name: "经过天数",
+    field: "run_day",
+  },
+  {
+    name: "开机小时数",
+    field: "run_hour",
+  },
+  {
+    name: "距下次清洗",
+    field: "surplus_day",
+  },
+  {
+    name: "剩余小时数",
+    field: "surplus_hour",
+  },
+])
+const queryParams = ref({
+  warn_id: route.query.id,
+  warn_type: route.query.type,
+})
+const barOption = {
+  grid: { top: 10, right: 40 },
+  legend: { show: false },
+  titleText: " ",
+  direction: "y",
+  tooltip: {
+    formatter: "{b1}: {c1}",
+  },
+}
+function init() {
+  getTitle()
+  getChart()
+}
+
+onMounted(init)
+</script>
+
+<template>
+  <div class="card card-bordered mb-8">
+    <div class="card-header bg-light-primary min-h-50px">
+      <div class="card-title">
+        <h3 class="text-primary">{{ chartTitle }}</h3>
+      </div>
+      <div class="card-toolbar">
+        <el-radio-group v-model="type">
+          <el-radio-button :label="0">按天数</el-radio-button>
+          <el-radio-button :label="1">按小时数</el-radio-button>
+        </el-radio-group>
+      </div>
+    </div>
+    <div class="card-body p-5 h-325px">
+      <BaseChart :data="chartData" type="bar" h="100%" :options="barOption"></BaseChart>
+    </div>
+  </div>
+  <VbDataTable
+    :header="cols"
+    url="sys/warn/getCleanWarntableById"
+    method="post"
+    :no-page="true"
+    :query-params="queryParams"
+    :has-checkbox="false"
+  >
+    <template #tableHeader>
+      <thead>
+        <tr class="text-center">
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">净化器名称</th>
+          <th class="bg-light-primary" colspan="2">清洗周期</th>
+          <th class="bg-light-primary" rowspan="2" style="vertical-align: middle">最后清洗时间</th>
+          <th class="bg-light-primary" colspan="2">上次清洗至今</th>
+          <th class="bg-light-primary" colspan="2">距下次清洗</th>
+        </tr>
+        <tr class="text-center">
+          <th class="bg-light-primary">天数</th>
+          <th class="bg-light-primary">开机小时</th>
+          <th class="bg-light-primary">经过天数</th>
+          <th class="bg-light-primary">开机小时数</th>
+          <th class="bg-light-primary">剩余天数</th>
+          <th class="bg-light-primary">剩余小时数</th>
+        </tr>
+      </thead>
+    </template>
+
+    <template #last_clean_time="{ row }">
+      {{ moment(row.last_clean_time, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+    </template>
+  </VbDataTable>
+</template>

+ 72 - 0
src/views/warn/abnormalList/strongWarn.vue

@@ -0,0 +1,72 @@
+<script setup lang="ts">
+import Rs from "@/core/services/RequestService"
+import { ref, onMounted } from "vue"
+import { useRoute } from "vue-router"
+const route = useRoute()
+const options = ref({
+  warn_id: route.query.id,
+  warn_type: route.query.type,
+})
+const chartTitle = ref("")
+const chartData = ref({})
+function getTitle() {
+  Rs.post("sys/warn/getWarnTitle", { data: options.value }).then((res) => {
+    chartTitle.value = res.data
+  })
+}
+function getChart() {
+  Rs.post("sys/warn/getWarnChartDataById", { data: options.value }).then((res) => {
+    chartData.value = res.data
+  })
+}
+const cols = ref([
+  {
+    name: "浓度最大值(mg/m3)",
+    field: "warn_max_value",
+  },
+  {
+    name: "排放限值",
+    field: "warn_threshold",
+  },
+  {
+    name: "排放占比(%)",
+    field: "discharge_percent",
+  },
+  {
+    name: "发生时间",
+    field: "warn_max_time",
+  },
+])
+const queryParams = ref({
+  warn_id: route.query.id,
+  warn_type: route.query.type,
+})
+const lineOptions = { titleText: " " }
+function init() {
+  getTitle()
+  getChart()
+}
+
+onMounted(init)
+</script>
+
+<template>
+  <div class="card card-bordered mb-8">
+    <div class="card-header bg-light-primary min-h-50px">
+      <div class="card-title">
+        <h3 class="text-primary">{{ chartTitle }}</h3>
+      </div>
+    </div>
+    <div class="card-body p-5 h-325px">
+      <BaseChart :data="chartData" type="line" h="100%" :options="lineOptions"></BaseChart>
+    </div>
+  </div>
+  <VbDataTable
+    :header="cols"
+    url="sys/warn/getWarntableDataById"
+    method="post"
+    :no-page="true"
+    :query-params="queryParams"
+    :has-checkbox="false"
+  ></VbDataTable>
+</template>

+ 296 - 0
src/views/warn/abnormal_declare.vue

@@ -0,0 +1,296 @@
+<script setup lang="ts">
+import { ref, computed } from "vue"
+import configs from "@/core/config/Index"
+import moment from "moment"
+import DySelect from "@/components/select/DySelect.vue"
+import { ElDatePicker, ElInput } from "element-plus"
+import type { VbFormItem } from "@/components/Forms/models"
+import Rs from "@/core/services/RequestService"
+
+const table = ref()
+const cols = ref<any>([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+    width: 60,
+  },
+  {
+    name: "设施名称",
+    field: "device_name",
+  },
+  {
+    name: "申报时间",
+    field: "abnormal_date",
+  },
+  {
+    name: "申报周期",
+    field: "maintain_times",
+    width: 350,
+  },
+
+  {
+    name: "申报类型",
+    field: "declare_type_name",
+  },
+  {
+    name: "审批状态",
+    field: "audit_state_name",
+  },
+  {
+    name: "操作",
+    field: "action",
+    width: 160,
+  },
+])
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const auditStateSelectList = [
+  {
+    label: "未审核",
+    value: "0",
+  },
+  {
+    label: "审核通过",
+    value: "1",
+  },
+  {
+    label: "审核未通过",
+    value: "2",
+  },
+]
+const auditState = ref("")
+const dateRange = ref<any>("")
+const queryParams = ref({
+  audit_state: auditState.value,
+  declare_time_query_start: "",
+  declare_time_query_end: "",
+})
+function query() {
+  if (dateRange.value[0]) {
+    queryParams.value = {
+      audit_state: auditState.value,
+      declare_time_query_start: moment(dateRange.value[0]).format("YYYYMMDD"),
+      declare_time_query_end: moment(dateRange.value[1]).format("YYYYMMDD"),
+    }
+  } else {
+    queryParams.value = {
+      audit_state: auditState.value,
+      declare_time_query_start: "",
+      declare_time_query_end: "",
+    }
+  }
+}
+function reset() {
+  auditState.value = ""
+  dateRange.value = ""
+  query()
+}
+const operationType = ref<"D" | "C">("D")
+const modal = ref()
+const modalTitle = computed(() => {
+  return operationType.value == "C" ? "添加申报" : operationType.value == "D" ? "申报详情" : ""
+})
+const formItems = computed(() => {
+  return (
+    operationType.value == "C"
+      ? [
+          {
+            label: "维护设备",
+            field: "device_id",
+            placeholder: "请选择维护设备",
+            required: true,
+            component: DySelect,
+            props: {
+              url: "sys/monitor/getDeviceList",
+            },
+            span: 12,
+          },
+          {
+            label: "维护类型",
+            field: "maintain_type",
+            placeholder: "请选择维护类型",
+            required: true,
+            component: DySelect,
+            props: {
+              url: "sys/dict/getList?code=000120001&key=temp",
+              formatRemoteData: (v: any) => {
+                return v?.list
+              },
+            },
+            span: 12,
+          },
+          {
+            label: "维护周期",
+            field: "date",
+            required: true,
+            component: ElDatePicker,
+            props: {
+              type: "datetimerange",
+              startPlaceholder: "开始时间",
+              endPlaceholder: "结束时间",
+              rangeSeparator: "~",
+              format: "YYYY-MM-DD HH:mm",
+              disabledDate: (v: any) => {
+                if (v <= moment().add(7, "day")) {
+                  if (v >= moment().startOf("day")) {
+                    return false
+                  }
+                }
+                return true
+              },
+            },
+            span: 24,
+          },
+          {
+            label: "申报备注",
+            field: "description",
+            placeholder: "请输入申报备注",
+            component: ElInput,
+            type: "textarea",
+            span: 24,
+          },
+        ]
+      : operationType.value == "D"
+      ? []
+      : []
+  ) as Array<VbFormItem>
+})
+const formData = ref<any>({})
+const emptyDate = {
+  device_id: "",
+  description: "",
+  maintain_type: "",
+}
+function add() {
+  operationType.value = "C"
+  formData.value = Object.assign({}, emptyDate)
+  formData.value.date = ""
+  modal.value.show()
+}
+const details = ref<any>({})
+function detail(row: any) {
+  operationType.value = "D"
+  details.value = {
+    device_name: row.device_name,
+    description: row.description,
+    maintain_times: row.maintain_times.substring(0, 19),
+    maintain_times_end: row.maintain_times.substring(20, 39),
+    maintain_type_name: row.maintain_type_name,
+  }
+  modal.value.show()
+}
+function onSave() {
+  if (operationType.value == "D") {
+    return
+  }
+  const data = {
+    device_id: formData.value.device_id,
+    declare_type: "1",
+    description: formData.value.description,
+    maintain_type: formData.value.maintain_type,
+    begin_date: moment(formData.value.date[0]).format("YYYYMMDDHHmmss"),
+    end_date: moment(formData.value.date[1]).format("YYYYMMDDHHmmss"),
+  }
+  Rs.post("sys/companyDeclare/newDeclare", { data }).then(() => {
+    table.value.search()
+  })
+}
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/companyDeclare/getCompanyMaintainList"
+    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">
+          <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="add">新建申报</el-button>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="审批状态">
+          <el-select v-model="auditState" :style="dySearchSelectStyle" :size="size" placeholder="请选择审批状态">
+            <el-option v-for="(v, i) in auditStateSelectList" :key="i" :label="v.label" :value="v.value"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center" label="日期">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="~"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            size="default"
+          />
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-form-item>
+      </el-form>
+    </template>
+
+    <template #abnormal_date="{ row }">
+      {{ moment(row.abnormal_date, "YYYYMMDDHHmmss").format("YYYY-MM-DD") }}
+    </template>
+    <template #audit_state_name="{ row }">
+      <span
+        class="badge"
+        :class="`badge-${row.audit_state == 0 ? 'primary' : row.audit_state == 1 ? 'success' : 'danger'}`"
+      >
+        {{ row["audit_state_name"] }}
+      </span>
+    </template>
+
+    <template #action="{ row }">
+      <span class="table-action" @click="detail(row)">申报详情</span>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    v-model:form-data="formData"
+    :form-items="formItems"
+    :title="modalTitle"
+    :confirm-btn="operationType == 'C'"
+    :close-btn-class="`btn btn-${operationType == 'C' ? 'light' : 'primary'}`"
+    @confirm="onSave"
+  >
+    <template v-if="operationType == 'D'" #body>
+      <el-row>
+        <el-col :span="12">
+          <dl>
+            <dt>维护设备:</dt>
+            <dd>{{ details.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>维护类型:</dt>
+            <dd>{{ details.maintain_type_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>开始时间:</dt>
+            <dd>{{ details.maintain_times }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>结束时间:</dt>
+            <dd>{{ details.maintain_times_end }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>申报备注:</dt>
+            <dd>{{ details.description }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+    </template>
+  </VbModal>
+</template>

+ 124 - 0
src/views/warn/companyWarnList.vue

@@ -0,0 +1,124 @@
+<script setup lang="ts">
+import { ref } from "vue"
+import NoProcess from "@/views/_component/_WarnNoProcess.vue"
+import Processing from "@/views/_component/_WarnProcessing.vue"
+import Processed from "@/views/_component/_WarnProcessed.vue"
+
+const active = ref(0)
+const cols = ref([
+  {
+    name: "公司名称",
+    field: "company_name",
+  },
+
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "告警类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "告警数据",
+    field: "warn_value",
+  },
+  {
+    name: "发生时间",
+    field: "warn_time",
+  },
+  {
+    name: "恢复时间",
+    field: "warn_recovery",
+  },
+  {
+    name: "持续时间",
+    field: "continue_time",
+  },
+])
+const col2s = ref([
+  {
+    name: "公司名称",
+    field: "company_name",
+  },
+  {
+    name: "告警类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "告警数据",
+    field: "warn_value",
+  },
+  {
+    name: "发生时间",
+    field: "warn_start_time",
+  },
+  {
+    name: "恢复时间",
+    field: "warn_recovery_time",
+  },
+  {
+    name: "持续时间",
+    field: "warn_continue_time",
+  },
+  {
+    name: "状态",
+    field: "work_order_stats_name",
+  },
+  {
+    name: "操作",
+    width: 150,
+    field: "action",
+  },
+])
+const col3s = ref([
+  {
+    name: "公司名称",
+    field: "company_name",
+  },
+
+  {
+    name: "设备名称",
+    field: "device_name",
+  },
+  {
+    name: "告警类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "告警数据",
+    field: "warn_value",
+  },
+  {
+    name: "创建时间",
+    field: "work_order_start_time",
+  },
+  {
+    name: "完结时间",
+    field: "work_order_end_time",
+  },
+  {
+    name: "持续时间",
+    field: "work_order_continue_time",
+  },
+  {
+    name: "操作",
+    width: 150,
+    field: "action",
+  },
+])
+</script>
+
+<template>
+  <el-tabs v-model="active" class="demo-tabs">
+    <el-tab-pane label="未处理" :name="0">
+      <NoProcess v-if="active == 0" :type="2" :columns="cols"></NoProcess>
+    </el-tab-pane>
+    <el-tab-pane label="处理中" :name="1">
+      <Processing v-if="active == 1" :type="2" :columns="col2s"></Processing>
+    </el-tab-pane>
+    <el-tab-pane label="已完结" :name="2">
+      <Processed v-if="active == 2" :type="2" :columns="col3s"></Processed>
+    </el-tab-pane>
+  </el-tabs>
+</template>

+ 266 - 2
src/views/warn/ouput.vue

@@ -1,7 +1,271 @@
 <script setup lang="ts">
-import { defineProps, reactive, ref, toRefs } from "vue"
+import { getAssetPath } from "@/core/helpers/assets"
+import RequestService from "@/core/services/RequestService"
+import moment from "moment"
+import { ref, computed, onMounted } from "vue"
+const cols = ref([
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "公司名称",
+    field: "name",
+  },
+  {
+    name: "排放超标次数",
+    field: "exceed_total",
+  },
+  {
+    name: "操作",
+    width: 150,
+    field: "action",
+  },
+])
+
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+
+const levelType = ref(0)
+const curDate = ref(new Date())
+const jidu = ref(1)
+const dateType = computed(() => {
+  return levelType.value == 0
+    ? "date"
+    : levelType.value == 1
+    ? "month"
+    : levelType.value == 2 || levelType.value == 3
+    ? "year"
+    : "date"
+})
+const dateLabel = computed(() => {
+  return levelType.value == 0
+    ? "日期"
+    : levelType.value == 1
+    ? "月份"
+    : levelType.value == 2 || levelType.value == 3
+    ? "年份"
+    : "日期"
+})
+const queryParams = ref({
+  type: 0,
+  date: moment(curDate.value).format("YYYYMMDD"),
+})
+const tableData = ref<any>({ red: [], black: [] })
+const redData = computed(() => {
+  return tableData.value.red
+})
+const blackData = computed(() => {
+  return tableData.value.black
+})
+const isLoading = ref(false)
+function loadTable() {
+  isLoading.value = true
+  RequestService.post("sys/companyRedBlack/query", { data: queryParams.value, successAlert: false })
+    .then((res) => {
+      isLoading.value = false
+      tableData.value = res.data
+    })
+    .catch(() => {
+      isLoading.value = false
+    })
+}
+function query() {
+  let date = ""
+  if (levelType.value == 0) {
+    date = moment(curDate.value).format("YYYYMMDD")
+  }
+  if (levelType.value == 1) {
+    date = moment(curDate.value).format("YYYYMM")
+  }
+  if (levelType.value == 2) {
+    date = moment(curDate.value).format("YYYY") + "0" + jidu.value
+  }
+  if (levelType.value == 3) {
+    date = moment(curDate.value).format("YYYY")
+  }
+  queryParams.value = {
+    type: levelType.value,
+    date: date,
+  }
+  loadTable()
+}
+function reset() {
+  jidu.value = 1
+  levelType.value = 0
+  curDate.value = new Date()
+  query()
+}
+function exported() {
+  let str = "/api/sys/companyRedBlack/export"
+  let date = ""
+  if (levelType.value == 0) {
+    date = moment(curDate.value).format("YYYYMMDD")
+  }
+  if (levelType.value == 1) {
+    date = moment(curDate.value).format("YYYYMM")
+  }
+  if (levelType.value == 2) {
+    date = moment(curDate.value).format("YYYY") + "0" + jidu.value
+  }
+  if (levelType.value == 3) {
+    date = moment(curDate.value).format("YYYY")
+  }
+  str += "?date=" + date + "&type=" + levelType.value
+  window.open(str)
+}
+const modal = ref()
+const details = ref<any>({})
+function detail(row: any) {
+  details.value = Object.assign({}, row)
+  if (details.value.warn_starttime) {
+    details.value.warn_starttime = moment(details.value.warn_starttime, "YYYYMMDD").format("YYYY-MM-DD")
+  }
+  modal.value.show()
+}
+onMounted(loadTable)
 </script>
 
 <template>
-  <div>ouput</div>
+  <!--begin::Alert-->
+  <div class="alert alert-dismissible bg-primary d-flex flex-sm-row p-5 mb-10">
+    <div class="d-flex align-items-center text-light pe-0 pe-sm-10">
+      <span class="svg-icon svg-icon-3hx svg-icon-light me-6">
+        <inline-svg :src="getAssetPath('media/icons/duotune/abstract/abs026.svg')" />
+      </span>
+      <h4 class="light text-light mb-0 fs-2hx ls-2" style="font-family: 'fangsong'">餐饮红黑榜</h4>
+    </div>
+  </div>
+  <div class="tool">
+    <el-form class="align-items-center mb-5" :inline="true">
+      <el-form-item class="mb-0 me-5 align-items-center" label="周期">
+        <el-select v-model="levelType" :style="dySearchSelectStyle" placeholder="请选择周期">
+          <el-option label="日" :value="0"></el-option>
+          <el-option label="月" :value="1"></el-option>
+          <el-option label="季度" :value="2"></el-option>
+          <el-option label="年" :value="3"></el-option>
+        </el-select>
+      </el-form-item>
+
+      <el-form-item class="mb-0 me-5 align-items-center" :label="dateLabel">
+        <el-date-picker
+          v-model="curDate"
+          :type="dateType"
+          range-separator="~"
+          placeholder="请选择日期"
+          :size="size"
+          style="width: 230px"
+          :clearable="false"
+        />
+      </el-form-item>
+      <el-form-item v-if="levelType == 2" class="mb-0 me-5 align-items-center" label="季度">
+        <el-select v-model="jidu" :style="dySearchSelectStyle" placeholder="请选择季度">
+          <el-option label="第一季度" :value="1"></el-option>
+          <el-option label="第二季度" :value="2"></el-option>
+          <el-option label="第三季度" :value="3"></el-option>
+          <el-option label="第四季度" :value="4"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item class="mb-0 me-0 align-items-center">
+        <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>
+  </div>
+  <el-row :gutter="30">
+    <el-col :span="12">
+      <div class="card">
+        <div class="card-header">
+          <div class="card-title">
+            <h1 class="text-primary">红榜名单</h1>
+          </div>
+        </div>
+        <div class="card-body p-5">
+          <VbDataTable :data="redData" :header="cols" :loading="isLoading" :has-checkbox="false" :no-page="true">
+            <template #action="{ row }">
+              <div class="text-danger text-center">
+                <span class="table-action" @click="detail(row)">查看详情</span>
+              </div>
+            </template>
+          </VbDataTable>
+        </div>
+      </div>
+    </el-col>
+    <el-col :span="12">
+      <div class="card">
+        <div class="card-header">
+          <div class="card-title">
+            <h1 class="text-primary">黑榜名单</h1>
+          </div>
+        </div>
+        <div class="card-body p-5">
+          <VbDataTable :data="blackData" :header="cols" :loading="isLoading" :has-checkbox="false" :no-page="true">
+            <template #action="{ row }">
+              <div class="text-danger text-center">
+                <span class="table-action" @click="detail(row)">查看详情</span>
+              </div>
+            </template>
+          </VbDataTable>
+        </div>
+      </div>
+    </el-col>
+  </el-row>
+
+  <VbModal
+    v-model:modal="modal"
+    modal-class="modal-md"
+    title="详情"
+    :confirm-btn="false"
+    :close-btn-class="`btn btn-primary`"
+  >
+    <template #body>
+      <el-row>
+        <el-col :span="24">
+          <dl>
+            <dt>企业名称:</dt>
+            <dd>{{ details.name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>油烟排放均值:</dt>
+            <dd>{{ details.smoke_density }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>颗粒物排放均值:</dt>
+            <dd>{{ details.pm25 }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>非甲烷排放均值:</dt>
+            <dd>{{ details.voc_density }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+    </template>
+  </VbModal>
 </template>
+<style lang="scss" scoped>
+.card-title {
+  h1 {
+    position: relative;
+    padding-left: 18px;
+    &:before {
+      content: "";
+      position: absolute;
+      left: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      display: block;
+      width: 5px;
+      height: 30px;
+      background-color: var(--bs-primary);
+      border-radius: 5px;
+    }
+  }
+}
+</style>

+ 323 - 4
src/views/warn/warnList.vue

@@ -1,9 +1,328 @@
 <script setup lang="ts">
-import { defineProps, reactive, ref, toRefs } from "vue"
-import apis from "@/api/index"
-const company = ref<any>({})
+import { computed, ref } from "vue"
+import configs from "@/core/config/Index"
+import moment from "moment"
+import router from "@/router"
+import { ElInput } from "element-plus"
+import type { VbFormItem } from "@/components/Forms/models"
+import Rs from "@/core/services/RequestService"
+const table = ref()
+const cols = ref([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+    width: 60,
+  },
+  {
+    name: "区域",
+    field: "org_name",
+  },
+  {
+    name: "企业名称",
+    field: "company_name",
+  },
+  {
+    name: "监测类型",
+    field: "monitoring_type_name",
+  },
+  {
+    name: "告警类型",
+    field: "warn_type_name",
+  },
+  {
+    name: "告警时间",
+    field: "warn_starttime",
+  },
+  // {
+  //   name: "处理时间",
+  //   field: "abnormal_date",
+  // },
+  {
+    name: "处理结果",
+    field: "abnormal_state_name",
+  },
+  {
+    name: "操作",
+    width: 210,
+    field: "action",
+  },
+])
+const jump = function (v: any) {
+  console.log("jump", v)
+  router.push({
+    path: "/goLineData/oilFumeConcentration",
+    query: {
+      back: 1,
+      comName: v.company_name,
+      company_id: v.company_id,
+    },
+  })
+}
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const companyName = ref("")
+const orgId = ref<string | null>(null)
+const warnType = ref("")
+const abnormalState = ref("")
+const monitoringType = ref("")
+const dateRange = ref<[Date, Date]>([moment(new Date()).add(-7, "d").toDate(), new Date()])
+const queryParams = ref<any>({
+  query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+  query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+})
+function query() {
+  const params = {
+    query_start_time: moment(dateRange.value[0]).format("YYYYMMDD"),
+    query_end_time: moment(dateRange.value[1]).format("YYYYMMDD"),
+    warn_type: warnType.value,
+    abnormal_state: abnormalState.value,
+    monitoring_type: monitoringType.value,
+    company_name: companyName.value,
+    org_id: orgId.value,
+  }
+  const keys = Object.keys(params)
+  keys.forEach((key) => {
+    if (params[key] == "") {
+      delete params[key]
+    }
+  })
+  queryParams.value = params
+}
+function reset() {
+  companyName.value = ""
+  orgId.value = ""
+  warnType.value = ""
+  abnormalState.value = ""
+  monitoringType.value = ""
+  dateRange.value = [moment(new Date()).add(-7, "d").toDate(), new Date()]
+  query()
+}
+const modal = ref()
+const operationType = ref<"D" | "N">("D")
+const modalTitle = computed(() => {
+  return operationType.value == "D" ? "告警详情" : operationType.value == "N" ? "下发通知" : ""
+})
+const formData = ref<any>({})
+const formItems = computed(() => {
+  return (
+    operationType.value == "N"
+      ? [
+          {
+            label: "企业名称:",
+            field: "company_name",
+            component: "innerText",
+            span: 12,
+          },
+          {
+            label: "异常类型:",
+            field: "warn_type_name",
+            component: "innerText",
+            span: 12,
+          },
+          {
+            label: "告警设备:",
+            field: "device_name",
+            component: "innerText",
+            span: 12,
+          },
+          {
+            label: "告警时间:",
+            field: "warn_starttime",
+            component: "innerText",
+            span: 12,
+          },
+          {
+            label: "通知内容:",
+            field: "content",
+            placeholder: "请输入通知内容",
+            required: true,
+            component: ElInput,
+            type: "textarea",
+            span: 24,
+          },
+          {
+            label: "通知部门:",
+            field: "notice_org",
+            component: "innerText",
+            span: 12,
+          },
+          {
+            label: "通知人:",
+            field: "notifier",
+            placeholder: "请输入通知人",
+            required: true,
+            component: ElInput,
+            span: 24,
+          },
+        ]
+      : operationType.value == "D"
+      ? []
+      : []
+  ) as Array<VbFormItem>
+})
+const details = ref<any>({})
+function detail(row: any) {
+  operationType.value = "D"
+  if (row.warn_type == "000100003" || row.warn_type == "000100005" || row.warn_type == "000100007") {
+    router.push({
+      path: "/warnList/strongWarn",
+      query: { back: 1, id: row.id, type: row.warn_type },
+    })
+  } else if (row.warn_type == "000100006") {
+    router.push({
+      path: "/warnList/disConnect",
+      query: { back: 1, id: row.id, type: row.warn_type },
+    })
+  } else {
+    router.push({
+      path: "/warnList/overdueWarn",
+      query: { back: 1, id: row.id, type: row.warn_type },
+    })
+  }
+}
+
+function notity(row: any) {
+  formData.value = Object.assign({}, row)
+  formData.value.content = ""
+  formData.value.notifier = ""
+  if (formData.value.warn_starttime) {
+    formData.value.warn_starttime = moment(formData.value.warn_starttime, "YYYYMMDD").format("YYYY-MM-DD")
+  }
+  operationType.value = "N"
+  modal.value.show()
+}
+
+function onSave() {
+  if (operationType.value == "D") {
+    return
+  }
+  const data = {
+    name: formData.value.warn_type,
+    company_id: formData.value.company_id,
+    type: 0,
+    warn_id: formData.value.id,
+    content: formData.value.content,
+    notifier: formData.value.notifier,
+  }
+  Rs.post("sys/notice/sendNotice", { data }).then(() => {
+    table.value.search()
+  })
+}
 </script>
 
 <template>
-  <div>warnList -- {{ company.name }}</div>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/warn/getWarnForPage"
+    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="商户名称">
+          <el-input
+            class=""
+            :style="dySearchSelectStyle"
+            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="dySearchSelectStyle"></OrgSelectTree>
+        </el-form-item>
+
+        <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/getList?code=000100001&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择告警类型"
+          ></DySelect>
+        </el-form-item>
+        <!-- <el-form-item class="mb-0 me-5 align-items-center" label="处理结果">
+          <DySelect
+            v-model="abnormalState"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getList?code=000170000&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择处理结果"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="监测类型">
+          <DySelect
+            v-model="monitoringType"
+            :url="'sys/dict/getCompanyMonitoringType'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择监测类型"
+          ></DySelect>
+        </el-form-item> -->
+        <el-form-item class="mb-0 me-5 align-items-center" label="日期">
+          <el-date-picker
+            v-model="dateRange"
+            type="daterange"
+            range-separator="~"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            :size="size"
+            style="width: 230px"
+          />
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-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 #abnormal_date="{ row }">
+      {{ row["abnormal_date"] ? moment(row["abnormal_date"], "YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss") : "" }}
+    </template>
+    <template #action="{ row }">
+      <div class="text-danger text-center">
+        <span class="table-action" @click="detail(row)">查看详情</span>
+        <span class="table-action" @click="notity(row)">下发通知</span>
+      </div>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    v-model:form-data="formData"
+    :form-items="formItems"
+    :title="modalTitle"
+    :confirm-btn="operationType == 'N'"
+    :close-btn-class="`btn btn-${operationType == 'N' ? 'light' : 'primary'}`"
+    @confirm="onSave"
+  >
+    <template #body>
+      <el-row v-if="operationType == 'D'">
+        <el-col :span="12">
+          <dl>
+            <dt>是否清洗:</dt>
+            <dd>{{ details.is_clear ? "已清洗" : "未清洗" }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>清洗时间:</dt>
+            <dd>{{ details.clear_date }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>备注:</dt>
+            <dd>{{ details.remark }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+    </template>
+  </VbModal>
 </template>

+ 232 - 0
src/views/workNotice/washNotice.vue

@@ -0,0 +1,232 @@
+<script setup lang="ts">
+import { ref, computed } from "vue"
+import configs from "@/core/config/Index"
+import moment from "moment"
+import type { VbFormItem } from "@/components/Forms/models"
+import Rs from "@/core/services/RequestService"
+import { ElDatePicker, ElInput, ElSelect } from "element-plus"
+import { dialog } from "@/core/utils/message"
+const table = ref()
+const cols = ref([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+    width: 60,
+  },
+  {
+    name: "通知名称",
+    field: "warn_type_name",
+  },
+  {
+    name: "通知单位",
+    field: "org_name",
+  },
+  {
+    name: "通知人",
+    field: "notifier",
+  },
+  {
+    name: "通知时间",
+    field: "notice_time_str",
+  },
+  {
+    name: "状态",
+    field: "stateC",
+  },
+  {
+    name: "操作",
+    field: "action",
+    width: 150,
+  },
+])
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const state = ref("")
+const queryParams = ref<any>({
+  type: "1",
+  state: state.value,
+})
+
+const selectList = [
+  {
+    label: "未回复",
+    value: "0",
+  },
+  {
+    label: "已回复",
+    value: "1",
+  },
+]
+
+function query() {
+  queryParams.value = {
+    type: "1",
+    state: state.value,
+  }
+}
+function reset() {
+  state.value = ""
+  query()
+}
+const operationType = ref<"D" | "R">("D")
+const modal = ref()
+const modalTitle = computed(() => {
+  return operationType.value == "D" ? "通知详情" : operationType.value == "R" ? "回复通知" : ""
+})
+const formData = ref<any>({})
+const formItems = computed(() => {
+  return (
+    operationType.value == "R"
+      ? [
+          {
+            label: "是否清洗",
+            field: "is_clear",
+            placeholder: "请选择处理状态",
+            required: true,
+            component: ElSelect,
+            data: [
+              {
+                label: "未清洗",
+                value: "0",
+              },
+              {
+                label: "已清洗",
+                value: "1",
+              },
+            ],
+            span: 12,
+          },
+          {
+            label: "清洗日期",
+            field: "clear_date",
+            required: true,
+            component: ElDatePicker,
+            props: {
+              type: "date",
+              placeholder: "请选择清洗日期",
+            },
+            span: 12,
+          },
+          {
+            label: "回复备注",
+            field: "remark",
+            placeholder: "请输入回复备注",
+            component: ElInput,
+            type: "textarea",
+            span: 24,
+          },
+        ]
+      : operationType.value == "D"
+      ? []
+      : []
+  ) as Array<VbFormItem>
+})
+const details = ref<any>({})
+function detail(row: any) {
+  operationType.value = "D"
+  details.value = Object.assign({}, row)
+  modal.value.show()
+}
+function reply(row: any) {
+  operationType.value = "R"
+  formData.value = {
+    id: row.id,
+    is_clear: "0",
+    clear_date: new Date(),
+    remark: "",
+  }
+  modal.value.show()
+}
+
+function onSave() {
+  if (operationType.value == "D") {
+    return
+  }
+  dialog.confirm("回复只能提交一次,点击取消检查确认,点击确定立刻提交回复!", "确认回复", function (v: any) {
+    if (v.isConfirmed) {
+      formData.value.clear_date = moment(formData.value.clear_date).format("YYYYMMDD")
+      Rs.post("sys/notice/reply", { data: formData.value }).then(() => {
+        table.value.search()
+        modal.value.hide()
+      })
+    }
+  })
+}
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/notice/selectNoticeList"
+    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="处理状态">
+          <el-select v-model="state" :style="dySearchSelectStyle" placeholder="请选择处理状态" :size="size">
+            <el-option v-for="(v, i) in selectList" :key="i" :label="v.label" :value="v.value"></el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-form-item>
+      </el-form>
+    </template>
+    <template #stateC="{ row }">
+      <span v-if="row.state == '0'" class="badge badge-primary">未回复</span>
+      <span v-if="row.state == '1'" class="badge badge-success">已回复</span>
+    </template>
+    <template #action="{ row }">
+      <span class="table-action" @click="detail(row)">详情</span>
+      <span v-if="row.state == '0'" class="table-action" @click="reply(row)">回复</span>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    v-model:form-data="formData"
+    :form-items="formItems"
+    :title="modalTitle"
+    :confirm-btn="operationType == 'R'"
+    :close-btn-class="`btn btn-${operationType == 'R' ? 'light' : 'primary'}`"
+    :save-auto-close="false"
+    @confirm="onSave"
+  >
+    <template v-if="operationType == 'D'" #body>
+      <div style="border-bottom: 1px solid rgba(232, 232, 232, 1); padding: 10px 30px 24px; margin-bottom: 24px">
+        <p style="color: #1890ff; padding-bottom: 10px">您有新的清洗任务需要马上执行</p>
+        <p>
+          设施名称:
+          <span style="color: #1890ff">{{ details.device_name }}</span>
+        </p>
+        <p>
+          清洗周期(天):
+          <span style="color: #1890ff">{{ details.clean_cycle_day }}天</span>
+        </p>
+        <p>
+          清洗周期(小时):
+          <span style="color: #1890ff">{{ details.clean_cycle_hour }}小时</span>
+        </p>
+        <p style="color: #ff0000; padding-top: 10px">清洗后请上传凭证</p>
+      </div>
+      <el-row>
+        <el-col :offset="18" :span="6">
+          <dl>
+            <dt>通知人:</dt>
+            <dd>{{ details.notifier }}</dd>
+          </dl>
+        </el-col>
+        <el-col :offset="18" :span="6">
+          <dl>
+            <dt>通知部门:</dt>
+            <dd>{{ details.org_name }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+    </template>
+  </VbModal>
+</template>

+ 222 - 0
src/views/workNotice/worryNotice.vue

@@ -0,0 +1,222 @@
+<script setup lang="ts">
+import { ref, computed } from "vue"
+import configs from "@/core/config/Index"
+import type { VbFormItem } from "@/components/Forms/models"
+import Rs from "@/core/services/RequestService"
+import { ElInput, ElSelect } from "element-plus"
+import { dialog } from "@/core/utils/message"
+const table = ref()
+const cols = ref([
+  {
+    name: "序号",
+    field: configs.TABLE_INDEX_FIELD,
+    width: 60,
+  },
+  {
+    name: "通知名称",
+    field: "warn_type_name",
+  },
+  {
+    name: "通知单位",
+    field: "org_name",
+  },
+  {
+    name: "通知人",
+    field: "notifier",
+  },
+  {
+    name: "通知时间",
+    field: "notice_time_str",
+  },
+  {
+    name: "状态",
+    field: "stateC",
+  },
+  {
+    name: "操作",
+    field: "action",
+    width: 150,
+  },
+])
+const size = ref<any>("default")
+const dySearchSelectStyle = { width: "180px" }
+const state = ref("")
+const queryParams = ref<any>({
+  type: "0",
+  state: state.value,
+})
+
+const selectList = [
+  {
+    label: "未回复",
+    value: "0",
+  },
+  {
+    label: "已回复",
+    value: "1",
+  },
+]
+
+function query() {
+  queryParams.value = {
+    type: "0",
+    state: state.value,
+  }
+}
+function reset() {
+  state.value = ""
+  query()
+}
+const operationType = ref<"D" | "R">("D")
+const modal = ref()
+const modalTitle = computed(() => {
+  return operationType.value == "D" ? "通知详情" : operationType.value == "R" ? "回复通知" : ""
+})
+const formData = ref<any>({})
+const formItems = computed(() => {
+  return (
+    operationType.value == "R"
+      ? [
+          {
+            label: "异常原因",
+            field: "remark",
+            placeholder: "请输入异常原因",
+            component: ElInput,
+            type: "textarea",
+            span: 24,
+          },
+        ]
+      : operationType.value == "D"
+      ? []
+      : []
+  ) as Array<VbFormItem>
+})
+const details = ref<any>({})
+function detail(row: any) {
+  operationType.value = "D"
+  details.value = Object.assign({}, row)
+  modal.value.show()
+}
+function reply(row: any) {
+  operationType.value = "R"
+  formData.value = {
+    id: row.id,
+    remark: "",
+  }
+  modal.value.show()
+}
+
+function onSave() {
+  if (operationType.value == "D") {
+    return
+  }
+  dialog.confirm("回复只能提交一次,点击取消检查确认,点击确定立刻提交回复!", "确认回复", function (v: any) {
+    if (v.isConfirmed) {
+      Rs.post("sys/notice/replyWarnNotice", { data: formData.value }).then(() => {
+        table.value.search()
+        modal.value.hide()
+      })
+    }
+  })
+}
+</script>
+
+<template>
+  <VbDataTable
+    ref="table"
+    :header="cols"
+    url="sys/notice/getWarnNoticeList"
+    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="处理状态">
+          <el-select v-model="state" :style="dySearchSelectStyle" placeholder="请选择处理状态" :size="size">
+            <el-option v-for="(v, i) in selectList" :key="i" :label="v.label" :value="v.value"></el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <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-form-item>
+      </el-form>
+    </template>
+    <template #stateC="{ row }">
+      <span v-if="row.state == '0'" class="badge badge-primary">未回复</span>
+      <span v-if="row.state == '1'" class="badge badge-success">已回复</span>
+    </template>
+    <template #action="{ row }">
+      <span v-if="row.state == '0'" class="table-action" @click="reply(row)">回复</span>
+      <span v-else class="table-action" @click="detail(row)">详情</span>
+    </template>
+  </VbDataTable>
+  <VbModal
+    v-model:modal="modal"
+    v-model:form-data="formData"
+    :form-items="formItems"
+    :title="modalTitle"
+    :confirm-btn="operationType == 'R'"
+    :close-btn-class="`btn btn-${operationType == 'R' ? 'light' : 'primary'}`"
+    :save-auto-close="false"
+    @confirm="onSave"
+  >
+    <template #body>
+      <el-row>
+        <el-col :span="12">
+          <dl>
+            <dt>企业名称:</dt>
+            <dd>{{ details.company_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>异常类型:</dt>
+            <dd>{{ details.warn_type_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>告警设备:</dt>
+            <dd>{{ details.device_name }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>告警时间:</dt>
+            <dd>{{ details.warn_time }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="24">
+          <dl>
+            <dt>通知内容:</dt>
+            <dd>{{ details.content }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>通知人:</dt>
+            <dd>{{ details.notifier }}</dd>
+          </dl>
+        </el-col>
+        <el-col :span="12">
+          <dl>
+            <dt>通知部门:</dt>
+            <dd>{{ details.org_name }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+      <span class="separator my-5"></span>
+      <el-row>
+        <el-col v-if="operationType == 'D'" :span="24">
+          <dl>
+            <dt>回复详情:</dt>
+            <dd>{{ details.remark }}</dd>
+          </dl>
+        </el-col>
+      </el-row>
+    </template>
+  </VbModal>
+</template>