餐饮油烟智能监测与监管一体化平台
riku
2026-03-17 45c217996d025d256fdd0ed5cb744750e68dd36d
2026.3.17
已修改9个文件
已添加1个文件
428 ■■■■ 文件已修改
src/api/fytz/creditApi.js 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/core/AppAside.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/index.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/menu.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/MonitorControl.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/scenenew/UserInfo.vue 178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/scenenew/components/CompUserInfo.vue 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/scenenew/components/CompUserInfoAddDrawer.vue 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inspection/task/TaskManage.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fytz/creditApi.js
@@ -1,5 +1,5 @@
import { Base64 } from 'js-base64';
import { $fytz } from '../index';
import { Base64 } from 'js-base64'
import { $fytz } from '../index'
/**
 * ä¿¡ç”¨è¯„ä¼°API接口
@@ -8,20 +8,22 @@
  /**
   * ä¸‹è½½ç”¨æˆ·çŽ¯ä¿¡ç 
   * @param {*} userId
   * @param {*} userName
   */
  downloadCode(userId) {
  downloadCode(userId, userName) {
    return $fytz
      .get(`credit/ecCode/download?userId=${userId}`, { responseType: 'blob' })
      .then((res) => {
        const name = Base64.decode(res.headers.get('fileName'));
        const url = window.URL.createObjectURL(res.data);
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', name);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
      });
  }
};
        const name = res.headers.get('fileName') || userName
        const fileName = Base64.decode(name)
        const url = window.URL.createObjectURL(res.data)
        const link = document.createElement('a')
        link.href = url
        link.setAttribute('download', fileName)
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        window.URL.revokeObjectURL(url)
      })
  },
}
src/components/core/AppAside.vue
@@ -55,7 +55,7 @@
    return {
      // collapse: false,
      menuHeight: '80vh',
      title: '油烟智能一体化平台',
      title: '油烟智能监测与监管',
      subTitle: '©上海飞羽环保科技有限公司',
      appIcon: AppIcon,
    }
src/constants/index.js
@@ -1 +1,2 @@
export { MENU } from './menu'
export { envCreditCode } from './envCreditCode'
src/constants/menu.js
@@ -74,15 +74,16 @@
    name: '监管巡查',
    children: [
      {
        path: '/index/inspection/monitor-control',
        icon: 'solar:eye-scan-line-duotone',
        name: '监管监控',
      },
      {
        path: '/index/inspection/task-manage',
        icon: 'solar:file-text-line-duotone',
        name: '任务管理',
      },
      {
        path: '/index/inspection/scene-info',
        icon: 'solar:shop-2-line-duotone',
        name: '店铺管理',
      },
      {
        path: '/index/inspection/pro-check',
        icon: 'solar:checklist-minimalistic-line-duotone',
@@ -109,6 +110,11 @@
        icon: 'solar:folder-favourite-bookmark-line-duotone',
        name: '评估报告',
      },
      {
        path: '/index/inspection/scene-info',
        icon: 'solar:shop-2-line-duotone',
        name: '店铺管理',
      },
      // {
      //   path: '/index/analysis/data-product',
      //   icon: 'solar:document-add-line-duotone',
src/router/index.js
@@ -105,6 +105,11 @@
              path: 'report-manage',
              component: () => import('@/views/inspection/report/ReportManage.vue'),
            },
            {
              name: 'monitor-control',
              path: 'monitor-control',
              component: () => import('@/views/inspection/MonitorControl.vue'),
            },
          ],
        },
        {
src/views/inspection/MonitorControl.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
<template>s</template>
src/views/inspection/scenenew/UserInfo.vue
@@ -1,88 +1,89 @@
<template>
  <FYTable @search="onSearch" :row-class-name="tableRowClassName">
    <template #options>
      <FYOptionLocation
        :allOption="true"
        :level="5"
        v-model:value="formSearch._locations"
      ></FYOptionLocation>
      <FYOptionText
        label="场景名称"
        placeholder="输入名称关键字"
        v-model:value="formSearch.searchText"
      ></FYOptionText>
      <FYOptionScene
        :allOption="true"
        :type="1"
        :initValue="false"
        v-model:value="formSearch.scensetype"
      ></FYOptionScene>
      <FYOptionOnlineStatus
        :allOption="true"
        v-model:value="formSearch.online"
      ></FYOptionOnlineStatus>
    </template>
  <div class="p-h-8">
    <FYTable @search="onSearch" :row-class-name="tableRowClassName">
      <template #options>
        <FYOptionLocation
          :allOption="true"
          :level="5"
          v-model:value="formSearch._locations"
        ></FYOptionLocation>
        <FYOptionText
          label="场景名称"
          placeholder="输入名称关键字"
          v-model:value="formSearch.searchText"
        ></FYOptionText>
        <FYOptionScene
          :allOption="true"
          :type="1"
          :initValue="false"
          v-model:value="formSearch.scensetype"
        ></FYOptionScene>
        <FYOptionOnlineStatus
          :allOption="true"
          v-model:value="formSearch.online"
        ></FYOptionOnlineStatus>
      </template>
    <template #table-column>
      <el-table-column
        fixed="left"
        prop="userInfo.realname"
        label="名称"
        :show-overflow-tooltip="true"
        width="400"
      >
      </el-table-column>
      <el-table-column prop="userInfo.acountname" label="账号" width="110" />
      <el-table-column prop="sceneTypeName" label="类型" width="100" />
      <el-table-column prop="biProvinceName" label="省" width="80" />
      <el-table-column prop="biCityName" label="市" width="80" />
      <!-- <el-table-column prop="districtname" label="区县" width="90" /> -->
      <el-table-column prop="userInfo.extension1" label="区县" width="80" />
      <el-table-column prop="biTownName" label="街道" width="110" />
      <el-table-column prop="biArea" label="集中区" width="110" />
      <el-table-column prop="biManagementCompany" label="物业" min-width="110" />
      <el-table-column prop="userInfo.isenable" label="状态" width="90">
        <template #default="{ row }">
          {{ row.userInfo.isenable ? '上线中' : '已下线' }}
        </template>
      </el-table-column>
      <el-table-column prop="userInfo.usertype" label="用户类型" width="90" />
      <el-table-column fixed="right" align="right" label="操作" width="190">
        <template #header>
          <el-button icon="DocumentAdd" size="default" type="success" @click="drawer = true"
            >新增用户</el-button
          >
        </template>
        <template #default="{ row }">
          <el-space>
            <el-button
              :loading="row.loading2"
              :type="row.userInfo.isenable != '0' ? 'danger' : 'primary'"
              size="small"
              @click="itemActive(row)"
              >{{ row.userInfo.isenable != '0' ? '下线' : '上线' }}</el-button
      <template #table-column>
        <el-table-column
          fixed="left"
          prop="userInfo.realname"
          label="名称"
          :show-overflow-tooltip="true"
          width="400"
        >
        </el-table-column>
        <el-table-column prop="userInfo.acountname" label="账号" width="110" />
        <el-table-column prop="sceneTypeName" label="类型" width="100" />
        <el-table-column prop="biProvinceName" label="省" width="80" />
        <el-table-column prop="biCityName" label="市" width="80" />
        <!-- <el-table-column prop="districtname" label="区县" width="90" /> -->
        <el-table-column prop="userInfo.extension1" label="区县" width="80" />
        <el-table-column prop="biTownName" label="街道" width="110" />
        <el-table-column prop="biArea" label="集中区" width="110" />
        <el-table-column prop="biManagementCompany" label="物业" min-width="110" />
        <el-table-column prop="userInfo.isenable" label="状态" width="90">
          <template #default="{ row }">
            {{ row.userInfo.isenable ? '上线中' : '已下线' }}
          </template>
        </el-table-column>
        <el-table-column prop="userInfo.usertype" label="用户类型" width="90" />
        <el-table-column fixed="right" align="right" label="操作" width="190">
          <template #header>
            <el-button icon="DocumentAdd" size="default" type="success" @click="drawer = true"
              >新增店铺</el-button
            >
            <el-button-group>
              <el-button type="primary" :loading="row.loading1" size="small" @click="editRow(row)"
                >查看</el-button
          </template>
          <template #default="{ row }">
            <el-space>
              <el-button
                :loading="row.loading2"
                :type="row.userInfo.isenable != '0' ? 'danger' : 'primary'"
                size="small"
                @click="itemActive(row)"
                >{{ row.userInfo.isenable != '0' ? '下线' : '上线' }}</el-button
              >
              <el-dropdown @command="handleCommand" trigger="click">
                <el-button
                  type="primary"
                  :loading="row.downloadLoading"
                  size="small"
                  :icon="row.downloadLoading ? '' : 'ArrowDown'"
                ></el-button>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item icon="Download" :command="{ c: 1, p: row }"
                      >下载环信码</el-dropdown-item
                    >
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </el-button-group>
            <!-- <el-dropdown
              <el-button-group>
                <el-button type="primary" :loading="row.loading1" size="small" @click="editRow(row)"
                  >查看</el-button
                >
                <el-dropdown @command="handleCommand" trigger="click">
                  <el-button
                    type="primary"
                    :loading="row.downloadLoading"
                    size="small"
                    :icon="row.downloadLoading ? '' : 'ArrowDown'"
                  ></el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item icon="Download" :command="{ c: 1, p: row }"
                        >下载环信码</el-dropdown-item
                      >
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </el-button-group>
              <!-- <el-dropdown
              split-button
              :loading="row.loading1"
              size="small"
@@ -100,11 +101,12 @@
                </el-dropdown-menu>
              </template>
            </el-dropdown> -->
          </el-space>
        </template>
      </el-table-column>
    </template>
  </FYTable>
            </el-space>
          </template>
        </el-table-column>
      </template>
    </FYTable>
  </div>
  <CompUserInfoAddDrawer v-model="drawer"></CompUserInfoAddDrawer>
</template>
@@ -206,12 +208,12 @@
      return row.userInfo.isenable ? 'online-row' : 'offline-row'
    },
    handleCommand(e) {
      const userId = e.p.userInfo.guid
      const { guid: userId, realname: userName } = e.p.userInfo
      switch (e.c) {
        // ä¸‹è½½çŽ¯ä¿¡ç 
        case 1:
          e.p.downloadLoading = true
          creditApi.downloadCode(userId).finally(() => {
          creditApi.downloadCode(userId, userName).finally(() => {
            e.p.downloadLoading = false
          })
          break
src/views/inspection/scenenew/components/CompUserInfo.vue
@@ -17,11 +17,11 @@
          placeholder="头像url"
        />
      </el-form-item> -->
      <el-form-item label="账户名" prop="acountname">
        <el-input clearable v-model="formObj.acountname" placeholder="账户名" />
      <el-form-item label="店铺名" prop="acountname">
        <el-input clearable v-model="formObj.acountname" placeholder="店铺名" />
      </el-form-item>
      <el-form-item label="用户昵称" prop="realname">
        <el-input clearable v-model="formObj.realname" placeholder="用户昵称" />
      <el-form-item label="昵称" prop="realname">
        <el-input clearable v-model="formObj.realname" placeholder="昵称" />
      </el-form-item>
      <el-form-item label="密码" prop="password">
        <el-col :span="18">
@@ -76,7 +76,7 @@
        prop="_scenetype"
        :allOption="false"
        :type="1"
        :initValue="false"
        :initValue="true"
        v-model:value="formObj._scenetype"
      ></FYOptionScene>
    </template>
@@ -84,55 +84,55 @@
</template>
<script setup>
import { defineProps, defineEmits, reactive, ref, watch, computed } from 'vue';
import { getSceneName } from '@/enum/scene';
import userApi from '@/api/fytz/userApi';
import { useMessageBoxTip } from '@/composables/messageBox';
import { defineProps, defineEmits, reactive, ref, watch, computed } from 'vue'
import { getSceneName } from '@/enum/scene'
import userApi from '@/api/fytz/userApi'
import { useMessageBoxTip } from '@/composables/messageBox'
const props = defineProps({
  //基本信息
  model: {
    type: Object
    type: Object,
    // default: () => {
    //   return { isenable: true };
    // }
  },
  create: {
    type: Boolean,
    default: false
    default: false,
  },
  active: {
    type: Boolean,
    default: false
  }
});
    default: false,
  },
})
const formInfo = ref({ isenable: true });
const formInfo = ref({ isenable: true })
watch(
  () => props.model,
  (nValue) => {
    formInfo.value = parseUserInfo(nValue);
  }
);
    formInfo.value = parseUserInfo(nValue)
  },
)
const emit = defineEmits(['onSubmit', 'onCancel', 'updateEdit']);
const emit = defineEmits(['onSubmit', 'onCancel', 'updateEdit'])
const rules = reactive({
  acountname: [
    {
      required: true,
      message: '账户名不能为空',
      trigger: 'blur'
    }
      trigger: 'blur',
    },
  ],
  realname: [
    {
      required: true,
      message: '用户昵称不能为空',
      trigger: 'blur'
    }
  ]
      trigger: 'blur',
    },
  ],
  // password: [
  //   {
  //     required: props.create,
@@ -140,44 +140,44 @@
  //     trigger: 'blur',
  //   },
  // ],
});
})
// ç”¨æˆ·åŸºæœ¬ä¿¡æ¯æ ¼å¼åŒ–
function parseUserInfo(s) {
  if (s.usertype && s.usertypeid) {
    s._usertype = {
      label: s.usertype,
      value: s.usertypeid + ''
    };
      value: s.usertypeid + '',
    }
  }
  if (s.extension2) {
    s._scenetype = getSceneName(s.extension2, 1);
    s._scenetype = getSceneName(s.extension2, 1)
  }
  s._locations = {};
  s._locations = {}
  return s;
  return s
}
function parseUserInfoReverse(v) {
  // è¡Œæ”¿åŒºåˆ’信息填充
  const a = v._locations;
  v.extension1 = a.dName;
  const a = v._locations
  v.extension1 = a.dName
  // ç”¨æˆ·ç±»åž‹ä¿¡æ¯å¡«å……
  const b = v._usertype;
  v.usertypeid = b.value;
  v.usertype = b.label;
  const b = v._usertype
  v.usertypeid = b.value
  v.usertype = b.label
  // åœºæ™¯ç±»åž‹ä¿¡æ¯å¡«å……
  const c = v._scenetype;
  v.extension2 = c.value;
  const c = v._scenetype
  v.extension2 = c.value
  return v;
  return v
}
function createUser(v, success, fail) {
  const l = v._locations;
  const l = v._locations
  const params = {
    userInfo: v,
    baseInfo: {
@@ -190,41 +190,41 @@
      biTownCode: l.tCode,
      biTownName: l.tName,
      biAreaCode: l.aCode,
      biArea: l.aName
      biArea: l.aName,
      // biManagementCompanyId:
      // biManagementCompany:
      // biContact
      // biTelephone
      // biAddress
    }
  };
    },
  }
  return userApi
    .createUser(params)
    .then(() => {
      emit('onSubmit', params);
      success();
      emit('onSubmit', params)
      success()
    })
    .catch((err) => {
      fail(err);
    });
      fail(err)
    })
}
function updateUser(v, success, fail) {
  return userApi
    .updateUserInfo(v)
    .then(() => {
      emit('onSubmit', v);
      if (success) success();
      emit('onSubmit', v)
      if (success) success()
    })
    .catch((err) => {
      if (fail) fail(err);
    });
      if (fail) fail(err)
    })
}
function submit(v, success, fail) {
  parseUserInfoReverse(v.value);
  parseUserInfoReverse(v.value)
  return props.create ? createUser(v.value, success, fail) : updateUser(v.value, success, fail);
  return props.create ? createUser(v.value, success, fail) : updateUser(v.value, success, fail)
  // parseUserInfoReverse(props.formInfo);
  // return props.create
@@ -233,18 +233,18 @@
}
// é‡ç½®å¯†ç 
const pwLoading = ref(false);
const pwLoading = ref(false)
function onResetPw() {
  useMessageBoxTip({
    confirmMsg: '是否重置该场景密码?',
    confirmTitle: '重置密码',
    onConfirm: async () => {
      pwLoading.value = true;
      pwLoading.value = true
      return userApi.resetPassword(formInfo.value.guid).finally(() => {
        pwLoading.value = false;
      });
    }
  });
        pwLoading.value = false
      })
    },
  })
}
</script>
src/views/inspection/scenenew/components/CompUserInfoAddDrawer.vue
@@ -1,6 +1,6 @@
<template>
  <el-drawer
    title="新增用户"
    title="新增店铺"
    direction="rtl"
    :model-value="modelValue"
    @open="updateDrawer(true)"
@@ -18,8 +18,8 @@
</template>
<script>
import CompUserInfo from './CompUserInfo.vue';
import { useMessageBox } from '@/composables/messageBox';
import CompUserInfo from './CompUserInfo.vue'
import { useMessageBox } from '@/composables/messageBox'
export default {
  components: { CompUserInfo },
@@ -27,12 +27,12 @@
  emits: ['update:modelValue'],
  data() {
    return {
      drawerEdit: false
    };
      drawerEdit: false,
    }
  },
  methods: {
    updateDrawer(status) {
      this.$emit('update:modelValue', status);
      this.$emit('update:modelValue', status)
    },
    onDrawerClose(done) {
      if (this.drawerEdit) {
@@ -41,14 +41,14 @@
          confirmMsg: '是否放弃已编辑的内容?',
          confirmTitle: '关闭弹出框',
          onConfirm: () => {
            done();
          }
        });
            done()
          },
        })
      } else {
        // ç›´æŽ¥å…³é—­
        done();
        done()
      }
    },
  }
};
  },
}
</script>
src/views/inspection/task/TaskManage.vue
@@ -1,19 +1,20 @@
<template>
  <BaseContentLayout>
  <BaseContentLayout asideWidth="0">
    <template #header>
      <!-- <FYSearchBar @search="search">
        <template #options>
          <FYOptionLocation
            :allOption="true"
            :level="3"
            :checkStrictly="false"
            v-model:value="formSearch.locations"
          ></FYOptionLocation>
        </template>
      </FYSearchBar> -->
      <div class="task-switcher">
        <el-button @click="switchTask(-1)" icon="ArrowLeft">前一个</el-button>
        <el-select
          v-model="curTaskTitle"
          @change="(t) => chooseTask(tasks.find((e) => e.title == t))"
          style="width: 260px"
        >
          <el-option v-for="s in tasks" :key="s.title" :label="s.title" :value="s.title" />
        </el-select>
        <el-button @click="switchTask(1)" icon="ArrowRight">后一个</el-button>
      </div>
    </template>
    <template #aside>
      <SideList :items="tasks" :loading="sideLoading" @item-click="chooseTask"></SideList>
      <!-- <SideList :items="tasks" :loading="sideLoading" @item-click="chooseTask"></SideList> -->
    </template>
    <template #main>
      <ToolBar
@@ -24,12 +25,6 @@
        :loading="mainLoading"
      ></ToolBar>
      <div v-if="curMonitorObjList.length > 0" v-loading="mainLoading">
        <!-- <div><el-text>监管计划</el-text></div>
          <el-button type="warning" size="small" @click="editPlan"
            >计划调整</el-button
          > -->
        <!-- <el-tabs model-value="first">
            <el-tab-pane label="巡查计划" name="first"> -->
        <el-space justify="" :size="30" style="padding: 16px 0px 16px 16px">
          <el-text size="large">巡查计划</el-text>
          <el-radio-group v-model="selectedSceneType" fill="#409eff">
@@ -186,7 +181,10 @@
      // å½“前任务的展示中的监管对象
      showMonitorObjList: [],
      //当前选中的任务
      curTaskTitle: '',
      curTask: {},
      // å½“前任务索引
      currentTaskIndex: 0,
      //当前选中的日任务
      curDayTaskList: [],
      daytaskLoading: false,
@@ -304,8 +302,19 @@
        if (list.length == 0) {
          this.sideLoading = false
          this.mainLoading = false
        } else {
          this.curTaskTitle = this.tasks[this.currentTaskIndex].title
          this.chooseTask(this.tasks[this.currentTaskIndex])
        }
      })
    },
    // åˆ‡æ¢ä»»åŠ¡
    switchTask(direction) {
      if (this.tasks.length === 0) return
      this.currentTaskIndex =
        (this.currentTaskIndex + direction + this.tasks.length) % this.tasks.length
      this.chooseTask(this.tasks[this.currentTaskIndex])
    },
    //获取任务的完成情况
    getTaskType(s) {
@@ -327,6 +336,8 @@
      return type
    },
    chooseTask(task) {
      // const task = this.tasks.find((e) => e.title == taskTitle)
      this.curTaskTitle = task.title
      this.task = task
      this.sideLoading = false
      this.mainLoading = true
@@ -492,4 +503,10 @@
.el-drawer__custom {
  padding: 0px !important;
}
.task-switcher {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
}
</style>