餐饮油烟智能监测与监管一体化平台
feiyu02
6 天以前 ccc970e575ef3f3e5c67af8da210263f4ac549f9
2026.4.10
已修改25个文件
已删除1个文件
已添加2个文件
676 ■■■■■ 文件已修改
components.d.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
index.html 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/loginPageBg1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CompGenericWrapper.vue 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SearchBar.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/core/AppHeader.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/info 补丁 | 查看 | 原始文档 | blame | 历史
src/components/monitor/DistrictRanking.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionTopTask.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/debug/debugdata.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/sfc/TimeSelect.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/LoginPage.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/analysis/huanxincode/HuanxinCodeManage.vue 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/ComplaintManage.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/MonitorControl.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/PunishmentManage.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/check/components/CompDeviceShowTest.vue 277 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/report/ReportManage.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/task/TaskManage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataAnalysisAll.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataAnalysisConcentration.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataAnalysisOnlineRate.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataAnalysisOpenRate.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataAnalysisOverStandardRate.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataDashboard.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataDashboard_old2.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataException.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/monitor/DataException_old.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
components.d.ts
@@ -18,6 +18,7 @@
    BaseContentLayout: typeof import('./src/components/core/BaseContentLayout.vue')['default']
    BaseMap: typeof import('./src/components/map/BaseMap.vue')['default']
    BaseOption: typeof import('./src/components/search-option/base/BaseOption.vue')['default']
    CompGenericWrapper: typeof import('./src/components/CompGenericWrapper.vue')['default']
    CompQuickSet: typeof import('./src/components/search-option/CompQuickSet.vue')['default']
    DeviceStatus: typeof import('./src/components/monitor/DeviceStatus.vue')['default']
    DistrictRanking: typeof import('./src/components/monitor/DistrictRanking.vue')['default']
@@ -145,6 +146,7 @@
  const BaseContentLayout: typeof import('./src/components/core/BaseContentLayout.vue')['default']
  const BaseMap: typeof import('./src/components/map/BaseMap.vue')['default']
  const BaseOption: typeof import('./src/components/search-option/base/BaseOption.vue')['default']
  const CompGenericWrapper: typeof import('./src/components/CompGenericWrapper.vue')['default']
  const CompQuickSet: typeof import('./src/components/search-option/CompQuickSet.vue')['default']
  const DeviceStatus: typeof import('./src/components/monitor/DeviceStatus.vue')['default']
  const DistrictRanking: typeof import('./src/components/monitor/DistrictRanking.vue')['default']
index.html
@@ -4,7 +4,7 @@
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>餐饮油烟智能监测与监管一体化平台</title>
    <title>餐饮油烟智能监测监管一体化平台</title>
  </head>
  <body>
    <div id="app"></div>
@@ -15,4 +15,4 @@
    </script>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
</html>
src/assets/loginPageBg1.png
src/components/CompGenericWrapper.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
<template>
  <!-- dialog包裹 -->
  <el-dialog
    v-if="currType == 'dialog'"
    :title="title"
    :model-value="visible"
    @opened="$emit('update:visible', true)"
    @closed="$emit('update:visible', false)"
    destroy-on-close
    :draggable="draggable"
    :modal="modal"
    :append-to-body="appendToBody"
  >
    <div v-if="visible">
      <slot name="content"></slot>
    </div>
  </el-dialog>
  <!-- drawer包裹 -->
  <el-drawer
    v-if="currType == 'drawer'"
    :title="title"
    size="45%"
    direction="ltr"
    :model-value="visible"
    @opened="$emit('update:visible', true)"
    @closed="$emit('update:visible', false)"
    destroy-on-close
  >
    <slot name="content"></slot>
  </el-drawer>
  <!-- é»˜è®¤æ— åŒ…裹 -->
  <div v-if="currType == 'normal'">
    <slot></slot>
  </div>
</template>
<script setup>
import { ref, defineEmits, watch } from 'vue';
const props = defineProps({
  visible: Boolean,
  title: String,
  type: {
    type: String,
    default: 'normal'
  },
  draggable: Boolean,
  modal: {
    type: Boolean,
    default: true
  },
  appendToBody: {
    type: Boolean,
    default: true
  }
});
const typeOptions = ref([
  { id: '0', label: 'dialog' },
  { id: '1', label: 'drawer' },
  { id: '10', label: '' }
]);
const currType = ref('');
const emit = defineEmits(['update:visible']);
watch(
  () => props.type,
  (nValue) => {
    currType.value = nValue;
  },
  { immediate: true }
);
</script>
<style scoped>
:deep(.el-drawer__body) {
  padding-top: 0;
}
:deep(.el-drawer__header) {
  margin-bottom: 16px;
}
</style>
src/components/SearchBar.vue
@@ -1,6 +1,6 @@
<template>
  <el-row class="layout">
    <el-col :span="$slots.summary ? 10 : 24">
    <el-col :span="$slots.summary ? 14 : 24">
      <el-form :inline="true" :model="formSearch">
        <el-form-item label="总任务">
          <!-- <el-input v-model="formSearch.topTaskId" placeholder="总任务" /> -->
@@ -19,7 +19,7 @@
        </el-form-item>
      </el-form>
    </el-col>
    <el-col :span="$slots.summary ? 14 : 0">
    <el-col :span="$slots.summary ? 10 : 0">
      <el-row justify="end">
        <slot name="summary"></slot>
      </el-row>
@@ -62,7 +62,7 @@
      taskApi.getTopTask().then((res) => {
        const list = res
          .filter((e) => {
            return e.districtname == '徐汇区' && dayjs(e.starttime).isBefore(dayjs('2023-12-31'))
            return e.districtname == '徐汇区' && dayjs(e.starttime).isBefore(dayjs('2025-12-31'))
          })
          .map((r) => {
            return {
src/components/core/AppHeader.vue
@@ -10,7 +10,7 @@
    </el-col>
    <el-col :span="12" class="logout">
      <FYBgTaskDialog></FYBgTaskDialog>
      <el-button icon="SwitchButton">退出登录</el-button>
      <el-button icon="SwitchButton" @click="logout">退出登录</el-button>
    </el-col>
  </el-row>
</template>
@@ -50,6 +50,12 @@
      this.isCollapsed = !this.isCollapsed
      this.$emit('collapsedSider', this.isCollapsed)
    },
    /**
     * é€€å‡ºç™»å½•
     */
    logout() {
      this.$router.push(`/login`);
    },
  },
}
</script>
src/components/info
src/components/monitor/DistrictRanking.vue
@@ -61,7 +61,7 @@
  props: {
    selectedMonth: {
      type: String,
      default: '2023-12',
      default: '2025-12',
    },
    rankingType: {
      type: String,
src/components/search-option/FYOptionTopTask.vue
@@ -82,7 +82,7 @@
        })
        this.topTasks = list.filter((e) => {
          return (
            e.data.districtname == '徐汇区' && dayjs(e.data.starttime).isBefore(dayjs('2023-12-31'))
            e.data.districtname == '徐汇区' && dayjs(e.data.starttime).isBefore(dayjs('2025-12-31'))
          )
        })
        if (this.initValue) {
src/debug/debugdata.js
@@ -81,7 +81,7 @@
        latitude: 31.17 + Math.random() * 0.1,
        longitude: 121.45 + Math.random() * 0.1,
        ringCodeLevel: ringCodeLevels[Math.floor(Math.random() * ringCodeLevels.length)],
        ringCodePublishTime: '2023-03-16 10:00:00',
        ringCodePublishTime: '2025-03-16 10:00:00',
        isOnline: isOnline,
        exceptionStatus: exceptionStatus,
      },
@@ -109,7 +109,7 @@
        latitude: 31.19 + Math.random() * 0.1,
        longitude: 121.41 + Math.random() * 0.1,
        ringCodeLevel: ringCodeLevels[Math.floor(Math.random() * ringCodeLevels.length)],
        ringCodePublishTime: '2023-03-16 10:00:00',
        ringCodePublishTime: '2025-03-16 10:00:00',
        isOnline: isOnline,
        exceptionStatus: exceptionStatus,
      },
@@ -123,7 +123,7 @@
  // ç”Ÿæˆè¿‘1小时的监测数据,每10分钟一条
  const data = []
  const now = new Date()
  now.setFullYear(2023)
  now.setFullYear(2025)
  for (let i = 5; i >= 0; i--) {
    const time = new Date(now.getTime() - i * 10 * 60 * 1000)
src/sfc/TimeSelect.vue
@@ -15,7 +15,7 @@
<script>
import dayjs from 'dayjs'
// æ—¶é—´èŒƒå›´å¿«æ·é€‰é¡¹
const dayStart = dayjs('2023-08-01').startOf('date')
const dayStart = dayjs('2025-08-01').startOf('date')
const dayEnd = dayStart.endOf('date')
const shortcuts = [
  {
@@ -79,7 +79,7 @@
    return {
      //保存开始和结束时间
      // éšä¾¿è®¾ç½®åˆå§‹å€¼ ï¼Œmounted时再设正确的,目的是改变时间了触发change
      time: ['2023-06-01 12:00:00', '2023-06-20 16:00:00'],
      time: ['2025-06-01 12:00:00', '2025-06-20 16:00:00'],
      // æŽ§åˆ¶æ—¶é—´é€‰æ‹©å™¨çš„æ˜¾ç¤º/隐藏(仅在新样式下使用)
      showTimePicker: false,
      // æ—¶é—´èŒƒå›´é€‰é¡¹
@@ -117,7 +117,7 @@
      // this.time[0] = dayjs().subtract(4, 'week').format('YYYY-MM-DD HH:mm:ss')
      // this.time[1] = dayjs().format('YYYY-MM-DD HH:mm:ss')
      // 2026.3.13 demo ä¸­å›ºå®šåˆå§‹æ—¶é—´
      this.time = ['2023-08-01 00:00:00', '2023-08-31 23:59:59']
      this.time = ['2025-08-01 00:00:00', '2025-08-31 23:59:59']
    },
    // å¿«æ·æ—¶æ®µé€‰æ‹©
src/views/LoginPage.vue
@@ -2,7 +2,7 @@
  <div class="login-container">
    <div class="login-wrapper">
      <div class="login-header">
        <h1 class="login-title">餐饮油烟智能监测与监管一体化平台</h1>
        <h1 class="login-title">餐饮油烟智能监测监管一体化平台</h1>
        <p class="login-subtitle">欢迎登录</p>
      </div>
      <div class="login-form">
@@ -108,7 +108,7 @@
  display: flex;
  align-items: center;
  justify-content: flex-end;
  background-image: url('@/assets/loginPageBg.png');
  background-image: url('@/assets/loginPageBg1.png');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
src/views/analysis/huanxincode/HuanxinCodeManage.vue
@@ -67,7 +67,7 @@
    <!-- åº—铺列表 -->
    <div class="shop-list">
      <el-table :data="pagedShopList" style="width: 100%">
      <el-table :data="filteredShopList" style="width: 100%" max-height="600px">
        <el-table-column prop="shopName" label="店铺名称" />
        <el-table-column prop="district" label="所在区县" width="120" />
        <el-table-column prop="town" label="所在街镇" width="150" />
@@ -107,7 +107,7 @@
          v-model:page-size="pageSize"
          :page-sizes="[10, 20, 50, 100]"
          layout="total, sizes, prev, pager, next, jumper"
          :total="filteredShopList.length"
          :total="total"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
@@ -221,7 +221,7 @@
    label: '餐饮',
    value: '1',
  },
  time: dayjs('2023-08-01').date(1).toDate(),
  time: dayjs('2025-08-01').date(1).toDate(),
})
// çŠ¶æ€
const drawerVisible = ref(false)
@@ -232,6 +232,7 @@
// åˆ†é¡µç›¸å…³
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
// çŽ¯ä¿¡ç å›¾ç‰‡URL
const codeImageUrl = ref('')
@@ -244,42 +245,6 @@
  redCount: 20,
  redPercentage: 10.5,
})
// åº—铺名称列表
const shopNames = [
  '付小姐在成都',
  '吉刻联盟',
  '家在塔啦',
  '狼来了',
  '乐凯撒星游店',
  '馨远美食小镇(哈尼美食广场)',
  '棒约翰',
  '弄堂咪道',
  '杨记齐齐哈尔烤肉',
  '上海稔传餐饮管理有限公司(人生一串)',
  '缘家',
  '泉盛餐饮(上海)有限公司(食其家)',
  '丰茂烤串',
  '上海泰煌餐饮管理有限公司(泰煌鸡)',
  '徐汇区辰熙餐馆(小铁君串烧居酒屋)',
]
// å¾æ±‡åŒºè¡—镇列表
const xuhuiTowns = [
  '天平路街道',
  '湖南路街道',
  '斜土路街道',
  '枫林路街道',
  '长桥街道',
  '田林街道',
  '虹梅路街道',
  '康健新村街道',
  '徐家汇街道',
  '凌云路街道',
  '龙华街道',
  '漕河泾街道',
  '华泾镇',
]
function onSearch() {
  const f = formSearch.value
@@ -307,9 +272,7 @@
  userApi.fetchUser(currentPage.value, pageSize.value, area).then((res) => {
    if (res) {
      res.data
      res.head.totalCount
      total.value = res.head.totalCount
      shopList.value = res.data.map((item, index) => {
        const { score, code } = generateRandomScore()
        return {
@@ -336,9 +299,9 @@
  })
}
// ç”Ÿæˆ2023å¹´8月内的随机时间
// ç”Ÿæˆ2025å¹´8月内的随机时间
function generateRandomDate() {
  const year = 2023
  const year = 2025
  const month = 7 // 0-11,8月是7
  const day = Math.floor(Math.random() * 31) + 1 // 1-31
  const hour = Math.floor(Math.random() * 24) // 0-23
@@ -346,11 +309,6 @@
  const date = new Date(year, month, day, hour, minute)
  return date.toISOString().slice(0, 16).replace('T', ' ')
}
// éšæœºé€‰æ‹©æ•°ç»„元素
function getRandomElement(array) {
  return array[Math.floor(Math.random() * array.length)]
}
// ç”Ÿæˆéšæœºè¯„分和对应环信码等级
@@ -376,173 +334,7 @@
}
// åº—铺数据
const shopList = ref([
  {
    id: 1,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'green',
    score: 90,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [
      {
        time: generateRandomDate(),
        content: '净化器运行时长不足',
        score: 90,
        handled: true,
      },
    ],
  },
  {
    id: 2,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'yellow',
    score: 75,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [
      {
        time: generateRandomDate(),
        content: '投诉次数较多',
        score: 80,
        handled: false,
      },
    ],
  },
  {
    id: 3,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'red',
    score: 60,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [
      {
        time: generateRandomDate(),
        content: '排放浓度超标',
        score: 65,
        handled: false,
      },
      {
        time: generateRandomDate(),
        content: '清洗频次不足',
        score: 62,
        handled: false,
      },
    ],
  },
  {
    id: 4,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'green',
    score: 92,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [],
  },
  {
    id: 5,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'yellow',
    score: 78,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [
      {
        time: generateRandomDate(),
        content: '风机联动率低',
        score: 75,
        handled: true,
      },
    ],
  },
  {
    id: 6,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'green',
    score: 90,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [],
  },
  {
    id: 7,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'red',
    score: 55,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [
      {
        time: generateRandomDate(),
        content: '未安装油烟净化设备',
        score: 60,
        handled: false,
      },
    ],
  },
  {
    id: 8,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'yellow',
    score: 72,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [
      {
        time: generateRandomDate(),
        content: '净化器清洗不及时',
        score: 75,
        handled: true,
      },
    ],
  },
  {
    id: 9,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'green',
    score: 93,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [],
  },
  {
    id: 10,
    shopName: getRandomElement(shopNames),
    district: '徐汇区',
    town: getRandomElement(xuhuiTowns),
    code: 'yellow',
    score: 76,
    trend: generateRandomTrend(),
    lastUpdate: generateRandomDate(),
    warnings: [
      {
        time: generateRandomDate(),
        content: '排放浓度接近标准限值',
        score: 78,
        handled: true,
      },
    ],
  },
])
const shopList = ref([])
// è¿‡æ»¤åŽçš„店铺列表
const filteredShopList = computed(() => {
@@ -550,13 +342,6 @@
    return shopList.value
  }
  return shopList.value.filter((shop) => shop.code === filterCode.value)
})
// åˆ†é¡µåŽçš„店铺列表
const pagedShopList = computed(() => {
  const start = (currentPage.value - 1) * pageSize.value
  const end = start + pageSize.value
  return filteredShopList.value.slice(start, end)
})
// ç”Ÿå‘½å‘¨æœŸ
@@ -581,10 +366,12 @@
function handleSizeChange(size) {
  pageSize.value = size
  currentPage.value = 1
  onSearch()
}
function handleCurrentChange(current) {
  currentPage.value = current
  onSearch()
}
function getCodeType(code) {
@@ -829,7 +616,7 @@
.card-content {
  text-align: center;
  padding: 20px 0;
  padding: 0px 0;
}
.card-title {
src/views/inspection/ComplaintManage.vue
@@ -151,7 +151,7 @@
import { ElMessage } from 'element-plus'
// æ—¶é—´èŒƒå›´å¿«æ·é€‰é¡¹
const dayStart = dayjs('2023-08-01').startOf('date')
const dayStart = dayjs('2025-08-01').startOf('date')
const dayEnd = dayStart.endOf('date')
const shortcuts = [
  {
@@ -285,7 +285,7 @@
  ]
  const complaintReasons = ['油烟扰民', '夜间噪声', '异味污染', '卫生问题']
  const sources = ['12345热线', '居民投诉', '网络平台', '其他']
  const departments = ['徐汇区环保局', '长宁区环保局', '静安区环保局', '普陀区环保局']
  const departments = ['徐汇区生态环境局', '长宁区生态环境局', '静安区生态环境局', '普陀区生态环境局']
  // const results = ['已处理', '处理中', '未处理']
  const results = ['已处理']
src/views/inspection/MonitorControl.vue
@@ -104,7 +104,7 @@
import { ElMessage } from 'element-plus'
// æ€»è§ˆçŽ°åœºå·¡æŸ¥æ•°æ®
const dayStart = dayjs('2023-08-01').startOf('date')
const dayStart = dayjs('2025-08-01').startOf('date')
const dayEnd = dayStart.endOf('date')
const shortcuts = [
  {
src/views/inspection/PunishmentManage.vue
@@ -147,7 +147,7 @@
import { ElMessage } from 'element-plus'
// æ—¶é—´èŒƒå›´å¿«æ·é€‰é¡¹
const dayStart = dayjs('2023-08-01').startOf('date')
const dayStart = dayjs('2025-08-01').startOf('date')
const dayEnd = dayStart.endOf('date')
const shortcuts = [
  {
@@ -282,7 +282,7 @@
    '火锅店',
  ]
  const punishmentItems = ['油烟超标排放', '未安装油烟净化设备', '设备未正常运行', '噪声污染']
  const departments = ['徐汇区环保局', '长宁区环保局', '静安区环保局', '普陀区环保局']
  const departments = ['徐汇区生态环境局', '长宁区生态环境局', '静安区生态环境局', '普陀区生态环境局']
  for (let i = 0; i < totalCount; i++) {
    // ç”Ÿæˆåœ¨æ—¶é—´èŒƒå›´å†…的随机时间
src/views/inspection/check/components/CompDeviceShowTest.vue
@@ -5,21 +5,10 @@
      <!-- è®¾å¤‡ç±»åž‹  -->
      <el-row>
        <el-col>
          <el-tabs
            class="child_select"
            placeholder="设备类型"
            v-model="currSelect.topDeviceTypeId"
          >
            <el-tab-pane
              v-for="item in deviceTopTypes"
              :key="item.id"
              :name="item.id"
            >
          <el-tabs class="child_select" placeholder="设备类型" v-model="currSelect.topDeviceTypeId">
            <el-tab-pane v-for="item in deviceTopTypes" :key="item.id" :name="item.id">
              <template #label>
                <el-badge
                  :value="item.count"
                  :type="item.count == 0 ? 'danger' : 'primary'"
                >
                <el-badge :value="item.count" :type="item.count == 0 ? 'danger' : 'primary'">
                  <span class="custom-tabs-label">
                    <span>{{ item.label }}</span>
                  </span>
@@ -37,22 +26,42 @@
          class="collapse-item-class"
        >
          <template #title>
            <div
              style="display: flex; width: 100%; justify-content: space-between"
            >
            <div style="display: flex; width: 100%; justify-content: space-between">
              <div style="">
                <el-descriptions style="" :column="3" size="small" border>
                <el-descriptions style="" :column="4" size="small" border>
                  <el-descriptions-item
                    width="64px"
                    :label="
                      currSelect.topDeviceTypeId == 0 ? '站点名称' : '设备名称'
                    "
                    :span="3"
                    :label="currSelect.topDeviceTypeId == 0 ? '站点名称' : '设备名称'"
                    >{{ item.name || '无' }}</el-descriptions-item
                  >
                  <el-descriptions-item label="品牌型号">{{
                    item.brandModel || '无'
                  }}</el-descriptions-item>
                  <el-descriptions-item label="供应商">{{
                    item.supplier || '无'
                  }}</el-descriptions-item>
                  <el-descriptions-item :rowspan="3">
                    <div style="display: flex">
                      <div class="image-container">
                        <div
                          class="block-div"
                          @click="onClickPic($event)"
                          v-for="(status, index) in item._statusList"
                          :key="index"
                        >
                          <el-image
                            v-if="index == 0"
                            fit="cover"
                            class="pic-style"
                            :src="status._picUrl"
                            :preview-src-list="Array.of(status._picUrl)"
                          />
                          <span class="abstract_pic_text" v-if="index == 0">{{
                            `最新状态 ${status.dlCreateTime.slice(0, 10)}`
                          }}</span>
                        </div>
                      </div>
                    </div>
                  </el-descriptions-item>
                  <el-descriptions-item label="运维商">{{
                    item.maintainer || '无'
                  }}</el-descriptions-item>
@@ -76,9 +85,6 @@
                  <el-descriptions-item label="运维联系方式">{{
                    item.maintainTel || '无'
                  }}</el-descriptions-item>
                  <el-descriptions-item label="品牌型号">{{
                    item.brandModel || '无'
                  }}</el-descriptions-item>
                  <el-descriptions-item label="运行状态">
                    <el-select
                      v-model="item.runningStatus"
@@ -96,12 +102,11 @@
                  <el-descriptions-item label="类型">
                    {{ item._typename || '无' }}
                  </el-descriptions-item>
                </el-descriptions>
              </div>
              <div style="display: flex">
                <!-- <div class="sub-title">{{ item.name }}</div> -->
                <!-- å›¾ç‰‡ -->
              <!-- <div style="display: flex">
                <div class="image-container">
                  <div
                    class="block-div"
@@ -121,7 +126,7 @@
                    }}</span>
                  </div>
                </div>
              </div>
              </div> -->
            </div>
          </template>
          <!-- è¯¦ç»†å†…容开始 -->
@@ -168,19 +173,19 @@
</template>
<script>
import deviceApi from '@/api/fysp/deviceApi';
import { $fysp } from '@/api/index';
import { toLabel } from '@/enum/device/device';
import deviceApi from '@/api/fysp/deviceApi'
import { $fysp } from '@/api/index'
import { toLabel } from '@/enum/device/device'
export default {
  components: {  },
  components: {},
  watch: {
    // é€‰æ‹©æ”¹å˜ç›‘听
    currSelect: {
      handler(newObj, oldObj) {
        this.getList();
        this.getList()
      },
      deep: true
    }
      deep: true,
    },
  },
  data() {
    return {
@@ -192,7 +197,7 @@
      // è¡¨å•详情点击按钮的图标
      isDetail: false,
      currSelect: {
        topDeviceTypeId: 0
        topDeviceTypeId: 0,
      },
      // æŽ§åˆ¶è¡¨å•是否可以编辑
      isDisabled: true,
@@ -202,29 +207,29 @@
      deviceTopTypes: [
        { id: 0, label: '监控设备' },
        { id: 1, label: '治理设备' },
        { id: 2, label: '生产设备' }
        { id: 2, label: '生产设备' },
      ],
      // è¿è¡ŒçŠ¶æ€
      runStatusArray: [
        { key: 0, value: '未联网' },
        { key: 1, value: '上线中' },
        { key: 2, value: '下线' },
        { key: 3, value: '拆除' }
        { key: 3, value: '拆除' },
      ],
      // ç»´æŠ¤é¢‘率状态
      maintainFrequencysArray: [
        { key: 1, value: '每月一次' },
        { key: 2, value: '每季度一次' },
        { key: 3, value: '每半年一次' },
        { key: 4, value: '每年一次' }
        { key: 4, value: '每年一次' },
      ],
      // ç§Ÿèµæ–¹å¼
      ownershipArray: [
        { key: 0, value: 'è´­ä¹°' },
        { key: 1, value: '租赁' }
        { key: 1, value: '租赁' },
      ],
      scene: {}
    };
      scene: {},
    }
  },
  props: {},
@@ -234,169 +239,167 @@
    getTabsCount() {
      this.deviceTopTypes.forEach((item) => {
        deviceApi.fetchDevices(this.scene.guid, item.id).then((result) => {
          item.count = result.data.length;
        });
      });
          item.count = result.data.length
        })
      })
    },
    // èŽ·å–è¿è¡ŒçŠ¶æ€å¯¹åº”çš„value
    getRunStatusValueByRunStatusKey(status) {
      var runningStatusValueArray = this.runStatusArray.filter((runStatus) => {
        return runStatus.key == status;
      });
        return runStatus.key == status
      })
      if (runningStatusValueArray.length > 0) {
        return runningStatusValueArray[0].value;
        return runningStatusValueArray[0].value
      }
    },
    // å±•示表单的详情的点击事件
    showDetail(item) {
      item._isDetail = !item._isDetail;
      item._isDetail = !item._isDetail
    },
    init(scene) {
      // çˆ¶ç»„件主动调用初始化子组件的方法
      this.scene = scene;
      this.scene = scene
      this.getList();
      this.getTabsCount();
      this.getList()
      this.getTabsCount()
    },
    // é‡ç½®å±•示的数据
    initList() {
      this.formInfo = [];
      this.isEmpty = false;
      this.formInfo = []
      this.isEmpty = false
    },
    // æ ‡å‡†åŒ–属性名
    convertKeys(obj) {
      // å°†ä¸€ä¸ªjs对象中所有di,wi,pi开头的属性全部改成去掉这些前缀并且重新变为驼峰式命名
      const newObj = {};
      const newObj = {}
      for (const key in obj) {
        let newKey = key;
        let newKey = key
        if (key.startsWith('di')) {
          newKey = key.substring(2);
          newKey = key.substring(2)
        } else if (key.startsWith('wi')) {
          newKey = key.substring(2);
          newKey = key.substring(2)
        } else if (key.startsWith('pi')) {
          newKey = key.substring(2);
          newKey = key.substring(2)
        }
        newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1);
        newObj[newKey] = obj[key];
        newKey = newKey.charAt(0).toLowerCase() + newKey.slice(1)
        newObj[newKey] = obj[key]
      }
      return newObj;
      return newObj
    },
    // æ–°å¢žå­—段
    initFormData(data) {
      data._isDetail = false;
      data._isDetail = false
    },
    getList() {
      deviceApi
        .fetchDevices(this.scene.guid, this.currSelect.topDeviceTypeId)
        .then((result) => {
          this.initList();
          if (result.data == null || result.data.length <= 0) {
            this.isEmpty = true;
            return;
      deviceApi.fetchDevices(this.scene.guid, this.currSelect.topDeviceTypeId).then((result) => {
        this.initList()
        if (result.data == null || result.data.length <= 0) {
          this.isEmpty = true
          return
        }
        // æ ‡å‡†åŒ–属性名
        for (let index = 0; index < result.data.length; index++) {
          var element = this.convertKeys(result.data[index])
          this.initFormData(element)
          // èŽ·å–è®¾å¤‡çŠ¶æ€ä¿¡æ¯
          let data = {
            deviceId: element.id,
            sceneId: element.sceneGuid,
            deviceTypeId: this.currSelect.topDeviceTypeId,
          }
          // æ ‡å‡†åŒ–属性名
          for (let index = 0; index < result.data.length; index++) {
            var element = this.convertKeys(result.data[index]);
            this.initFormData(element);
            // èŽ·å–è®¾å¤‡çŠ¶æ€ä¿¡æ¯
            let data = {
              deviceId: element.id,
              sceneId: element.sceneGuid,
              deviceTypeId: this.currSelect.topDeviceTypeId
            };
            deviceApi.fetchDeviceStatus(data).then((status) => {
              var statusData = status.data;
              var imgPaths = [];
              if (statusData) {
                if (statusData.length == 0) {
                  this.formInfo.push(element);
                  return;
                }
                element = this.convertKeys(result.data[index]);
                element = this.setDeviceType(element);
                element._picUrls = imgPaths;
                for (let index = 0; index < statusData.length; index++) {
                  const statusItem = statusData[index];
                  // è®¾å¤‡å¯¹è±¡æ·»åŠ ä¸€ä¸ªå±žæ€§åˆ—è¡¨å±žæ€§ç”¨æ¥ä¿å­˜è®¾å¤‡çŠ¶æ€
                  this.saveStatus(element, statusItem);
                  element.dlLocation = statusItem.dlLocation;
                  this.formInfo.push(element);
                }
          deviceApi.fetchDeviceStatus(data).then((status) => {
            var statusData = status.data
            var imgPaths = []
            if (statusData) {
              if (statusData.length == 0) {
                this.formInfo.push(element)
                return
              }
            });
          }
        });
              element = this.convertKeys(result.data[index])
              element = this.setDeviceType(element)
              element._picUrls = imgPaths
              for (let index = 0; index < statusData.length; index++) {
                const statusItem = statusData[index]
                // è®¾å¤‡å¯¹è±¡æ·»åŠ ä¸€ä¸ªå±žæ€§åˆ—è¡¨å±žæ€§ç”¨æ¥ä¿å­˜è®¾å¤‡çŠ¶æ€
                this.saveStatus(element, statusItem)
                element.dlLocation = statusItem.dlLocation
                this.formInfo.push(element)
              }
            }
          })
        }
      })
    },
    setDeviceType(element) {
      var type = [];
      var type = []
      type = toLabel(element.sceneTypeId, this.currSelect.topDeviceTypeId, [
        element.typeId,
        element.subtypeId
      ]);
      element._typename = type.join('-');
      return element;
        element.subtypeId,
      ])
      element._typename = type.join('-')
      return element
    },
    // ä¿å­˜çŠ¶æ€ä¿¡æ¯
    saveStatus(device, status) {
      var _picUrl = $fysp.imgUrl + status.dlPicUrl;
      status._picUrl = _picUrl;
      status._paths = _picUrl.split(';');
      device._picUrls.push(_picUrl);
      var _picUrl = $fysp.imgUrl + status.dlPicUrl
      status._picUrl = _picUrl
      status._paths = _picUrl.split(';')
      device._picUrls.push(_picUrl)
      if ('_statusList' in device) {
        device._statusList.push(status);
        device._statusList.push(status)
      } else {
        device._statusList = Array.of(status);
        device._statusList = Array.of(status)
      }
      // æŽ’序
      device._statusList.sort(function (x, y) {
        return new Date(x.dlCreateTime) - new Date(y.dlCreateTime); //    é™åºï¼Œå‡åºåˆ™åä¹‹
      });
        return new Date(x.dlCreateTime) - new Date(y.dlCreateTime) //    é™åºï¼Œå‡åºåˆ™åä¹‹
      })
    },
    submit() {},
    cancel() {},
    modifyObjectKeys(obj) {
      const newObj = {};
      const newObj = {}
      for (const key in obj) {
        // è·³è¿‡ä»¥ 'dl' æˆ– '_' å¼€å¤´çš„属性
        if (key.startsWith('dl') || key.startsWith('_')) {
          newObj[key] = obj[key];
          continue;
          newObj[key] = obj[key]
          continue
        }
        // æ ¹æ® topDeviceTypeId æ·»åŠ å‰ç¼€
        let prefix = '';
        let prefix = ''
        switch (this.currSelect.topDeviceTypeId) {
          case 0:
            prefix = 'di';
            break;
            prefix = 'di'
            break
          case 1:
            prefix = 'pi';
            break;
            prefix = 'pi'
            break
          case 2:
            prefix = 'wi';
            break;
            prefix = 'wi'
            break
          default:
            // å¦‚æžœ topDeviceTypeId ä¸æ˜¯ 0, 1, æˆ– 2,不添加前缀
            newObj[key] = obj[key];
            continue;
            newObj[key] = obj[key]
            continue
        }
        // æ·»åŠ å‰ç¼€å¹¶è½¬æ¢ä¸ºé©¼å³°å¼å‘½å
        const newKey = `${prefix}${key.charAt(0).toUpperCase() + key.slice(1)}`;
        newObj[newKey] = obj[key];
        const newKey = `${prefix}${key.charAt(0).toUpperCase() + key.slice(1)}`
        newObj[newKey] = obj[key]
      }
      return newObj;
      return newObj
    },
    // ç”ŸæˆæŽ¥å£å‚æ•°
    generateQuery(obj) {
      // éœ€è¦æ ¹æ®åœºæ™¯ç±»åž‹ç¡®å®šæŽ¥å£å‚数的属性名
      var query = this.modifyObjectKeys(obj);
      return query;
      var query = this.modifyObjectKeys(obj)
      return query
    },
    onClickPic(e, item) {
      e.stopPropagation();
    }
  }
};
      e.stopPropagation()
    },
  },
}
</script>
<style scoped>
src/views/inspection/report/ReportManage.vue
@@ -117,9 +117,9 @@
    data.push({
      index: i,
      name: `${searchForm.value.district === 'xuhui' ? '徐汇区' : '其他区'}${baseName}(${area})-2023å¹´${month}月`,
      name: `${searchForm.value.district === 'xuhui' ? '徐汇区' : '其他区'}${baseName}(${area})-2025å¹´${month}月`,
      district: searchForm.value.district === 'xuhui' ? '徐汇区' : '其他区',
      reportMonth: `2023å¹´${month}月`,
      reportMonth: `2025å¹´${month}月`,
      generateTime: new Date().toLocaleString('zh-CN'),
      auditStatus: Math.random() > 0.5 ? '已审核' : '未审核',
    })
@@ -136,7 +136,7 @@
// æŸ¥çœ‹æŠ¥å‘Š
const viewReport = (row) => {
  // ä½¿ç”¨æ¨¡æ‹Ÿçš„æŠ¥å‘Šæ–‡ä»¶
  reportUrl.value = '/徐汇区餐饮监管简报(天钥桥)-2023å¹´8月(1).docx'
  reportUrl.value = '/徐汇区餐饮监管简报(天钥桥)-2025å¹´8月(1).docx'
  drawerVisible.value = true
  prepareDocxBlob(reportUrl.value).then((blob) => {
src/views/inspection/task/TaskManage.vue
@@ -296,7 +296,7 @@
        })
        this.tasks = list.filter((e) => {
          return (
            e.data.districtname == '徐汇区' && dayjs(e.data.starttime).isBefore(dayjs('2023-12-31'))
            e.data.districtname == '徐汇区' && dayjs(e.data.starttime).isBefore(dayjs('2025-12-31'))
          )
        })
        if (list.length == 0) {
src/views/monitor/DataAnalysisAll.vue
@@ -67,8 +67,8 @@
      loading: false,
      chartData: [], //保存查询的结果
      //devId:'',          //设备编号
      begin: '2023-05-01', //开始时间
      end: '2023-05-15', //结束时间
      begin: '2025-05-01', //开始时间
      end: '2025-05-15', //结束时间
      value: ['付小姐在成都', 'qinshi_31010320210010'], //保存选择的店铺名称和设备名称
      options: [
        {
src/views/monitor/DataAnalysisConcentration.vue
@@ -65,8 +65,8 @@
      loading: false,
      chartData: [], //保存查询的结果
      devId: '', //设备编号
      begin: '2023-05-01', //开始时间
      end: '2023-05-15', //结束时间
      begin: '2025-05-01', //开始时间
      end: '2025-05-15', //结束时间
      value: ['付小姐在成都', 'qinshi_31010320210010'], //保存选择的店铺名称和设备名称
      options: [
        {
src/views/monitor/DataAnalysisOnlineRate.vue
@@ -65,8 +65,8 @@
      loading: false,
      chartData: [], //保存查询的结果
      //devId:'',          //设备编号
      begin: '2023-05-01', //开始时间
      end: '2023-05-15', //结束时间
      begin: '2025-05-01', //开始时间
      end: '2025-05-15', //结束时间
      value: ['付小姐在成都', 'qinshi_31010320210010'], //保存选择的店铺名称和设备名称
      options: [
        {
src/views/monitor/DataAnalysisOpenRate.vue
@@ -65,8 +65,8 @@
      loading: false,
      chartData: [], //保存查询的结果
      devId: '', //设备编号
      begin: '2023-05-01', //开始时间
      end: '2023-05-15', //结束时间
      begin: '2025-05-01', //开始时间
      end: '2025-05-15', //结束时间
      value: ['付小姐在成都', 'qinshi_31010320210010'], //保存选择的店铺名称和设备名称
      options: [
        {
src/views/monitor/DataAnalysisOverStandardRate.vue
@@ -65,8 +65,8 @@
      loading: false,
      chartData: [], //保存查询的结果
      //devId:'',          //设备编号
      begin: '2023-05-01', //开始时间
      end: '2023-05-15', //结束时间
      begin: '2025-05-01', //开始时间
      end: '2025-05-15', //结束时间
      value: ['付小姐在成都', 'qinshi_31010320210010'], //保存选择的店铺名称和设备名称
      options: [
        {
src/views/monitor/DataDashboard.vue
@@ -613,7 +613,7 @@
    return {
      activeTime: 'day',
      activeMode: 'pollution', // é»˜è®¤æ±¡æŸ“态势模式
      currentDate: new Date('2023-08-01'),
      currentDate: new Date('2025-08-01'),
      timeTabs: [
        { label: '日', value: 'day' },
        { label: '周', value: 'week' },
src/views/monitor/DataDashboard_old2.vue
@@ -65,7 +65,7 @@
      devices: [],
      // åˆ†åŒºæ•°æ®æŽ’名
      selectedMonth: '2023-12',
      selectedMonth: '2025-12',
      rankingType: 'hourly',
      rankingData: [],
      sortedRankingData: [],
src/views/monitor/DataException.vue
@@ -627,7 +627,7 @@
      return summary
    },
    // åŠŸèƒ½ï¼šå¯¹è¯æ¡†è¡¨æ ¼åºå·é€’å¢ž
    // æ—¶é—´ï¼š2023-8-17
    // æ—¶é—´ï¼š2025-8-17
    indexMethod(index) {
      return index + 1
    },
src/views/monitor/DataException_old.vue
@@ -612,7 +612,7 @@
  },
  methods: {
    // åŠŸèƒ½ï¼šå¯¹è¯æ¡†è¡¨æ ¼åºå·é€’å¢ž
    // æ—¶é—´ï¼š2023-8-17
    // æ—¶é—´ï¼š2025-8-17
    indexMethod(index) {
      return index + 1
    },