riku
2023-12-12 a35ff1ca423e2fea33dc11f37b54d076acaab3c5
2023.12.12
已修改20个文件
已删除9个文件
已添加23个文件
已重命名1个文件
9388 ■■■■■ 文件已修改
.eslintignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintrc.cjs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintrc.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.prettierrc.json 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 4924 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fysp/problemApi.js 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/fytz/userApi.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/button.css 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/main.css 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components.d.ts 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/ProblemCard.vue 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SearchBar.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/__tests__/HelloWorld.spec.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/core/BaseContentLayout.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/core/SiderMenu.vue 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/form/FYForm.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionLocation.vue 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionOnlineStatus.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionScene.vue 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionTime.vue 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYOptionUserType.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/FYSearchBar.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/OptionLocation.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/OptionOnlineStatus.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/OptionScene.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/search-option/base/FYOptionText.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/table/FYTable.vue 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/index.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/menu.js 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/enum/location.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/enum/scene.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/enum/user.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/baseinfo/fysp/scene/CompSceneBaseInfo.vue 223 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/baseinfo/fysp/scene/SceneInfo.vue 268 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/baseinfo/fysp/user/CompUserInfo.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/check/ProCheck.vue 306 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/check/ProCheckProxy.js 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/check/proStatus.js 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/ProCheck.vue 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/ProCheckProxy.js 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompProblemCard.vue 241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/check/components/CompSubTaskStatistic.vue 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/config/ProblemType.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/DataSource.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/ResultManage.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fysp/evaluation/components/CompPreCheck.vue 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fytz/ledger/LedgerManage.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fytz/user/UserInfo.vue 343 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/fytz/user/components/CompUserInfo.vue 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
/**/*.d.ts
.eslintrc.cjs
ÎļþÒÑɾ³ý
.eslintrc.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-essential',
    'plugin:flowtype/recommended',
  ],
  overrides: [
    {
      env: {
        node: true,
      },
      files: [
        '.eslintrc.{js,cjs}',
        'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}',
      ],
      extends: ['plugin:cypress/recommended'],
      //   parserOptions: {
      //     sourceType: 'script',
      //   },
    },
  ],
  parser: 'vue-eslint-parser',
  // parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    // parser: '@typescript-eslint/parser',
    // parser: '@babel/eslint-parser',
    // ecmaFeatures: {
    //   jsx: true,
    //   modules: true,
    // },
  },
  plugins: ['vue', '@typescript-eslint'],
  rules: {
    // 'vue/comment-directive': 'off',
    'no-unused-vars': 1,
    'no-parsing-error': 0,
  },
};
.prettierrc.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
{
  "$schema": "https://json.schemastore.org/prettierrc",
  "semi": false,
  "tabWidth": 2,
  "singleQuote": true,
  "printWidth": 100,
  "trailingComma": "none"
}
package-lock.json
ÎļþÌ«´ó
package.json
@@ -13,9 +13,11 @@
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
  },
  "dependencies": {
    "@ctrl/tinycolor": "^4.0.2",
    "@element-plus/icons-vue": "^2.0.10",
    "@vueuse/core": "^9.7.0",
    "axios": "^1.2.1",
    "dayjs": "^1.11.10",
    "element-plus": "^2.2.26",
    "pinia": "^2.0.26",
    "vue": "^3.2.45",
@@ -26,6 +28,8 @@
    "@babel/core": "^7.20.5",
    "@babel/eslint-parser": "^7.19.1",
    "@babel/preset-env": "^7.20.2",
    "@typescript-eslint/eslint-plugin": "^6.10.0",
    "@typescript-eslint/parser": "^6.10.0",
    "@vitejs/plugin-vue": "^3.2.0",
    "@vue/test-utils": "^2.2.4",
    "autoprefixer": "^10.4.13",
@@ -33,10 +37,11 @@
    "eslint": "^8.22.0",
    "eslint-plugin-cypress": "^2.12.1",
    "eslint-plugin-flowtype": "^8.0.3",
    "eslint-plugin-vue": "^9.3.0",
    "eslint-plugin-vue": "^9.18.1",
    "jsdom": "^20.0.3",
    "less": "^4.1.3",
    "less-loader": "^11.1.0",
    "prettier": "^3.1.0",
    "sass": "^1.56.2",
    "start-server-and-test": "^1.14.0",
    "typescript": "^4.9.4",
src/api/fysp/problemApi.js
@@ -9,14 +9,7 @@
   * @param {Number} action 0:问题通过;1:问题不通过;2:整改通过;3整改不通过
   */
  checkProblem({ pId, action, remark = '', userId = id, userName = name }) {
    return $fysp
      .post('problemlist/check', {
        pId: pId,
        action: action,
        remark: remark,
        userId: userId,
        userName: userName,
      })
      .then((res) => res.data);
    const params = `?pId=${pId}&action=${action}&remark=${remark}&userId=${userId}&userName=${userName}`;
    return $fysp.post(`problemlist/check${params}`).then((res) => res.data);
  },
};
src/api/fytz/userApi.js
@@ -9,9 +9,9 @@
   * @param {Object} data
   * @returns
   */
  fetchUser(userId, page = 1, per_page = 20, data) {
  fetchUser(page = 1, per_page = 20, data) {
    const params = `page=${page}&per_page=${per_page}`;
    return $fytz.post(`userInfo/searchUser/${userId}?${params}`, data);
    return $fytz.post(`baseInfo/search/?${params}`, data).then((res) => res.data);
  },
  /**
src/api/index.js
@@ -1,26 +1,36 @@
import axios from 'axios';
import { ElMessage } from 'element-plus';
const ip1 = 'http://47.100.191.150:9005/';
// const ip1 = 'http://192.168.1.9:8080/';
// const ip2 = 'http://47.100.191.150:9006/';
const ip2 = 'http://192.168.1.8:8080/';
// const ip2 = 'https://fyami.com.cn/';
const debug = true;
let ip1 = 'http://47.100.191.150:9005/';
let ip1_file = 'http://47.100.191.150:9005/';
let ip2 = 'https://fyami.com.cn/';
let ip2_file = 'https://fyami.com.cn/';
if (debug) {
  // ip1 = 'http://192.168.0.123:8082/';
  // ip1_file = 'http://47.100.191.150:9005/';
  ip2 = 'http://192.168.0.138:8080/';
  // ip2_file = 'https://fyami.com.cn/';
}
// const ip2 = 'http://192.168.0.123:8080/';
//飞羽监管
const $fysp = axios.create({
  baseURL: ip1,
  timeout: 10000,
});
$fysp.imgUrl = `${ip1}images/`;
$fysp.downloadUrl = `${ip1}files/`;
$fysp.imgUrl = `${ip1_file}images/`;
$fysp.downloadUrl = `${ip1_file}files/`;
//飞羽环境
const $fytz = axios.create({
  baseURL: ip2,
  timeout: 10000,
});
$fytz.imgUrl = `${ip2}images/`;
$fytz.imgUrl = `${ip2_file}images/`;
//添加拦截器
[$fysp, $fytz].forEach((i) => {
@@ -55,7 +65,10 @@
      console.log(response);
      console.log('==>请求结束');
      if (response.status == 200) {
        if (response.data.success != undefined && response.data.success != null) {
        if (
          response.data.success != undefined &&
          response.data.success != null
        ) {
          if (response.data.success == true) {
            return response;
          } else {
src/assets/button.css
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
.fy-button-div {
  background-color: #545c64;
  color: white;
  padding: 16px 24px;
  font-size: 14px;
  cursor: pointer;
}
.fy-button-div:hover {
  /* background-color: var(--el-menu-hover-bg-color) */
  background-color: #40464d;
}
src/assets/main.css
@@ -1,4 +1,5 @@
@import './base.css';
@import './button.css';
#app {
  /* background-color: rgb(212, 41, 41); */
src/components.d.ts
@@ -10,7 +10,7 @@
    BaseContentLayout: typeof import('./components/core/BaseContentLayout.vue')['default']
    BasePanelLayout: typeof import('./components/core/BasePanelLayout.vue')['default']
    Content: typeof import('./components/core/Content.vue')['default']
    copy: typeof import('./components/search-option/OptionScene copy.vue')['default']
    copy: typeof import('./components/search-option/FYOptionScene copy.vue')['default']
    ElAside: typeof import('element-plus/es')['ElAside']
    ElAvatar: typeof import('element-plus/es')['ElAvatar']
    ElBacktop: typeof import('element-plus/es')['ElBacktop']
@@ -42,6 +42,7 @@
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElPopover: typeof import('element-plus/es')['ElPopover']
    ElRow: typeof import('element-plus/es')['ElRow']
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
    ElSelect: typeof import('element-plus/es')['ElSelect']
@@ -60,17 +61,20 @@
    ElTree: typeof import('element-plus/es')['ElTree']
    Footer: typeof import('./components/core/Footer.vue')['default']
    FormCol: typeof import('./components/layout/FormCol.vue')['default']
    FYForm: typeof import('./components/form/FYForm.vue')['default']
    FYOptionLocation: typeof import('./components/search-option/FYOptionLocation.vue')['default']
    FYOptionOnlineStatus: typeof import('./components/search-option/FYOptionOnlineStatus.vue')['default']
    FYOptionScene: typeof import('./components/search-option/FYOptionScene.vue')['default']
    FYOptionText: typeof import('./components/search-option/base/FYOptionText.vue')['default']
    FYOptionTime: typeof import('./components/search-option/FYOptionTime.vue')['default']
    FYOptionUserType: typeof import('./components/search-option/FYOptionUserType.vue')['default']
    FYSearchBar: typeof import('./components/search-option/FYSearchBar.vue')['default']
    FYTable: typeof import('./components/table/FYTable.vue')['default']
    Header: typeof import('./components/core/Header.vue')['default']
    Location: typeof import('./components/search-option/Location.vue')['default']
    LocationOption: typeof import('./components/search-option/LocationOption.vue')['default']
    MenuItems: typeof import('./components/core/MenuItems.vue')['default']
    OptionLocation: typeof import('./components/search-option/OptionLocation.vue')['default']
    OptionOnlineStatus: typeof import('./components/search-option/OptionOnlineStatus.vue')['default']
    OptionScene: typeof import('./components/search-option/OptionScene.vue')['default']
    ProblemCard: typeof import('./components/ProblemCard.vue')['default']
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
    SceneOption: typeof import('./components/search-option/SceneOption.vue')['default']
    SearchBar: typeof import('./components/SearchBar.vue')['default']
    SideList: typeof import('./components/SideList.vue')['default']
    SiderMenu: typeof import('./components/core/SiderMenu.vue')['default']
src/components/ProblemCard.vue
ÎļþÒÑɾ³ý
src/components/SearchBar.vue
@@ -1,6 +1,6 @@
<template>
  <el-row class="layout">
    <el-col :span="16">
    <el-col :span="12">
      <el-form :inline="true" :model="formSearch">
        <el-form-item label="总任务">
          <!-- <el-input v-model="formSearch.topTaskId" placeholder="总任务" /> -->
@@ -28,7 +28,7 @@
        </el-form-item>
      </el-form>
    </el-col>
    <el-col :span="8">
    <el-col :span="12">
      <el-row justify="end">
        <slot name="summary"></slot>
      </el-row>
src/components/__tests__/HelloWorld.spec.js
ÎļþÒÑɾ³ý
src/components/core/BaseContentLayout.vue
@@ -33,10 +33,11 @@
}
.el-header {
  /* height: initial; */
  padding: 0;
  height: initial;
  padding: 0 0 0px 0;
  /* background-color: rgb(216, 201, 201); */
  /* border-bottom: 1px solid var(--el-color-info-light-7); */
  margin-bottom: 4px;
}
.el-main {
src/components/core/SiderMenu.vue
@@ -10,20 +10,49 @@
    @close="handleClose"
    router
  >
    <!-- æ ‡é¢˜ -->
    <el-row ref="headerRef" class="header">
      <el-space>
        <el-avatar size="default" icon="UserFilled" />
        <div>{{ collapse ? '' : title }}</div>
      </el-space>
    </el-row>
    <!-- ç³»ç»Ÿåˆ‡æ¢æŒ‰é’® -->
    <el-popover
      ref="header2Ref"
      placement="right"
      trigger="click"
      effect="dark"
      :visible="popVisible"
    >
      <template #reference>
        <div class="fy-button-div" @click="popVisible = !popVisible">
          <div style="font-size: 12px; color: gray; margin-left: 24px; margin-bottom: 4px">
            å½“前系统
          </div>
          <el-row justify="space-between" align="middle">
            <el-space>
              <el-icon :size="16"><Open /></el-icon>
              <span>{{ sysName }}</span>
            </el-space>
            <el-icon><ArrowRight /></el-icon>
          </el-row>
        </div>
      </template>
      <div class="fy-button-div" v-for="(n, i) in sysNames" :key="i" @click="choseSys(i)">
        {{ n.name }}
      </div>
    </el-popover>
    <!-- èœå• -->
    <el-scrollbar :height="menuHeight" v-if="!collapse">
      <MenuItems :routes="routes" @navPage="navPage"> </MenuItems>
      <MenuItems :routes="menus" @navPage="navPage"> </MenuItems>
    </el-scrollbar>
    <template v-else>
      <MenuItems :routes="routes" @navPage="navPage"> </MenuItems>
      <MenuItems :routes="menus" @navPage="navPage"> </MenuItems>
    </template>
    <!-- å•†æ ‡ -->
    <el-row ref="subTitleRef" class="sub-title" justify="center">
      <el-space>{{ collapse ? '' : subTitle }}</el-space>
    </el-row>
@@ -31,221 +60,84 @@
</template>
<script>
import { MENU_FYSP, MENU_FYTZ, MENU_FYPW } from '../../constants/index'
export default {
  name: 'CoreSiderMenu',
  props: {
    collapse: {
      type: Boolean,
      default: false,
    },
      default: false
    }
  },
  emits: ['navPage'],
  data() {
    return {
      popVisible: false,
      menuHeight: '600px',
      title: '生态环境线上监管',
      subTitle: '©上海飞羽环保科技有限公司',
      routes: [
        {
          icon: 'Search',
          name: '巡查审核',
          children: [
            {
              path: '/procheck',
              icon: 'Search',
              name: '问题审核',
            },
            // {
            //   path: '/changecheck',
            //   icon: 'Search',
            //   name: '整改审核',
            // },
          ],
        },
        // {
        //   path: '/ledger',
        //   icon: 'Search',
        //   name: '台账审核',
        // },
        // {
        //   icon: 'Search',
        //   name: '业务分析',
        //   children: [
        //     {
        //       path: '/analysis/profollow',
        //       icon: 'Search',
        //       name: '问题动态跟踪',
        //     },
        //     {
        //       path: '/analysis/proanalysis',
        //       icon: 'Search',
        //       name: '问题整改分析',
        //     },
        //     {
        //       path: '/analysis/standardjudge',
        //       icon: 'Search',
        //       name: '规范性评估',
        //     },
        //   ],
        // },
        // {
        //   path: '/dailyreport',
        //   icon: 'Search',
        //   name: '日报管理',
        // },
        // {
        //   icon: 'Search',
        //   name: '场景报告',
        //   children: [
        //     {
        //       path: '/scenereport/construction',
        //       icon: 'Search',
        //       name: '工地',
        //     },
        //     {
        //       path: '/scenereport/wharf',
        //       icon: 'Search',
        //       name: '码头',
        //     },
        //     {
        //       path: '/scenereport/mixing',
        //       icon: 'Search',
        //       name: '搅拌站',
        //     },
        //     {
        //       path: '/scenereport/storage',
        //       icon: 'Search',
        //       name: '堆场',
        //     },
        //   ],
        // },
        {
          path: '/notice',
          icon: 'Search',
          name: '通知管理',
        },
        {
          icon: 'Search',
          name: '用户管理',
          children: [
            {
              group: true,
              name: '飞羽监管',
              children: [
                // {
                //   path: '/fysp/userInfo',
                //   name: '账户信息',
                // },
                {
                  path: '/fysp/sceneInfo',
                  name: '场景信息',
                },
              ],
            },
            {
              group: true,
              name: '飞羽环境',
              children: [
                {
                  path: '/fytz/userInfo',
                  name: '账户信息',
                },
              ],
            },
          ],
        },
        {
          icon: 'Search',
          name: '业务分析',
          children: [
            {
              group: true,
              name: '飞羽监管',
              children: [
                {
                  path: '/fysp/userInfo',
                  name: '账户信息',
                },
                {
                  path: '/fysp/sceneInfo',
                  name: '场景信息',
                },
              ],
            },
            {
              group: true,
              name: '飞羽环境',
              children: [
                // {
                //   path: '/fytz/userInfo',
                //   name: '账户信息',
                // },
              ],
            },
          ],
        },
        // {
        //   path: '/scenereport/storage',
        //   icon: 'Search',
        //   name: '排污抽运',
        // },
        // {
        //   path: '/common/userMatch',
        //   icon: 'Connection',
        //   name: '账户匹配',
        // },
        {
          icon: 'Search',
          name: '资源管理',
          children: [
            {
              path: '/fytz/enforceCase',
              icon: 'Search',
              name: '督察案例',
            },
            // {
            //   path: '/changecheck',
            //   icon: 'Search',
            //   name: '整改审核',
            // },
          ],
        },
      ],
    };
      sysIndex: 0,
      sysNames: [
        { name: '飞羽监管', des: '' },
        { name: '飞羽环境', des: '' },
        { name: '排污抽运', des: '' }
      ]
    }
  },
  computed: {
    homePage() {
      return this.routes[0].children
        ? this.routes[0].children[0].path
        : this.routes[0].path;
      const paths = this.menuPath(this.menus[0])
      return paths[paths.length - 1].path
    },
    sysName() {
      return this.sysNames[this.sysIndex].name
    },
    menus() {
      return [MENU_FYSP, MENU_FYTZ, MENU_FYPW][this.sysIndex]
    }
  },
  methods: {
    handleOpen() {},
    handleClose() {},
    choseSys(i) {
      this.sysIndex = i
      const paths = this.menuPath(this.menus[0])
      this.navPage(...paths)
      const p = paths[paths.length - 1].path
      this.$router.push(p)
      this.popVisible = false
    },
    navPage(...item) {
      const titles = item.map((value) => {
        return value.name;
      });
      this.$emit('navPage', titles);
        return value.name
      })
      this.$emit('navPage', titles)
    },
    calMenuHeight() {
      const h1 = this.$refs.headerRef.$el.offsetHeight;
      const h2 = this.$refs.subTitleRef.$el.offsetHeight;
      return `calc(100vh - ${h1}px - ${h2}px)`;
      const h1 = this.$refs.headerRef.$el.offsetHeight
      const h2 = this.$refs.header2Ref.$el.offsetHeight
      const h3 = this.$refs.subTitleRef.$el.offsetHeight
      return `calc(100vh - ${h1}px - ${h2}px - ${h3}px)`
    },
    menuPath(m) {
      if (m.children) {
        const arr = this.menuPath(m.children)
        arr.shift(m)
        return arr
      } else {
        return [m]
      }
    }
  },
  created() {
    this.$router.push(this.homePage);
    const child = this.routes[0].children
      ? this.routes[0].children[0]
      : undefined;
    this.navPage(this.routes[0], child);
    this.$router.push(this.homePage)
    this.navPage(...this.menuPath(this.menus[0]))
  },
  mounted() {
    this.menuHeight = this.calMenuHeight();
  },
};
    this.menuHeight = this.calMenuHeight()
  }
}
</script>
<style scoped>
@@ -253,7 +145,7 @@
  color: white;
  background-color: #454f5a;
  padding: 10px;
  margin-bottom: 10px;
  /* margin-bottom: 10px; */
  box-shadow: 0 0.4rem 0.7rem 0 #5d656e;
  white-space: nowrap;
  overflow: hidden;
src/components/form/FYForm.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
<!-- åœºæ™¯åŸºæœ¬ä¿¡æ¯ç¼–辑 -->
<template>
  <el-form
    :inline="false"
    :model="formObj"
    ref="formRef"
    :rules="rules"
    label-position="right"
    label-width="150px"
  >
    <slot name="form-item" :formObj="formObj"></slot>
    <el-form-item>
      <el-button
        :disabled="!edit"
        type="primary"
        @click="onSubmit"
        :loading="loading"
        >提交</el-button
      >
      <el-button :disabled="!edit" @click="onReset">重置</el-button>
      <el-button v-if="enableCancelBtn" @click="onCancel">取消</el-button>
    </el-form-item>
  </el-form>
</template>
<script setup>
/**
 * è¡¨å•编辑基类
 * ä½¿ç”¨è¯´æ˜Žï¼š
 * åœ¨æ’æ§½<slot name="form-item">中添加自定义<el-form-item>元素
 * å¯ä¼ å…¥åˆå§‹è¡¨å•数据formInfo,表单校验规则rules
 * å®žçްsubmit和cancel触发函数
 */
import { defineProps, defineEmits, reactive, ref, watch } from 'vue';
import { useFormConfirm } from '@/composables/formConfirm';
const props = defineProps({
  //表单基本信息
  formInfo: Object,
  //表单检验规则
  rules: Object,
  //取消按钮是否可用
  enableCancelBtn: Boolean,
  //触发重置
  reset: Boolean,
  //通知编辑状态
  isEdit: Boolean,
});
//触发函数,提交和取消
const emit = defineEmits(['submit', 'cancel', 'update:isEdit']);
//表单操作函数
const { formObj, formRef, edit, onSubmit, onCancel, onReset } = useFormConfirm({
  submit: {
    do: submit,
  },
  cancel: {
    do: cancel,
  },
});
//加载状态
const loading = ref(false);
//提交按钮触发
function submit() {
  loading.value = true;
  return new Promise((resolve, reject) => {
    emit('submit', formObj, () => {
      loading.value = false;
      resolve();
    });
  });
}
//取消按钮触发
function cancel() {
  emit('cancel');
}
//监听表单初始数据传入
watch(
  () => props.formInfo,
  (nValue) => {
    formObj.value = nValue;
  }
);
//监听表单重置功能触发
watch(
  () => props.reset,
  (nValue) => {
    if (nValue) {
      onReset();
    }
  }
);
//监听表单编辑状态
watch(edit, (nValue) => {
  emit('update:isEdit', nValue);
});
</script>
<style scoped></style>
src/components/search-option/FYOptionLocation.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,161 @@
<template>
  <el-form-item :label="placeholder">
    <el-cascader
      v-model="selectedOptions"
      :options="locations"
      :placeholder="placeholder"
      :props="optionProps"
      style="width: 280px"
    />
  </el-form-item>
</template>
<script>
import { enumLocation } from '@/enum/location'
// è®°å½•选项是否是自定义传入的
let customValue = true
export default {
  props: {
    // æ˜¯å¦åœ¨é¦–选项处添加“全部”选项
    allOption: {
      type: Boolean,
      default: true
    },
    // æŸ¥è¯¢çš„行政级别,取值1,2,3,4
    level: {
      type: Number,
      default: 4
    },
    // ç»“果返回
    value: Object,
    // æ˜¯å¦é»˜è®¤è¿”回初始选项
    initValue: {
      type: Boolean,
      default: true
    },
    // èƒ½å¦é€‰æ‹©ä»»æ„ä¸€çº§é€‰é¡¹
    checkStrictly: {
      type: Boolean,
      default: true
    }
  },
  emits: ['update:value'],
  data() {
    return {
      locations: enumLocation(this.allOption, this.level),
      selectedOptions: [],
      optionProps: {
        checkStrictly: this.checkStrictly
      }
    }
  },
  computed: {
    placeholder() {
      const list = '省/市/区/镇'.split('/')
      const p = []
      for (let i = 0; i < this.level; i++) {
        p.push(list[i])
      }
      return p.join('/')
    }
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          const res = this.optionFormat(nVal)
          // æ ‡æ˜Žæ­¤æ—¶çš„value更新是组件内部触发
          customValue = false
          this.$emit('update:value', res)
        }
      },
      deep: true
    },
    value: {
      handler(nVal, oVal) {
        // åªæœ‰å½“值是外部传入时,才触发更新
        if (!customValue) {
          customValue = true
          return
        }
        if (nVal != oVal) {
          if (nVal || nVal.length > 0) {
            this.selectedOptions = this.optionFormatReverse(nVal);
          }
        }
      },
      deep: true,
    },
  },
  methods: {
    /**
     * åœ°åŒºé€‰é¡¹ç»“果格式化
     */
    optionFormat(val) {
      const res = {
        pCode: null,
        pName: null,
        cCode: null,
        cName: null,
        dCode: null,
        dName: null,
        tCode: null,
        tName: null
      }
      if (val.length > 0) {
        res.pCode = val[0][0]
        res.pName = val[0][1]
      }
      if (val.length > 1) {
        res.cCode = val[1][0]
        res.cName = val[1][1]
      }
      if (val.length > 2) {
        res.dCode = val[2][0]
        res.dName = val[2][1]
      }
      if (val.length > 3) {
        res.tCode = val[3][0]
        res.tName = val[3][1]
      }
      return res
    },
    optionFormatReverse(val) {
      const res = []
      if (val.pCode) {
        res.push([val.pCode, val.pName])
      }
      if (val.cCode) {
        res.push([val.cCode, val.cName])
      }
      if (val.dCode) {
        res.push([val.dCode, val.dName])
      }
      if (val.tCode) {
        res.push([val.tCode, val.tName])
      }
      return res
    }
  },
  mounted() {
    if (this.initValue) {
      if (this.checkStrictly) {
        this.selectedOptions = [this.locations[0].value]
      } else {
        const f = (location) => {
          if (location.children && location.children.length > 0) {
            const r = f(location.children[0])
            r.unshift(location.value)
            return r
          } else {
            return [location.value]
          }
        }
        this.selectedOptions = f(this.locations[0])
      }
    }
  }
}
</script>
src/components/search-option/FYOptionOnlineStatus.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<template>
  <el-form-item label="上线状态">
    <el-select
      v-model="selectedOptions"
      placeholder="上线状态"
      style="width: 75px"
    >
      <el-option
        v-for="s in onlineStatus"
        :key="s.value"
        :label="s.label"
        :value="s"
      />
    </el-select>
  </el-form-item>
</template>
<script>
import { enumOnlineStatus } from '@/enum/onlineStatus';
export default {
  props: {
    // æ˜¯å¦åœ¨é¦–选项处添加“全部”选项
    allOption: {
      type: Boolean,
      default: true,
    },
    // è¿”回结果
    value: Object,
    // æ˜¯å¦é»˜è®¤è¿”回初始选项
    initValue: {
      type: Boolean,
      default: true,
    },
  },
  emits: ['update:value'],
  data() {
    return {
      onlineStatus: enumOnlineStatus(),
      selectedOptions: {},
    };
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal);
        }
      },
      deep: true,
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal;
        }
      },
      deep: true,
    },
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = this.onlineStatus[0];
    }
  },
};
</script>
src/components/search-option/FYOptionScene.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
<template>
  <el-form-item label="场景类型">
    <el-select
      v-model="selectedOptions"
      placeholder="场景类型"
      style="width: 150px"
    >
      <el-option
        v-for="s in sceneTypes"
        :key="s.value"
        :label="s.label"
        :value="s"
      />
    </el-select>
  </el-form-item>
</template>
<script>
import { enumScene } from '@/enum/scene';
export default {
  props: {
    // æ˜¯å¦åœ¨é¦–选项处添加“全部”选项
    allOption: {
      type: Boolean,
      default: true,
    },
    // 1:飞羽环境系统;2:飞羽监管系统;
    type: {
      type: Number,
      default: 1,
    },
    // è¿”回结果
    value: Object,
    // æ˜¯å¦é»˜è®¤è¿”回初始选项
    initValue: {
      type: Boolean,
      default: true,
    },
  },
  emits: ['update:value'],
  data() {
    return {
      sceneTypes: enumScene(this.type, this.allOption),
      selectedOptions: {},
    };
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal);
        }
      },
      deep: true,
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal;
        }
      },
      deep: true,
    },
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = this.sceneTypes[0];
    }
  },
};
</script>
src/components/search-option/FYOptionTime.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
<template>
  <el-form-item label="时间">
    <el-date-picker
      v-model="selectedOptions"
      type="month"
      placeholder="选择时间"
      style="width: 150px"
    />
  </el-form-item>
</template>
<script>
import dayjs from 'dayjs'
const MONTH = 'month'
export default {
  props: {
    type: {
      type: String,
      default: MONTH
    },
    // è¿”回结果
    value: Date,
    // æ˜¯å¦é»˜è®¤è¿”回初始选项
    initValue: {
      type: Boolean,
      default: true
    }
  },
  emits: ['update:value'],
  data() {
    return {
      selectedOptions: ''
    }
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal)
        }
      }
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal
        }
      }
    }
  },
  methods: {
    timeFormat() {
      switch (this.type) {
        case MONTH:
          return 'YYYY-MM'
        default:
          return 'YYYY-MM'
      }
    }
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = new Date()
    }
  }
}
</script>
src/components/search-option/FYOptionUserType.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<template>
  <el-form-item label="用户类型">
    <el-select
      v-model="selectedOptions"
      placeholder="用户类型"
      style="width: 150px"
    >
      <el-option
        v-for="s in userTypes"
        :key="s.value"
        :label="s.label"
        :value="s"
      />
    </el-select>
  </el-form-item>
</template>
<script>
import { enumUser } from '@/enum/user';
export default {
  props: {
    // æ˜¯å¦åœ¨é¦–选项处添加“全部”选项
    allOption: {
      type: Boolean,
      default: true,
    },
    // è¿”回结果
    value: Object,
    // æ˜¯å¦é»˜è®¤è¿”回初始选项
    initValue: {
      type: Boolean,
      default: true,
    },
  },
  emits: ['update:value'],
  data() {
    return {
      userTypes: enumUser(this.allOption),
      selectedOptions: {},
    };
  },
  watch: {
    selectedOptions: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.$emit('update:value', nVal);
        }
      },
      deep: true,
    },
    value: {
      handler(nVal, oVal) {
        if (nVal != oVal) {
          this.selectedOptions = nVal;
        }
      },
      deep: true,
    },
  },
  mounted() {
    if (this.initValue) {
      this.selectedOptions = this.userTypes[0];
    }
  },
};
</script>
src/components/search-option/FYSearchBar.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
<template>
  <el-form :inline="true">
    <slot name="options"></slot>
    <el-form-item>
      <el-button icon="Search" type="primary" @click="search">{{ btnText }}</el-button>
    </el-form-item>
  </el-form>
</template>
<script>
export default {
  props: {
    btnText: {
      type: String,
      default: '查询'
    }
  },
  emits: ['search'],
  methods: {
    search() {
      this.$emit('search')
    }
  }
}
</script>
src/components/search-option/OptionLocation.vue
ÎļþÒÑɾ³ý
src/components/search-option/OptionOnlineStatus.vue
ÎļþÒÑɾ³ý
src/components/search-option/OptionScene.vue
ÎļþÒÑɾ³ý
src/components/search-option/base/FYOptionText.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
<template>
  <el-form-item :label="label">
    <el-input clearable v-model="searchText" :placeholder="placeholder" />
  </el-form-item>
</template>
<script>
export default {
  props: {
    label: {
      type: String,
      default: '查询项',
    },
    placeholder: {
      type: String,
      default: '输入搜索内容',
    },
    // è¿”回结果
    value: String,
  },
  emits: ['update:value'],
  data() {
    return {
      searchText: '',
    };
  },
  watch: {
    searchText(nVal, oVal) {
      if (nVal != oVal) {
        this.$emit('update:value', nVal);
      }
    },
    value(nVal, oVal) {
      if (nVal != oVal) {
        this.searchText = nVal;
      }
    },
  },
};
</script>
src/components/table/FYTable.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,134 @@
<template>
  <el-row ref="searchRef">
    <FYSearchBar @search="onSearch">
      <template #options>
        <slot name="options"></slot>
      </template>
    </FYSearchBar>
  </el-row>
  <el-table
    :data="tableData"
    v-loading="loading"
    table-layout="fixed"
    :row-class-name="tableRowClassName"
    :height="tableHeight"
  >
    <slot name="table-column"></slot>
  </el-table>
  <el-pagination
    ref="paginationRef"
    class="el-pagination"
    v-model:current-page="currentPage"
    v-model:page-size="pageSize"
    :page-sizes="[10, 20, 50, 100]"
    :background="true"
    layout="total, sizes, prev, pager, next, jumper"
    :total="total"
  />
</template>
<script>
/**
 * æ•°æ®è¡¨æ ¼ç»„ä»¶
 * å¸ƒå±€ä»Žä¸Šå¾€ä¸‹åŒ…括了三个部分
 * 1. æŸ¥è¯¢æ¡ä»¶FYSearchBar
 * 2. æ•°æ®è¡¨æ ¼el-table
 * 3. åˆ†é¡µç»„ä»¶el-pagination
 * ä½¿ç”¨æ—¶éœ€è¦åœ¨<slot #options>中添加自定义查询选项,在<slot #table-column>中添加自定义表格列,同时实现触发函数search
 */
export default {
  props: {
    rowClassName: undefined,
  },
  data() {
    return {
      tableHeight: '500',
      tableData: [],
      total: 0,
      currentPage: 1,
      pageSize: 20,
      loading: false,
    };
  },
  emits: ['search'],
  watch: {
    currentPage(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch();
      }
    },
    pageSize(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch();
      }
    },
  },
  methods: {
    /**
     * è¡¨æ ¼æ•°æ®æŸ¥è¯¢ï¼Œä¼ é€’两组参数,分页信息和回调函数
     * åˆ†é¡µä¿¡æ¯åŒ…括当前页码currentPage和每页数据量pageSize
     * å›žè°ƒå‡½æ•°æŽ¥æ”¶ä¸€ä¸ªå¯¹è±¡ï¼ŒåŒ…括表格数据数组data和数据总数total
     */
    onSearch() {
      this.loading = true;
      this.$emit(
        'search',
        {
          currentPage: this.currentPage,
          pageSize: this.pageSize,
        },
        (res) => {
          this.tableData = res.data;
          this.total = res.total;
          this.loading = false;
        }
      );
    },
    calcTableHeight() {
      const h1 = this.$refs.searchRef.$el.offsetHeight;
      const h2 = this.$refs.paginationRef.$el.offsetHeight;
      // return `calc(100vh - ${h1}px - ${h2}px - var(--el-main-padding) * 2 - var(--el-header-height))`;
      return `calc(100vh - ${h1}px - ${h2}px - 60px - var(--el-main-padding) * 2)`;
    },
    tableRowClassName({ row }) {
      if (this.rowClassName) {
        if (typeof this.rowClassName == 'string') {
          return this.rowClassName;
        } else if (typeof this.rowClassName == 'function') {
          return this.rowClassName({ row });
        }
      } else {
        return row.extension1 != '0' ? 'online-row' : 'offline-row';
      }
    },
  },
  mounted() {
    this.tableHeight = this.calcTableHeight();
    this.onSearch();
  },
};
</script>
<style>
.el-table .online-row {
  /* background-color: rgb(4, 202, 21); */
}
.el-table .offline-row {
  background-color: var(--el-disabled-bg-color);
  color: var(--el-disabled-text-color);
}
.el-table .cell {
  white-space: nowrap;
}
.el-pagination {
  background-color: var(--el-color-white);
  padding-top: 20px;
  border-top: 1px solid rgba(0, 0, 0, 0.096);
  /* margin-top: 2px; */
}
</style>
src/constants/index.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
export { MENU_FYSP, MENU_FYTZ, MENU_FYPW } from './menu'
src/constants/menu.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,167 @@
const MENU_COMMON = [
  {
    path: '/common/userMatch',
    icon: 'Connection',
    name: '账户匹配'
  }
]
const MENU_FYSP = [
  // {
  //   icon: 'Search',
  //   name: '巡查审核',
  //   children: [
  //   ]
  // },
  {
    path: '/fysp/procheck',
    icon: 'CircleCheck',
    name: '问题审核'
  },
  // {
  //   path: '/changecheck',
  //   icon: 'Search',
  //   name: '整改审核',
  // },
  {
    path: '/fysp/sceneInfo',
    icon: 'Files',
    name: '场景信息'
  },
  {
    icon: 'DocumentChecked',
    name: '自动评估',
    children: [
      {
        path: '/fysp/evaluation/datasource',
        icon: 'MessageBox',
        name: '评估数据源',
      },
      {
        path: '/fysp/evaluation/resultManage',
        icon: 'Tickets',
        name: '评估管理',
      },
    ],
  },
  {
    icon: 'Setting',
    name: '配置管理',
    children: [
      {
        path: '/fysp/config/problemType',
        icon: 'List',
        name: '监管问题',
      },
    ],
  },
  // {
  //   icon: 'Search',
  //   name: '业务分析',
  //   children: [
  //     {
  //       path: '/analysis/profollow',
  //       icon: 'Search',
  //       name: '问题动态跟踪',
  //     },
  //     {
  //       path: '/analysis/proanalysis',
  //       icon: 'Search',
  //       name: '问题整改分析',
  //     },
  //     {
  //       path: '/analysis/standardjudge',
  //       icon: 'Search',
  //       name: '规范性评估',
  //     },
  //   ],
  // },
  // {
  //   path: '/dailyreport',
  //   icon: 'Search',
  //   name: '日报管理',
  // },
  // {
  //   icon: 'Search',
  //   name: '场景报告',
  //   children: [
  //     {
  //       path: '/scenereport/construction',
  //       icon: 'Search',
  //       name: '工地',
  //     },
  //     {
  //       path: '/scenereport/wharf',
  //       icon: 'Search',
  //       name: '码头',
  //     },
  //     {
  //       path: '/scenereport/mixing',
  //       icon: 'Search',
  //       name: '搅拌站',
  //     },
  //     {
  //       path: '/scenereport/storage',
  //       icon: 'Search',
  //       name: '堆场',
  //     },
  //   ],
  // },
  // {
  //   icon: 'Search',
  //   name: '用户管理',
  //   children: [
  //     {
  //       group: true,
  //       name: '飞羽监管',
  //       children: [
  //       ]
  //     },
  //   ]
  // },
  ...MENU_COMMON
]
const MENU_FYTZ = [
  {
    path: '/fytz/ledger',
    icon: 'Search',
    name: '台账审核'
  },
  {
    path: '/fytz/notice',
    icon: 'Message',
    name: '通知管理'
  },
  {
    path: '/fytz/userInfo',
    icon: 'User',
    name: '账户信息'
  },
  {
    icon: 'Search',
    name: '资源管理',
    children: [
      {
        path: '/fytz/enforceCase',
        icon: 'Search',
        name: '督察案例'
      }
    ]
  },
  ...MENU_COMMON
]
const MENU_FYPW = [
  {
    path: '/scenereport/storage',
    icon: 'Search',
    name: '排污抽运'
  }
]
export { MENU_FYSP, MENU_FYTZ, MENU_FYPW, MENU_COMMON }
src/enum/location.js
@@ -18,7 +18,7 @@
  return [
    {
      label: '全部',
      value: ['0', '全部'],
      value: [null, '全部'],
    },
    {
      label: '上海市',
src/enum/scene.js
@@ -24,7 +24,7 @@
}
function getSceneName(value, type = 1) {
  enumScene(type).find((v) => {
  return enumScene(type).find((v) => {
    if (v.value == value) {
      return v;
    }
@@ -36,7 +36,7 @@
  return [
    {
      label: '全部',
      value: '0',
      value: null,
    },
    {
      label: '餐饮',
@@ -74,7 +74,7 @@
  return [
    {
      label: '全部',
      value: '0',
      value: null,
    },
    {
      label: '工地',
src/enum/user.js
@@ -1,6 +1,15 @@
// åŒºåŽ¿æžšä¸¾
function enumUser() {
function enumUser(allOption = true) {
  const l = _enumUser();
  if (!allOption) {
    l.shift();
  }
  return l;
}
function _enumUser() {
  return [
    {
      label: '全部',
@@ -25,10 +34,4 @@
  ];
}
function enumUserNA() {
  const l = enumUser();
  l.shift();
  return l;
}
export { enumUser, enumUserNA };
export { enumUser };
src/router/index.js
@@ -1,133 +1,142 @@
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router';
import pinia from "../stores/index";
import { useLoadingStore } from "../stores/loadingStore";
// eslint-disable-next-line no-unused-vars
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
import pinia from '../stores/index'
import { useLoadingStore } from '../stores/loadingStore'
const routes = [
  {
    //问题审核
    name: 'procheck',
    path: '/procheck',
    component: () => import('@/views/check/ProCheck.vue'),
  },
  {
    //整改审核
    name: 'changecheck',
    path: '/changecheck',
    component: () => import('@/views/check/ChangeCheck.vue'),
  },
  {
    //台账审核
    name: 'ledger',
    path: '/ledger',
    component: () => import('@/views/ledger/LedgerManage.vue'),
    component: () => import('@/views/check/ChangeCheck.vue')
  },
  {
    //问题动态跟踪
    name: 'profollow',
    path: '/analysis/profollow',
    component: () => import('@/views/analysis/ProFollow.vue'),
    component: () => import('@/views/analysis/ProFollow.vue')
  },
  {
    //问题整改分析
    name: 'proanalysis',
    path: '/analysis/proanalysis',
    component: () => import('@/views/analysis/ProAnalysis.vue'),
    component: () => import('@/views/analysis/ProAnalysis.vue')
  },
  {
    //规范性评估
    name: 'standardjudge',
    path: '/analysis/standardjudge',
    component: () => import('@/views/analysis/StandardJudge.vue'),
    component: () => import('@/views/analysis/StandardJudge.vue')
  },
  {
    //日报管理
    name: 'dailyreport',
    path: '/dailyreport',
    component: () => import('@/views/dailyreport/DailyReport.vue'),
    component: () => import('@/views/dailyreport/DailyReport.vue')
  },
  {
    //场景报告-工地
    name: 'construction',
    path: '/scenereport/construction',
    component: () => import('@/views/scenereport/ConstructionReport.vue'),
    component: () => import('@/views/scenereport/ConstructionReport.vue')
  },
  {
    //场景报告-码头
    name: 'wharf',
    path: '/scenereport/wharf',
    component: () => import('@/views/scenereport/WharfReport.vue'),
    component: () => import('@/views/scenereport/WharfReport.vue')
  },
  {
    //场景报告-搅拌站
    name: 'mixing',
    path: '/scenereport/mixing',
    component: () => import('@/views/scenereport/MixingReport.vue'),
    component: () => import('@/views/scenereport/MixingReport.vue')
  },
  {
    //场景报告-堆场
    name: 'storage',
    path: '/scenereport/storage',
    component: () => import('@/views/scenereport/StorageReport.vue'),
  },
  {
    //通知管理
    name: 'notice',
    path: '/notice',
    component: () => import('@/views/notice/NoticeManage.vue'),
    component: () => import('@/views/scenereport/StorageReport.vue')
  },
  /**********************************飞羽监管***********************************************/
  {
    //问题审核
    name: 'procheck',
    path: '/fysp/procheck',
    component: () => import('@/views/fysp/check/ProCheck.vue')
  },
  {
    //账户管理
    name: 'fyspUser',
    path: '/fysp/userInfo',
    component: () => import('@/views/baseinfo/fysp/user/UserInfo.vue'),
    component: () => import('@/views/baseinfo/fysp/user/UserInfo.vue')
  },
  // {
  //   //场景管理
  //   name: 'fyspSceneManage',
  //   path: '/fysp/sceneManage/',
  //   component: () => import('@/views/user/fysp/SceneManage.vue'),
  //   children: [
  //   ]
  // },
  {
    //监管问题
    name: 'fyspProblemType',
    path: '/fysp/config/problemType',
    component: () => import('@/views/fysp/config/ProblemType.vue')
  },
  {
    //评估数据源
    name: 'fyspDatasource',
    path: '/fysp/evaluation/datasource',
    component: () => import('@/views/fysp/evaluation/DataSource.vue')
  },
  {
    //评估管理
    name: 'fyspResultManage',
    path: '/fysp/evaluation/resultManage',
    component: () => import('@/views/fysp/evaluation/ResultManage.vue')
  },
  {
    //场景信息
    name: 'fyspSceneInfo',
    path: '/fysp/sceneInfo',
    component: () => import('@/views/baseinfo/fysp/scene/SceneInfo.vue'),
    meta: { keepAlive: true },
    meta: { keepAlive: true }
  },
  {
    //场景编辑
    name: 'fyspSceneEdit',
    path: '/fysp/sceneEdit/:sid',
    component: () => import('@/views/baseinfo/fysp/scene/SceneEdit.vue'),
    meta: { transition: 'slide-left' },
    meta: { transition: 'slide-left' }
  },
  /**********************************飞羽环境***********************************************/
  {
    //台账审核
    name: 'ledger',
    path: '/fytz/ledger',
    component: () => import('@/views/fytz/ledger/LedgerManage.vue')
  },
  {
    //通知管理
    name: 'notice',
    path: '/fytz/notice',
    component: () => import('@/views/notice/NoticeManage.vue')
  },
  {
    //账户管理
    name: 'fytzUser',
    path: '/fytz/userInfo',
    component: () => import('@/views/fytz/user/UserInfo.vue'),
    meta: { keepAlive: true },
    meta: { keepAlive: true }
  },
  {
    //账户编辑
    name: 'fytzUserEdit',
    path: '/fytz/userEdit/:userId',
    component: () => import('@/views/fytz/user/UserEdit.vue'),
    meta: { transition: 'slide-left' },
    meta: { transition: 'slide-left' }
  },
  {
    //环保督察案例
    name: 'enforceCase',
    path: '/fytz/enforceCase',
    component: () => import('@/views/fytz/enforce-case/EnforceCase.vue'),
    component: () => import('@/views/fytz/enforce-case/EnforceCase.vue')
  },
  /**********************************通用模块***********************************************/
@@ -135,19 +144,20 @@
    //账户匹配
    name: 'userMatch',
    path: '/common/userMatch',
    component: () => import('@/views/common/UserMatch.vue'),
  },
];
    component: () => import('@/views/common/UserMatch.vue')
  }
]
const router = createRouter({
  // history: createWebHistory(import.meta.env.BASE_URL)
  history: createWebHashHistory(),
  routes: routes,
});
  routes: routes
})
const loadingStore = useLoadingStore(pinia)
// eslint-disable-next-line no-unused-vars
router.afterEach((to, from) => {
  loadingStore.clearLoading()
})
export { router, routes };
export { router, routes }
src/views/baseinfo/fysp/scene/CompSceneBaseInfo.vue
@@ -1,104 +1,93 @@
<!-- åœºæ™¯åŸºæœ¬ä¿¡æ¯ç¼–辑 -->
<template>
  <el-form
    :inline="false"
    :model="formObj"
    ref="formRef"
  <FYForm
    :form-info="_formInfo"
    :rules="rules"
    label-position="right"
    label-width="150px"
    @submit="submit"
    @cancel="cancel"
  >
    <el-form-item label="场景名称" prop="name">
      <el-input
        clearable
        show-word-limit
        v-model="formObj.name"
        placeholder="场景名称"
      />
    </el-form-item>
    <el-form-item label="场景类型" prop="_typeObj">
      <el-select v-model="formObj._typeObj" placeholder="场景类型">
        <el-option
          v-for="s in sceneTypes"
          :key="s.value"
          :label="s.label"
          :value="s"
    <template #form-item="{ formObj }">
      <el-form-item label="场景名称" prop="name">
        <el-input
          clearable
          show-word-limit
          v-model="formObj.name"
          placeholder="场景名称"
        />
      </el-select>
    </el-form-item>
    <el-form-item label="省/市/区/镇" prop="_locations">
      <el-cascader
        v-model="formObj._locations"
        :options="locations"
        placeholder="省/市/区/镇"
        style="width: 300px"
        :props="cascaderProps"
      />
    </el-form-item>
    <el-form-item label="地址" prop="location">
      <el-input
        show-word-limit
        clearable
        v-model="formObj.location"
        placeholder="地址"
      />
    </el-form-item>
    <el-form-item label="经度" prop="longitude">
      <!-- <el-input type="number" v-model="formObj.longitude" placeholder="经度" /> -->
      <el-input-number
        v-model="formObj.longitude"
        :precision="6"
        :step="0.1"
        controls-position="right"
      />
    </el-form-item>
    <el-form-item label="纬度" prop="latitude">
      <!-- <el-input type="number" v-model="formObj.latitude" placeholder="纬度" /> -->
      <el-input-number
        v-model="formObj.latitude"
        :precision="6"
        :step="0.1"
        controls-position="right"
      />
    </el-form-item>
    <el-form-item label="联系人" prop="contacts">
      <el-input
        show-word-limit
        clearable
        v-model="formObj.contacts"
        placeholder="联系人"
      />
    </el-form-item>
    <el-form-item label="联系电话" prop="contactst">
      <el-input type="tel" v-model="formObj.contactst" placeholder="联系电话">
        <template #prepend>
          <el-icon>
            <Iphone />
          </el-icon>
        </template>
      </el-input>
    </el-form-item>
    <el-form-item label="状态" prop="online">
      <el-switch v-model="formObj.online" />
      <span style="margin-left: 16px">{{
        formObj.online ? '在线' : '下线'
      }}</span>
    </el-form-item>
    <el-form-item label="编号" prop="index">
      <el-input-number readonly v-model="formObj.index" :step="1" :min="0" />
    </el-form-item>
    <el-form-item>
      <el-button
        :disabled="!edit"
        type="primary"
        @click="onSubmit"
        :loading="loading"
        >提交</el-button
      >
      <el-button :disabled="!edit" @click="onReset">重置</el-button>
    </el-form-item>
  </el-form>
      </el-form-item>
      <el-form-item label="场景类型" prop="_typeObj">
        <el-select v-model="formObj._typeObj" placeholder="场景类型">
          <el-option
            v-for="s in sceneTypes"
            :key="s.value"
            :label="s.label"
            :value="s"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="省/市/区/镇" prop="_locations">
        <el-cascader
          v-model="formObj._locations"
          :options="locations"
          placeholder="省/市/区/镇"
          style="width: 300px"
          :props="cascaderProps"
        />
      </el-form-item>
      <el-form-item label="地址" prop="location">
        <el-input
          show-word-limit
          clearable
          v-model="formObj.location"
          placeholder="地址"
        />
      </el-form-item>
      <el-form-item label="经度" prop="longitude">
        <!-- <el-input type="number" v-model="formObj.longitude" placeholder="经度" /> -->
        <el-input-number
          v-model="formObj.longitude"
          :precision="6"
          :step="0.1"
          controls-position="right"
        />
      </el-form-item>
      <el-form-item label="纬度" prop="latitude">
        <!-- <el-input type="number" v-model="formObj.latitude" placeholder="纬度" /> -->
        <el-input-number
          v-model="formObj.latitude"
          :precision="6"
          :step="0.1"
          controls-position="right"
        />
      </el-form-item>
      <el-form-item label="联系人" prop="contacts">
        <el-input
          show-word-limit
          clearable
          v-model="formObj.contacts"
          placeholder="联系人"
        />
      </el-form-item>
      <el-form-item label="联系电话" prop="contactst">
        <el-input type="tel" v-model="formObj.contactst" placeholder="联系电话">
          <template #prepend>
            <el-icon>
              <Iphone />
            </el-icon>
          </template>
        </el-input>
      </el-form-item>
      <el-form-item label="状态" prop="online">
        <el-switch v-model="formObj.online" />
        <span style="margin-left: 16px">{{
          formObj.online ? '在线' : '下线'
        }}</span>
      </el-form-item>
      <el-form-item label="编号" prop="index">
        <el-input-number readonly v-model="formObj.index" :step="1" :min="0" />
      </el-form-item>
    </template>
  </FYForm>
</template>
<script setup>
@@ -106,7 +95,6 @@
import { enumScene } from '@/enum/scene';
import { enumLocation } from '@/enum/location';
import sceneApi from '@/api/fysp/sceneApi';
import { useFormConfirm } from '@/composables/formConfirm';
const props = defineProps({
  //场景基本信息
@@ -117,15 +105,7 @@
const emit = defineEmits(['onSubmit', 'onCancel']);
const { formObj, formRef, edit, onSubmit, onReset } = useFormConfirm({
  submit: {
    do: submit,
  },
  cancel: {
    do: cancel,
  },
});
const loading = ref(false);
const _formInfo = ref();
const sceneTypes = reactive(enumScene(2, false));
const locations = reactive(enumLocation(false));
const cascaderProps = reactive({
@@ -169,7 +149,11 @@
  ],
});
// åœºæ™¯åŸºæœ¬ä¿¡æ¯æ ¼å¼åŒ–
/**
 * åœºæ™¯åŸºæœ¬ä¿¡æ¯æ ¼å¼åŒ–
 * å¯¹åœºæ™¯ç±»åž‹ã€åœºæ™¯è¡Œæ”¿åŒºåˆ’和场景可用状态进行格式化
 * @param {*} s åœºæ™¯ä¿¡æ¯
 */
function parseSceneBaseInfo(s) {
  s._typeObj = {
    label: s.type,
@@ -191,30 +175,28 @@
  return s;
}
// åˆ›å»ºæ–°åœºæ™¯
function createScene() {
  loading.value = true;
function createScene(formObj, func) {
  return sceneApi
    .createScene(formObj.value)
    .then((res) => {
      console.log(res);
    .then(() => {
      emit('onSubmit', formObj);
    })
    .finally(() => {
      loading.value = false;
      func();
    });
}
// æ›´æ–°åœºæ™¯
function updateScene() {
  loading.value = true;
function updateScene(formObj, func) {
  return sceneApi
    .updateScene(formObj.value)
    .then((res) => {
      console.log(res);
    .then(() => {
      emit('onSubmit', formObj);
    })
    .finally(() => {
      loading.value = false;
      func();
    });
}
function submit() {
function submit(formObj, func) {
  // è¡Œæ”¿åŒºåˆ’信息填充
  const a = formObj.value._locations;
  if (a[0]) {
@@ -248,16 +230,17 @@
  emit('onSubmit', formObj);
  return props.create ? createScene() : updateScene();
  return props.create ? createScene(formObj, func) : updateScene(formObj, func);
}
function cancel() {
  emit('onCancel');
}
// ç›‘听传入的场景基本信息,做相应的数据格式化
watch(
  () => props.formInfo,
  (nValue) => {
    formObj.value = parseSceneBaseInfo(nValue);
    _formInfo.value = parseSceneBaseInfo(nValue);
  }
);
</script>
src/views/baseinfo/fysp/scene/SceneInfo.vue
@@ -1,122 +1,63 @@
<template>
  <el-row ref="searchRef">
    <el-form :inline="true" :model="formSearch">
      <el-form-item label="省/市/区/镇" prop="_locations">
        <!-- <el-cascader
          v-model="formSearch._locations"
          :options="locations"
          placeholder="省/市/区/镇"
          :props="props"
          style="width: 280px"
        /> -->
        <OptionLocation
          :allOption="true"
          :level="4"
          v-model:value="formSearch._locations"
        ></OptionLocation>
      </el-form-item>
      <el-form-item label="场景类型" prop="scensetype">
        <OptionScene
          :allOption="true"
          :type="2"
          v-model:value="formSearch.scensetype"
        ></OptionScene>
        <!-- <el-select
          v-model="formSearch.scensetypeid"
          placeholder="场景类型"
          style="width: 150px"
        >
          <el-option
            v-for="s in sceneTypes"
            :key="s.value"
            :label="s.label"
            :value="s.value"
          />
        </el-select> -->
      </el-form-item>
      <el-form-item label="上线状态" prop="online">
        <OptionOnlineStatus
          :allOption="true"
          v-model:value="formSearch.online"
        ></OptionOnlineStatus>
        <!-- <el-select
          v-model="formSearch.online"
          placeholder="上线状态"
          style="width: 75px"
        >
          <el-option
            v-for="s in onlineStatus"
            :key="s.value"
            :label="s.label"
            :value="s.value"
          />
        </el-select> -->
      </el-form-item>
      <el-form-item>
        <el-button icon="Search" type="primary" @click="onSearch"
          >查询</el-button
        >
      </el-form-item>
    </el-form>
  </el-row>
  <FYTable @search="onSearch">
    <template #options>
      <FYOptionLocation
        :allOption="true"
        :level="4"
        v-model:value="formSearch._locations"
      ></FYOptionLocation>
      <FYOptionScene
        :allOption="true"
        :type="2"
        v-model:value="formSearch.scensetype"
      ></FYOptionScene>
      <FYOptionOnlineStatus
        :allOption="true"
        v-model:value="formSearch.online"
      ></FYOptionOnlineStatus>
    </template>
  <el-table
    :data="tableData"
    v-loading="loading"
    table-layout="fixed"
    :row-class-name="tableRowClassName"
    :height="tableHeight"
  >
    <el-table-column prop="name" label="名称" width="400">
      <template #default="scope">
        <el-tooltip
          effect="dark"
          :content="scope.row.name"
          placement="top-start"
          :show-after="500"
        >
          {{ scope.row.name }}
        </el-tooltip>
      </template>
    </el-table-column>
    <el-table-column prop="type" label="类型" width="130" />
    <el-table-column prop="provincename" label="省" width="90" />
    <el-table-column prop="cityname" label="市" width="90" />
    <el-table-column prop="districtname" label="区县" width="90" />
    <el-table-column prop="townname" label="街道" width="110" />
    <el-table-column prop="location" label="地址" min-width="400" />
    <el-table-column prop="longitude" label="经度" width="110" />
    <el-table-column prop="latitude" label="纬度" width="110" />
    <el-table-column fixed="right" label="操作" width="160">
      <template #default="scope">
        <el-button
          :loading="scope.row.loading1"
          type="default"
          size="small"
          @click="itemEdit(scope)"
          >编辑</el-button
        >
        <el-button
          :loading="scope.row.loading2"
          :type="scope.row.extension1 != '0' ? 'danger' : 'primary'"
          size="small"
          @click="itemActive(scope)"
          >{{ scope.row.extension1 != '0' ? '下线' : '上线' }}</el-button
        >
      </template>
    </el-table-column>
  </el-table>
  <el-pagination
    ref="paginationRef"
    class="el-pagination"
    v-model:current-page="currentPage"
    v-model:page-size="pageSize"
    :page-sizes="[10, 20, 50, 100]"
    :background="true"
    layout="total, sizes, prev, pager, next, jumper"
    :total="total"
  />
    <template #table-column>
      <el-table-column fixed="left" prop="name" label="名称" width="400">
        <template #default="scope">
          <el-tooltip
            effect="dark"
            :content="scope.row.name"
            placement="top-start"
            :show-after="500"
          >
            {{ scope.row.name }}
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="type" label="类型" width="130" />
      <el-table-column prop="provincename" label="省" width="90" />
      <el-table-column prop="cityname" label="市" width="90" />
      <el-table-column prop="districtname" label="区县" width="90" />
      <el-table-column prop="townname" label="街道" width="110" />
      <el-table-column prop="location" label="地址" min-width="400" />
      <el-table-column prop="longitude" label="经度" width="110" />
      <el-table-column prop="latitude" label="纬度" width="110" />
      <el-table-column fixed="right" label="操作" width="160">
        <template #default="scope">
          <el-button
            :loading="scope.row.loading1"
            type="default"
            size="small"
            @click="itemEdit(scope)"
            >编辑</el-button
          >
          <el-button
            :loading="scope.row.loading2"
            :type="scope.row.extension1 != '0' ? 'danger' : 'primary'"
            size="small"
            @click="itemActive(scope)"
            >{{ scope.row.extension1 != '0' ? '下线' : '上线' }}</el-button
          >
        </template>
      </el-table-column>
    </template>
  </FYTable>
</template>
<script>
@@ -129,78 +70,40 @@
  data() {
    return {
      formSearch: {
        _locations: [],
        _locations: {},
        scensetype: {},
        online: {},
      },
      tableData: [],
      tableHeight: '500',
      loading: false,
      currentPage: 1,
      pageSize: 20,
      total: 0,
    };
  },
  watch: {
    currentPage(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch();
      }
    },
    pageSize(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch();
      }
    },
  },
  computed: {
    ...mapStores(useLoadingStore),
  },
  methods: {
    onSearch() {
      this.loading = true;
    onSearch(page, func) {
      const f = this.formSearch;
      const area = {};
      // è¡Œæ”¿åŒºåˆ’
      f._locations.length > 0
        ? (area.provincecode = f._locations[0][0])
        : (area.provincecode = null);
      if (area.provincecode == '0') area.provincecode = null;
      f._locations.length > 1
        ? (area.citycode = f._locations[1][0])
        : (area.citycode = null);
      f._locations.length > 2
        ? (area.districtcode = f._locations[2][0])
        : (area.districtcode = null);
      f._locations.length > 3
        ? (area.towncode = f._locations[3][0])
        : (area.towncode = null);
      area.provincecode = f._locations.pCode
      area.citycode = f._locations.cCode
      area.districtcode = f._locations.dCode
      area.towncode = f._locations.tCode
      // åœºæ™¯ç±»åž‹
      area.scensetypeid = f.scensetype.value;
      if (area.scensetypeid == '0') area.scensetypeid = null;
      // ä¸Šä¸‹çº¿çŠ¶æ€
      area.online = f.online.value;
      sceneApi
        .searchScene(area, this.currentPage, this.pageSize)
      return sceneApi
        .searchScene(area, page.currentPage, page.pageSize)
        .then((res) => {
          if (res.success) {
            this.tableData = res.data;
            this.currentPage = res.head.page;
            this.total = res.head.totalCount;
            func({
              data: res.data,
              total: res.head.totalCount,
            });
          }
        })
        .finally(() => {
          this.loading = false;
        });
    },
    calcTableHeight() {
      const h1 = this.$refs.searchRef.$el.offsetHeight;
      const h2 = this.$refs.paginationRef.$el.offsetHeight;
      // return `calc(100vh - ${h1}px - ${h2}px - var(--el-main-padding) * 2 - var(--el-header-height))`;
      return `calc(100vh - ${h1}px - ${h2}px - 60px - var(--el-main-padding) * 2)`;
    },
    itemEdit(scope) {
      scope.row.loading1 = true;
@@ -217,7 +120,7 @@
        confirmTitle: msg,
        onConfirm: () => {
          scope.row.loading2 = true;
          sceneApi
          return sceneApi
            .updateScene(rb)
            .then((res) => {
              if (res == 1) {
@@ -230,35 +133,8 @@
        },
      });
    },
    tableRowClassName({ row }) {
      return row.extension1 != '0' ? 'online-row' : 'offline-row';
    },
  },
  mounted() {
    this.tableHeight = this.calcTableHeight();
    this.onSearch();
  },
};
</script>
<style>
.el-table .online-row {
  /* background-color: rgb(4, 202, 21); */
}
.el-table .offline-row {
  background-color: var(--el-disabled-bg-color);
  color: var(--el-disabled-text-color);
}
.el-table .cell {
  white-space: nowrap;
}
.el-pagination {
  background-color: var(--el-color-white);
  padding-top: 20px;
  border-top: 1px solid rgba(0, 0, 0, 0.096);
  /* margin-top: 2px; */
}
</style>
<style></style>
src/views/baseinfo/fysp/user/CompUserInfo.vue
@@ -76,7 +76,7 @@
<script setup>
import { defineProps, defineEmits, reactive, ref, watch } from 'vue';
import { enumUserNA } from '@/enum/user';
import { enumUser } from '@/enum/user';
import { useFormConfirm } from '@/composables/formConfirm';
import userApi from '@/api/fysp/userApi';
@@ -98,7 +98,7 @@
});
const createLoading = ref(false);
const updateLoading = ref(false);
const userTypes = reactive(enumUserNA());
const userTypes = reactive(enumUser());
const rules = reactive({
  name: [
    {
src/views/check/ProCheck.vue
ÎļþÒÑɾ³ý
src/views/check/ProCheckProxy.js
ÎļþÒÑɾ³ý
src/views/check/proStatus.js
ÎļþÒÑɾ³ý
src/views/fysp/check/ProCheck.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,230 @@
<template>
  <BaseContentLayout>
    <template #header>
      <SearchBar @on-submit="search">
        <template #summary>
          <CompSubTaskStatistic :subtasks="subtasks"/>
        </template>
      </SearchBar>
    </template>
    <template #aside>
      <SideList
        :items="subtasks"
        :loading="sideLoading"
        @item-click="chooseSubtask"
      ></SideList>
    </template>
    <template #main>
      <ToolBar
        :title="curSubtask.title"
        :descriptions="proStatus"
        :buttons="buttons"
        :loading="mainLoading"
      ></ToolBar>
      <el-scrollbar
        v-if="curProList.length > 0"
        class="el-scrollbar"
        v-loading="mainLoading"
      >
        <CompProblemCard
          v-for="(p, i) in curProList"
          :key="i"
          :index="i+1"
          :problem="p"
          @submit="updateSubtask"
        ></CompProblemCard>
      </el-scrollbar>
      <el-empty v-else description="暂无记录" v-loading="mainLoading" />
    </template>
  </BaseContentLayout>
</template>
<script>
import taskApi from '@/api/fysp/taskApi';
import ProCheckProxy from './ProCheckProxy';
import { ElMessageBox, ElNotification, ElMessage } from 'element-plus';
import CompProblemCard from './components/CompProblemCard.vue';
import CompSubTaskStatistic from './components/CompSubTaskStatistic.vue';
export default {
  components: { CompProblemCard, CompSubTaskStatistic },
  data() {
    return {
      //左侧菜单栏加载状态
      sideLoading: false,
      //右侧内容栏加载状态
      mainLoading: false,
      //子任务列表
      subtasks: [],
      //当前选中的任务
      curSubtask: {},
      //当前任务的问题列表
      curProList: [],
      //操作按钮
      buttons: [
        {
          name: '新增问题',
          color: 'success',
        },
        {
          name: '任意图片',
          color: 'warning',
        },
        {
          name: '批量审核',
          color: 'primary',
        },
      ],
    };
  },
  computed: {
    //问题状态
    proStatus() {
      return ProCheckProxy.proStatusArray(this.curProList);
    },
    //任务问题审核情况统计信息
    summary() {
      const _summary = [
        {
          name: '任务总计',
          value: 0,
          type: 'info',
        },
        {
          name: '问题未审核',
          value: 0,
          type: 'success',
          icon: 'SuccessFilled',
        },
        {
          name: '问题部分审核',
          value: 0,
          type: 'success',
          icon: 'SuccessFilled',
        },
        {
          name: '问题全部审核',
          value: 0,
          type: 'success',
          icon: 'SuccessFilled',
        },
        {
          name: '未整改',
          value: 0,
          type: 'info',
          icon: 'WarningFilled',
        },
        {
          name: '整改未审核',
          value: 0,
          type: 'info',
          icon: 'WarningFilled',
        },
        {
          name: '整改部分审核',
          value: 0,
          type: 'warning',
          icon: 'WarningFilled',
        },
        {
          name: '整改全部审核',
          value: 0,
          type: 'warning',
          icon: 'WarningFilled',
        },
      ];
      this.subtasks.forEach((s) => {
        _summary[0].value++;
        if (s.data.proNum == 0) {
          _summary[1].value++;
        } else if (s.data.proCheckedNum == 0) {
          _summary[3].value++;
        } else if (s.data.proCheckedNum < s.data.proNum) {
          _summary[2].value++;
        } else {
          _summary[1].value++;
        }
      });
      _summary.forEach((s, i) => {
        if (i > 0) {
          let per = Math.round((s.value / _summary[0].value) * 1000) / 10;
          if (isNaN(per)) per = 0;
          s.value = `${s.value}(${per}%)`;
        }
      });
      return _summary;
    },
  },
  methods: {
    //查询子任务统计信息
    search(formSearch) {
      this.sideLoading = true;
      this.mainLoading = true;
      this.curProList = [];
      this.curSubtask = {};
      taskApi.getSubtaskSummary(formSearch).then((res) => {
        const list = [];
        res.forEach((s) => {
          const t = this.getSubtaskType(s);
          list.push({
            type: t,
            title: s.stName,
            categoly: s.stPlanTime.split('T')[0],
            data: s,
          });
        });
        this.subtasks = list;
        if (list.length == 0) {
          this.sideLoading = false;
          this.mainLoading = false;
        }
      });
    },
    //获取任务问题的审核情况
    getSubtaskType(s) {
      let type = 0;
      if (s.proNum == 0) {
        type = 2;
      } else if (s.proCheckedNum == 0) {
        type = 0;
      } else if (s.proCheckedNum < s.proNum) {
        type = 1;
      } else {
        type = 2;
      }
      return type;
    },
    //点击左侧菜单任务事件
    chooseSubtask(s) {
      this.sideLoading = false;
      this.mainLoading = true;
      // const controller = new AbortController();
      taskApi
        .getProBySubtask(s.data.stGuid)
        .then((res) => {
          this.curProList = res;
          this.curSubtask = s;
        })
        .finally(() => {
          this.mainLoading = false;
        });
    },
    updateSubtask() {
      this.curSubtask.data.proCheckedNum++;
      this.curSubtask.type = this.getSubtaskType(this.curSubtask.data);
    }
  },
  mounted() {},
};
</script>
<style scoped>
.el-scrollbar {
  height: calc(100vh - 60px * 2 - 20px * 2 - var(--height-toolbar));
}
</style>
src/views/fysp/check/ProCheckProxy.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
import { $fysp } from '@/api/index'
//问题状态
const proStatus = {
  unCheck: 'unCheck',
  pass: 'pass',
  fail: 'fail',
  change_unCheck: 'change_unCheck',
  change_fail: 'change_fail',
  change_pass: 'change_pass'
}
export default {
  //统计问题
  calProStatus(proList) {
    const status = {
      //问题数量
      proNum: proList.length,
      //整改数量
      changeNum: 0,
      //待审核数量
      uncheckNum: 0,
      //已审核通过数量
      passNum: 0,
      //未通过数量
      unpassNum: 0,
      //整改率
      changePer: '0%',
      //通过率
      passPer: '0%',
      //审核率
      checkPer: '0%'
    }
    proList.forEach((p) => {
      if (p.ischanged) status.changeNum++
      if (p.extension3 == 'fail' || p.extension3 == 'change_fail') status.unpassNum++
      else if (
        p.extension3 == 'unCheck' ||
        p.extension3 == 'change_unCheck' ||
        (p.extension3 == 'pass' && p.ischanged)
      )
        status.uncheckNum++
      else status.passNum++
      status.changePer =
        String(
          (status.changeNum / status.proNum) * 100
            ? ((status.changeNum / status.proNum) * 100).toFixed(1)
            : 0
        ) + '%'
      status.passPer =
        String(
          (status.passNum / status.proNum) * 100
            ? ((status.passNum / status.proNum) * 100).toFixed(1)
            : 0
        ) + '%'
      status.checkPer =
        String(
          ((status.passNum + status.unpassNum) / status.proNum) * 100
            ? (((status.passNum + status.unpassNum) / status.proNum) * 100).toFixed(1)
            : 0
        ) + '%'
    })
    return status
  },
  //统计问题,返回数组形式
  proStatusArray(proList) {
    const status = this.calProStatus(proList)
    return [
      { name: '问题数', value: status.proNum },
      { name: '整改数', value: status.changeNum },
      { name: '待审核', value: status.uncheckNum },
      { name: '已审核', value: status.passNum },
      { name: '未通过', value: status.unpassNum },
      { name: '整改率', value: status.changePer },
      { name: '通过率', value: status.passPer },
      { name: '审核率', value: status.checkPer }
    ]
  },
  //问题图片和整改图片
  proPics(pro) {
    const pics = [
      {
        title: '问题图片',
        path: []
      },
      {
        title: '整改图片',
        path: []
      }
    ]
    if (pro.mediafileList) {
      pro.mediafileList.forEach((m) => {
        pics[m.ischanged ? 1 : 0].path.push(`${$fysp.imgUrl}${m.extension1}${m.guid}.jpg`)
      })
    }
    return pics
  },
  //问题审核状态转换中文
  proStatusMap(p) {
    switch (p) {
      case proStatus.unCheck:
        return { name: '问题未审核', type: 'warning', index: 0, checkable: true, deletable: true, changeable: false }
      case proStatus.pass:
        return { name: '问题通过', type: 'success', index: 1, checkable: false, deletable: true, changeable: false }
      case proStatus.fail:
        return { name: '问题不通过', type: 'danger', index: 1, checkable: false, deletable: true, changeable: false }
      case proStatus.change_unCheck:
        return { name: '整改未审核', type: 'warning', index: 2, checkable: true, deletable: false, changeable: true }
      case proStatus.change_fail:
        return { name: '整改不通过', type: 'danger', index: 3, checkable: false, deletable: false, changeable: true }
      case proStatus.change_pass:
        return { name: '整改通过', type: 'success', index: 3, checkable: false, deletable: false, changeable: true }
      default:
        return { name: '问题未审核', type: 'warning', index: 0, checkable: true, deletable: true }
    }
  },
  /**
   * é—®é¢˜å®¡æ ¸åŽçŠ¶æ€å˜æ¢
   * @param {String} s å½“前问题状态
   * @param {Boolean} isPass å®¡æ ¸é€šè¿‡æˆ–驳回
   * @returns ä¸‹ä¸€ä¸ªé—®é¢˜çŠ¶æ€
   */
  proNextStatus(s, isPass) {
    let status, action
    switch (s) {
      case proStatus.unCheck:
        status = isPass ? proStatus.pass : proStatus.fail
        action = isPass ? 0 : 1
        break
      case proStatus.change_unCheck:
        status = isPass ? proStatus.change_pass : proStatus.change_fail
        action = isPass ? 2 : 3
        break
    }
    return { status: status, action: action }
  }
}
src/views/fysp/check/components/CompProblemCard.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,241 @@
<template>
  <el-card class="layout" shadow="hover">
    <el-steps :active="proStatus.index" finish-status="success" style="" align-center>
      <el-step v-for="(s, i) in getSteps" :key="i" :title="s" />
    </el-steps>
    <el-descriptions :column="3" size="small">
      <template #title>
        <span class="d-index">{{ index }}</span>
        <span class="d-title">{{ title }}</span>
      </template>
      <template #extra>
        <!-- <span class="d-extra">{{ status }}</span> -->
        <el-tag
          style="font-size: 16px; line-height: 16px; padding: 14px"
          :type="proStatus.type"
          effect="plain"
          size="large"
          round
          >{{ proStatus.name }}</el-tag
        >
      </template>
      <el-descriptions-item
        label-class-name="descriptions-label-1"
        v-for="(d, i) in descriptions"
        :key="i"
        :label="d.name"
        >{{ d.value }}</el-descriptions-item
      >
    </el-descriptions>
    <el-scrollbar>
      <el-descriptions title=" " :column="2" direction="vertical" size="small" border>
        <template v-for="(pic, t) in pics" :key="t">
          <template v-if="pic.path.length > 0">
            <el-descriptions-item
              :label="pic.title"
              :label-class-name="t == 0 ? 'descriptions-label-1' : 'descriptions-label-2'"
            >
              <el-space>
                <el-image
                  v-for="(p, i) in pic.path"
                  class="image"
                  :key="i"
                  :src="p"
                  :preview-src-list="pic.path"
                  :initial-index="i"
                  fit="cover"
                  lazy
                />
              </el-space>
            </el-descriptions-item>
          </template>
        </template>
      </el-descriptions>
    </el-scrollbar>
    <!-- é—®é¢˜æ“ä½œ -->
    <el-row v-if="true" style="margin-top: 16px">
      <el-col :span="12">
        <el-row justify="start" class="btn-group">
          <el-button type="success" size="small" @click="updatePro" plain>修改问题</el-button>
          <el-button
            type="primary"
            size="small"
            @click="updateChange"
            plain
            :disabled="!proStatus.changeable"
            >修改整改</el-button
          >
        </el-row>
      </el-col>
      <el-col :span="12">
        <el-row justify="end" class="btn-group">
          <el-button type="danger" size="small" @click="deletePro" :disabled="!proStatus.deletable"
            >删除</el-button
          >
          <el-button type="warning" size="small" @click="rejectPro" :disabled="!proStatus.checkable"
            >驳回</el-button
          >
          <el-button type="success" size="small" @click="passPro" :disabled="!proStatus.checkable"
            >通过</el-button
          >
        </el-row>
      </el-col>
    </el-row>
  </el-card>
</template>
<script>
import ProCheckProxy from '../ProCheckProxy'
import problemApi from '@/api/fysp/problemApi'
import { useMessageBoxTip } from '@/composables/messageBox'
export default {
  props: {
    problem: {
      type: Object,
      default: () => {
        return {}
      }
    },
    index: {
      type: Number,
      default: 1
    }
  },
  emits:['submit'],
  data() {
    return {
      // å®¡æ ¸æ­¥éª¤
      steps: [
        {
          bef: '问题待审核',
          aft: '问题已审核'
        },
        {
          bef: '问题待整改',
          aft: '问题已整改'
        },
        {
          bef: '整改待审核',
          aft: '整改已审核'
        }
      ]
    }
  },
  computed: {
    // é—®é¢˜åç§°
    title() {
      return this.problem.problemname
    },
    // é—®é¢˜æè¿°
    descriptions() {
      return [
        {
          name: '问题位置',
          value: this.problem.location
        },
        {
          name: '提交时间',
          value: this.problem.time.replace('T', ' ').split('.')[0]
        }
      ]
    },
    // é—®é¢˜å›¾ç‰‡
    pics() {
      return ProCheckProxy.proPics(this.problem)
    },
    /**
     * èŽ·å–å½“å‰é—®é¢˜å®¡æ ¸æ­¥éª¤
     */
    getSteps() {
      return this.steps.map((v, i) => {
        if (i >= this.proStatus.index) {
          return v.bef
        } else {
          return v.aft
        }
      })
    },
    // é—®é¢˜çŠ¶æ€
    proStatus() {
      return ProCheckProxy.proStatusMap(this.problem.extension3)
    }
  },
  methods: {
    deletePro() {},
    rejectPro() {
      this.checkPro(false)
    },
    passPro() {
      this.checkPro(true)
    },
    checkPro(pass) {
      const pro = this.problem
      let doneMsg = pass ? '通过' : '驳回'
      useMessageBoxTip({
        confirmMsg: `确认是否${doneMsg}该问题?`,
        confirmTitle: '问题审核',
        onConfirm: () => {
          const { status, action } = ProCheckProxy.proNextStatus(pro.extension3, pass)
          return problemApi.checkProblem({ pId: pro.guid, action: action }).then((res) => {
            if (res.success) {
              pro.extension3 = status
              this.$emit('submit')
            }
          })
        }
      })
    },
    updatePro() {},
    updateChange() {}
  }
}
</script>
<style scoped>
.layout {
  background-color: transparent;
  margin-top: 20px;
  /* border: none; */
  border-color: rgba(0, 0, 0, 0.308);
}
.image {
  width: 200px;
  height: 210px;
  border-radius: 4px;
}
.d-index {
  display: inline-block;
  width: 22px;
  height: 22px;
  line-height: 22px;
  text-align: center;
  border-radius: 50%;
  color: var(--el-color-primary-light-3);
  border: 2px solid var(--el-color-primary-light-3);
  margin-right: 4px;
}
.d-title {
  /* background: var(--el-color-danger-light-3); */
  font-weight: 600;
  font-size: var(--el-font-size-large);
}
.d-extra {
}
.descriptions-label-1 {
  color: whitesmoke;
  background: var(--el-color-danger-light-3);
}
.descriptions-label-2 {
  color: whitesmoke;
  background-color: var(--el-color-success-light-3);
}
</style>
src/views/fysp/check/components/CompSubTaskStatistic.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
<template>
  <el-descriptions :column="8" size="small" border direction="vertical">
    <el-descriptions-item label="任务总计">{{ summary.total }}</el-descriptions-item>
    <el-descriptions-item label="问题未审核">{{ summary.proUnCheck }}</el-descriptions-item>
    <el-descriptions-item label="问题部分审核">{{ summary.proPartCheck }}</el-descriptions-item>
    <el-descriptions-item label="问题全部审核">{{ summary.proAllCheck }}</el-descriptions-item>
    <el-descriptions-item label="未整改">{{ summary.UnChange }}</el-descriptions-item>
    <el-descriptions-item label="整改未审核">{{ summary.changeUnCheck }}</el-descriptions-item>
    <el-descriptions-item label="整改部分审核">{{ summary.changePartCheck }}</el-descriptions-item>
    <el-descriptions-item label="整改全部审核">{{ summary.changeAllCheck }}</el-descriptions-item>
  </el-descriptions>
  <!-- <el-space>
    <el-tag v-for="(s, i) in summary" :key="i" :type="s.type" size="small">
      <el-icon v-if="s.icon" color="">
        <component :is="s.icon"></component>
      </el-icon>
      {{ s.name + ': ' + s.value }}
    </el-tag>
  </el-space> -->
</template>
<script>
export default {
  props: {
    subtasks: Array
  },
  computed: {
    //任务问题审核情况统计信息
    summary() {
      const _summary = {
        total: 0,
        proUnCheck: 0,
        proPartCheck: 0,
        proAllCheck: 0,
        UnChange: 0,
        changeUnCheck: 0,
        changePartCheck: 0,
        changeAllCheck: 0
      }
      this.subtasks.forEach((s) => {
        _summary.total++
        // é—®é¢˜å®¡æ ¸æƒ…况
        if (s.data.proNum == 0) {
          _summary.proAllCheck++
        } else if (s.data.proCheckedNum == 0) {
          _summary.proUnCheck++
        } else if (s.data.proCheckedNum < s.data.proNum) {
          _summary.proPartCheck++
        } else {
          _summary.proAllCheck++
        }
        // æ˜¯å¦æœ‰æœªæ•´æ”¹
        if (s.data.changeNum < s.data.proNum) {
          _summary.UnChange++
        }
        // æ•´æ”¹å®¡æ ¸æƒ…况
        if (s.data.proNum == 0) {
          _summary.changeAllCheck++
        } else if (s.data.changeNum > 0) {
          if (s.data.changeCheckedNum == 0) {
            _summary.changeUnCheck++
          } else if (s.data.changeCheckedNum < s.data.changeNum) {
            _summary.changePartCheck++
          } else {
            _summary.changeAllCheck++
          }
        }
      })
      // _summary.forEach((s, i) => {
      //   if (i > 0) {
      //     let per = Math.round((s.value / _summary[0].value) * 1000) / 10
      //     if (isNaN(per)) per = 0
      //     s.value = `${s.value}(${per}%)`
      //   }
      // })
      return _summary
    }
  }
}
</script>
src/views/fysp/config/ProblemType.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
<template>
  <div>ProblemType</div>
</template>
<script>
export default {
  name: 'ProblemType',
  data() {
    return {
    }
  }
};
</script>
src/views/fysp/evaluation/DataSource.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
<template>
  <div>DataSource</div>
</template>
<script>
export default {
  name: 'DataSource',
  data() {
    return {
    }
  }
};
</script>
src/views/fysp/evaluation/ResultManage.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
<template>
  <CompPreCheck @pre-check="autoEvaluate"></CompPreCheck>
</template>
<script>
import CompPreCheck from './components/CompPreCheck.vue';
export default {
    name: 'ResultManage',
    components: { CompPreCheck },
    data() {
        return {};
    },
    methods:{
      /**
       * é€šè¿‡è‡ªè¯„预检后,执行自动评估
       * @param {*} options æŸ¥è¯¢å‚æ•°
       */
      autoEvaluate(options){
      }
    }
};
</script>
src/views/fysp/evaluation/components/CompPreCheck.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,134 @@
<template>
  <FYSearchBar @search="preCheck">
    <template #options>
      <!-- åŒºåŽ¿ -->
      <FYOptionLocation
        :allOption="false"
        :level="3"
        :checkStrictly="false"
        v-model:value="formSearch._locations"
      ></FYOptionLocation>
      <!-- åœºæ™¯ç±»åž‹ -->
      <FYOptionScene
        :allOption="false"
        :type="2"
        v-model:value="formSearch.scenetype"
      ></FYOptionScene>
      <!-- æ—¶é—´ -->
      <FYOptionTime type="month" v-model:value="formSearch.time"></FYOptionTime>
    </template>
  </FYSearchBar>
  <el-row>
    <span>快捷选择</span>
    <el-button v-for="(v, i) in quickSetting" :key="i" type="primary" @click="quickSet(v)">{{
      v.name
    }}</el-button>
  </el-row>
</template>
<script>
/**
 * è‡ªåŠ¨è¯„ä¼°æ¡ä»¶åˆè§„æ€§æ£€æŸ¥
 */
export default {
  name: 'CompPreCheck',
  props: {
    quickSetting: {
      type: Array,
      default: () => {
        return [
          {
            name: '静安工地',
            locations: {
              pCode: '31',
              pName: '上海市',
              cCode: '3100',
              cName: '上海市',
              dCode: '310106',
              dName: '静安区'
            },
            scenetype: { label: '工地', value: '1' }
          },
          {
            name: '徐汇餐饮',
            locations: {
              pCode: '31',
              pName: '上海市',
              cCode: '3100',
              cName: '上海市',
              dCode: '310104',
              dName: '徐汇区'
            },
            scenetype: { label: '餐饮', value: '5' }
          },
          {
            name: '金山工地',
            locations: {
              pCode: '31',
              pName: '上海市',
              cCode: '3100',
              cName: '上海市',
              dCode: '310116',
              dName: '金山区'
            },
            scenetype: { label: '工地', value: '1' }
          },
          {
            name: '金山码头',
            locations: {
              pCode: '31',
              pName: '上海市',
              cCode: '3100',
              cName: '上海市',
              dCode: '310116',
              dName: '金山区'
            },
            scenetype: { label: '码头', value: '2' }
          },
          {
            name: '金山搅拌站',
            locations: {
              pCode: '31',
              pName: '上海市',
              cCode: '3100',
              cName: '上海市',
              dCode: '310116',
              dName: '金山区'
            },
            scenetype: { label: '搅拌站', value: '3' }
          }
        ]
      }
    }
  },
  emits: ['preCheck'],
  data() {
    return {
      formSearch: {
        _locations: {},
        scenetype: {},
        time: undefined
      }
    }
  },
  methods: {
    /**
     * è‡ªåŠ¨è¯„ä¼°å‰ç½®åˆè§„æ€§æ£€æŸ¥
     * æ£€æŸ¥æ‰€é€‰èŒƒå›´å†…各项评估数据源是否完整
     */
    preCheck() {
      this.$emit('preCheck', this.formSearch)
    },
    /**
     * å¿«é€Ÿè®¾ç½®æ¡ä»¶
     */
    quickSet(set) {
      this.formSearch._locations = set.locations
      this.formSearch.scenetype = set.scenetype
      this.preCheck()
    }
  }
}
</script>
src/views/fytz/ledger/LedgerManage.vue
src/views/fytz/user/UserInfo.vue
@@ -1,146 +1,95 @@
<template>
  <el-row ref="searchRef">
    <el-col>
      <el-form :inline="true" :model="formSearch">
        <el-form-item label="省/市/区/镇" prop="_locations">
          <el-cascader
            v-model="formSearch._locations"
            :options="locations"
            placeholder="省/市/区/镇"
            :props="props"
            style="width: 280px"
          />
        </el-form-item>
        <el-form-item label="场景名称" prop="searchText">
          <el-input
            clearable
            v-model="formSearch.searchText"
            placeholder="输入搜索场景名称"
          />
        </el-form-item>
        <el-form-item label="用户类型" prop="scensetypeid">
          <el-select
            v-model="formSearch.scensetypeid"
            placeholder="用户类型"
            style="width: 75px"
          >
            <el-option
              v-for="s in sceneTypes"
              :key="s.value"
              :label="s.label"
              :value="s.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="上线状态" prop="online">
          <el-select
            v-model="formSearch.online"
            placeholder="全部"
            style="width: 75px"
          >
            <el-option
              v-for="s in onlineStatus"
              :key="s.value"
              :label="s.label"
              :value="s.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button icon="Search" type="primary" @click="onSearch"
            >查询</el-button
          >
        </el-form-item>
      </el-form>
    </el-col>
  </el-row>
  <el-table
    :data="tableData"
    v-loading="loading"
    table-layout="fixed"
    :row-class-name="tableRowClassName"
    :height="tableHeight"
  >
    <el-table-column prop="realname" label="公司" align="center">
      <template #default="scope">
        <el-tooltip
          effect="dark"
          :content="scope.row.realname"
          placement="top-start"
          :show-after="500"
        >
          {{ scope.row.realname }}
        </el-tooltip>
      </template>
    </el-table-column>
    <el-table-column prop="telephone" label="电话" align="center" />
    <el-table-column
      prop="extension1"
      label="区县"
      width="120"
      align="center"
    />
    <el-table-column prop="usertype" label="类型"  align="center" />
    <el-table-column prop="departmentname" label="名称">
      <template #default="scope">
        <el-tooltip
          effect="dark"
          :content="scope.row.departmentname"
          placement="top-start"
          :show-after="500"
        >
          {{ scope.row.departmentname }}
        </el-tooltip>
      </template>
    </el-table-column>
    <el-table-column fixed="right" label="操作" width="140">
      <template #header>
        <el-button icon="DocumentAdd" type="success" @click="drawer = true"
          >新增用户</el-button
        >
      </template>
      <template #default="scope">
        <el-button
          :loading="scope.row.loading1"
          type="default"
          size="small"
          @click="editRow(scope)"
          >编辑</el-button
        >
        <el-button
          :loading="scope.row.loading2"
          :type="scope.row.extension1 != '0' ? 'danger' : 'primary'"
          size="small"
          @click="itemActive(scope)"
          >{{ scope.row.extension1 != '0' ? '下线' : '上线' }}</el-button
        >
      </template>
    </el-table-column>
  </el-table>
  <FYTable @search="onSearch" :row-class-name="tableRowClassName">
    <template #options>
      <FYOptionLocation
        :allOption="true"
        :level="4"
        v-model:value="formSearch._locations"
      ></FYOptionLocation>
      <FYOptionText label="关键字" placeholder="输入名称关键字" v-model:value="formSearch.searchText"></FYOptionText>
      <FYOptionScene
        :allOption="true"
        :type="1"
        v-model:value="formSearch.scensetype"
      ></FYOptionScene>
      <FYOptionOnlineStatus
        :allOption="true"
        v-model:value="formSearch.online"
      ></FYOptionOnlineStatus>
    </template>
  <el-pagination
    ref="paginationRef"
    class="el-pagination"
    v-model:current-page="currentPage"
    v-model:page-size="pageSize"
    :page-sizes="[10, 20, 50, 100]"
    :background="true"
    layout="total, sizes, prev, pager, next, jumper"
    :total="total"
  />
    <template #table-column>
      <el-table-column
        type="index"
        fixed="left"
        prop="userInfo.realname"
        label="名称"
        width="400"
      >
        <template #default="scope">
          <el-tooltip
            effect="dark"
            :content="scope.row.userInfo.realname"
            placement="top-start"
            :show-after="500"
          >
            {{ scope.row.userInfo.realname }}
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="userInfo.acountname" label="账号" width="110" />
      <el-table-column prop="sceneTypeName" label="类型" width="130" />
      <el-table-column prop="biProvinceName" label="省" width="90" />
      <el-table-column prop="biCityName" label="市" width="90" />
      <!-- <el-table-column prop="districtname" label="区县" width="90" /> -->
      <el-table-column prop="userInfo.extension1" label="区县" width="90" />
      <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="scope">
          {{ scope.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="140">
        <template #header>
          <el-button
            icon="DocumentAdd"
            size="default"
            type="success"
            @click="drawer = true"
            >新增用户</el-button
          >
        </template>
        <template #default="scope">
          <el-button
            :loading="scope.row.loading1"
            type="primary"
            size="small"
            @click="editRow(scope)"
            >查看</el-button
          >
          <!-- <el-button
            :loading="scope.row.loading2"
            :type="scope.row.extension1 != '0' ? 'danger' : 'primary'"
            size="small"
            @click="itemActive(scope)"
            >{{ scope.row.extension1 != '0' ? '下线' : '上线' }}</el-button
          > -->
        </template>
      </el-table-column>
    </template>
  </FYTable>
  <CompUserInfoAddDrawer v-model:drawer="drawer"></CompUserInfoAddDrawer>
</template>
<script>
import { enumScene } from '@/enum/scene';
import { enumLocation } from '@/enum/location';
import { enumOnlineStatus } from '@/enum/onlineStatus';
import userApi from '@/api/fytz/userApi';
import { useLoadingStore } from '@/stores/loadingStore';
import { mapStores } from 'pinia';
import { useMessageBoxTip } from '@/composables/messageBox';
import CompUserInfoAddDrawer from './components/CompUserInfoAddDrawer.vue';
import CompUserInfoAddDrawer from '@/views/fytz/user/components/CompUserInfoAddDrawer.vue';
export default {
  components: {
@@ -148,98 +97,58 @@
  },
  data() {
    return {
      locations: enumLocation(),
      sceneTypes: enumScene(1),
      onlineStatus: enumOnlineStatus(),
      formSearch: {
        _locations: [],
        _locations: {},
        searchText: '',
        scensetypeid: '',
        online: '',
        scensetype: {},
        online: {},
      },
      props: {
        checkStrictly: true,
      },
      tableData: [],
      tableHeight: '500',
      loading: false,
      currentPage: 1,
      pageSize: 20,
      total: 0,
      drawer: false,
    };
  },
  watch: {
    currentPage(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch();
      }
    },
    pageSize(nValue, oValue) {
      if (nValue != oValue) {
        this.onSearch();
      }
    },
  },
  computed: {
    ...mapStores(useLoadingStore),
  },
  methods: {
    onSearch() {
      this.loading = true;
    onSearch(page, func) {
      const f = this.formSearch;
      const area = {};
      // è¡Œæ”¿åŒºåˆ’
      f._locations.length > 0
        ? ([area.provinceCode, area.provinceName] = f._locations[0])
        : ([area.provinceCode, area.provinceName] = [null, null]);
      if (area.provinceCode == '0')
        [area.provinceCode, area.provinceName] = [null, null];
      f._locations.length > 1
        ? (area.citycode = f._locations[1][0])
        : (area.citycode = null);
      f._locations.length > 2
        ? ([area.districtCode, area.districtName] = f._locations[2])
        : ([area.districtCode, area.districtName] = [null, null]);
      f._locations.length > 3
        ? (area.towncode = f._locations[3][0])
        : (area.towncode = null);
      area.provinceCode = f._locations.pCode ? f._locations.pCode + '0000' : undefined
      area.provinceName = f._locations.pName;
      if (area.provinceCode == null) {
        area.provinceCode = null;
        area.provinceName = null;
      }
      area.cityCode = f._locations.cCode ? f._locations.cCode.substring(0, 3) + '100' : undefined
      area.cityName = f._locations.cName;
      area.districtCode = f._locations.dCode;
      area.districtName = f._locations.dName;
      area.townCode = f._locations.tCode;
      area.townName = f._locations.tName;
      // åœºæ™¯ç±»åž‹
      area.scensetypeid = f.scensetypeid;
      if (area.scensetypeid == '0') area.scensetypeid = null;
      area.sceneTypes = [];
      f.scensetype.value == null
        ? (area.sceneTypes = [])
        : (area.sceneTypes = [f.scensetype.value]);
      // ä¸Šä¸‹çº¿çŠ¶æ€
      area.online = f.online;
      area.online = f.online.value;
      // å…³é”®å­—
      area.searchText = f.searchText;
      userApi
        .fetchUser('00EQQVnE9QFvbkQr', this.currentPage, this.pageSize, area)
        .then((res) => {
          if (res) {
            this.tableData = res.data;
            this.currentPage = res.headers.currentPage;
            console.log(res.headers);
            this.total = parseInt(res.headers.totalPage) * this.pageSize;
          }
        })
        .finally(() => {
          this.loading = false;
        });
    },
    calcTableHeight() {
      const h1 = this.$refs.searchRef.$el.offsetHeight;
      const h2 = this.$refs.paginationRef.$el.offsetHeight;
      // return `calc(100vh - ${h1}px - ${h2}px - var(--el-main-padding) * 2 - var(--el-header-height))`;
      return `calc(100vh - ${h1}px - ${h2}px - 60px - var(--el-main-padding) * 2)`;
      userApi.fetchUser(page.currentPage, page.pageSize, area).then((res) => {
        if (res) {
          func({
            data: res.data,
            total: res.head.totalCount,
          });
        }
      });
    },
    editRow(scope) {
      scope.row.loading1 = true;
      this.loadingStore.loadingStatus.push(() => (scope.row.loading1 = false));
      this.$router.push(`userEdit/${scope.row.guid}`);
      this.$router.push(`userEdit/${scope.row.biGuid}`);
    },
    itemActive(scope) {
      const rb = {};
@@ -251,7 +160,7 @@
        confirmTitle: msg,
        onConfirm: () => {
          scope.row.loading2 = true;
          userApi
          return userApi
            .updateScene(rb)
            .then((res) => {
              if (res == 1) {
@@ -265,31 +174,9 @@
      });
    },
    tableRowClassName({ row }) {
      return row.extension1 != '0' ? 'online-row' : 'offline-row';
      return row.userInfo.isenable ? 'online-row' : 'offline-row';
    },
  },
  mounted() {
    this.formSearch.scensetypeid = this.sceneTypes[0].value;
    this.formSearch._locations = [this.locations[0].value];
    this.formSearch.online = this.onlineStatus[0].value;
    this.tableHeight = this.calcTableHeight();
    this.onSearch();
  },
};
</script>
<style>
.el-table .offline-row {
  background-color: var(--el-disabled-bg-color);
  color: var(--el-disabled-text-color);
}
.el-table .cell {
  white-space: nowrap;
  color: var(--el-disabled-text-color);
}
.el-pagination {
  background-color: var(--el-color-white);
  padding-top: 20px;
  border-top: 1px solid rgba(0, 0, 0, 0.096);
  /* margin-top: 2px; */
}
</style>
<style></style>
src/views/fytz/user/components/CompUserInfo.vue
@@ -1,119 +1,97 @@
<template>
  <el-form
    :inline="false"
    :model="formObj"
    ref="formRef"
  <FYForm
    :form-info="_formInfo"
    :rules="rules"
    label-position="right"
    label-width="150px"
    :reset="active"
    v-model:is-edit="_edit"
    @submit="submit"
    @cancel="cancel"
  >
    <!-- <el-form-item label="id" prop="GUID">
      <el-input clearable v-model="formObj.GUID" placeholder="id" />
    </el-form-item> -->
    <!-- <el-form-item label="头像url" prop="HeadIconUrl">
      <el-input clearable v-model="formObj.HeadIconUrl" placeholder="头像url" />
    </el-form-item> -->
    <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>
    <el-form-item label="密码" prop="password">
      <el-input
        clearable
        type="password"
        v-model="formObj.password"
        placeholder="默认密码123456"
      />
    </el-form-item>
    <!-- <el-form-item label="用户类型id" prop="UserTypeID">
      <el-input
        clearable
        v-model="formObj.UserTypeID"
        placeholder="用户类型id"
      />
    </el-form-item> -->
    <el-form-item label="用户类型" prop="_usertype">
      <el-select v-model="formObj._usertype" placeholder="用户类型">
        <el-option
          v-for="s in userTypes"
          :key="s.value"
          :label="s.label"
          :value="s"
    <template #form-item="{ formObj }">
      <!-- <el-form-item label="头像url" prop="HeadIconUrl">
        <el-input
          clearable
          v-model="formObj.HeadIconUrl"
          placeholder="头像url"
        />
      </el-select>
    </el-form-item>
    <el-form-item label="所属企业" prop="departmentname">
      <el-input
        clearable
        v-model="formObj.departmentname"
        placeholder="所属企业"
        disabled
      />
    </el-form-item>
    <el-form-item label="是否可用" prop="isenable">
      <el-switch v-model="formObj.isenable" />
      <span style="margin-left: 16px">{{
        formObj.isenable ? '可用' : '不可用'
      }}</span>
    </el-form-item>
    <el-form-item label="工号" prop="workno">
      <el-input clearable v-model="formObj.workno" placeholder="工号" />
    </el-form-item>
    <el-form-item label="手机" prop="telephone">
      <el-input
        clearable
        type="tel"
        v-model="formObj.telephone"
        placeholder="手机"
      />
    </el-form-item>
    <!-- <el-form-item label="微信id" prop="WechatID">
      <el-input clearable v-model="formObj.WechatID" placeholder="微信id" />
    </el-form-item> -->
    <el-form-item label="省/市/区/镇" prop="_locations">
      <el-cascader
        v-model="formObj._locations"
        :options="locations"
        placeholder="省/市/区/镇"
        :props="locationsProps"
        style="width: 280px"
        :disabled="!create"
      />
    </el-form-item>
    <el-form-item label="场景类型" prop="_scenetype">
      <el-select
        v-model="formObj._scenetype"
        placeholder="场景类型"
      >
        <el-option
          v-for="s in sceneTypes"
          :key="s.value"
          :label="s.label"
          :value="s"
      </el-form-item> -->
      <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>
      <el-form-item label="密码" prop="password">
        <el-col :span="18">
          <el-input
            :disabled="true"
            clearable
            type="password"
            v-model="formObj.password"
            placeholder="默认密码123456"
          />
        </el-col>
        <el-col :span="6" v-if="!create">
          <el-row justify="end">
            <el-button type="danger" @click="onResetPw" :loading="pwLoading"
              >重置密码</el-button
            >
          </el-row>
        </el-col>
      </el-form-item>
      <FYOptionUserType
        :allOption="false"
        :initValue="false"
        v-model:value="formObj._usertype"
      ></FYOptionUserType>
      <!-- <el-form-item label="所属企业" prop="departmentname">
        <el-input
          clearable
          v-model="formObj.departmentname"
          placeholder="所属企业"
          disabled
        />
      </el-select>
    </el-form-item>
    <el-form-item>
      <el-button
        :disabled="!edit"
        type="primary"
        @click="onSubmit"
        :loading="loading"
        >提交</el-button
      >
      <el-button :disabled="!edit" @click="onReset">重置</el-button>
    </el-form-item>
  </el-form>
      </el-form-item> -->
      <el-form-item label="是否可用" prop="isenable">
        <el-switch v-model="formObj.isenable" />
        <span style="margin-left: 16px">{{
          formObj.isenable ? '可用' : '不可用'
        }}</span>
      </el-form-item>
      <el-form-item label="工号" prop="workno">
        <el-input clearable v-model="formObj.workno" placeholder="工号" />
      </el-form-item>
      <el-form-item label="手机" prop="telephone">
        <el-input
          clearable
          type="tel"
          v-model="formObj.telephone"
          placeholder="手机"
        />
      </el-form-item>
      <!-- <el-form-item label="省/市/区/镇" prop="_locations">
        <el-cascader
          v-model="formObj._locations"
          :options="locations"
          placeholder="省/市/区/镇"
          :props="locationsProps"
          style="width: 280px"
          :disabled="!create"
        />
      </el-form-item> -->
      <FYOptionScene
        :allOption="false"
        :type="1"
        :initValue="false"
        v-model:value="formObj._scenetype"
      ></FYOptionScene>
    </template>
  </FYForm>
</template>
<script setup>
import { defineProps, defineEmits, reactive, ref, watch } from 'vue';
import { useFormConfirm } from '@/composables/formConfirm';
import { enumUserNA } from '@/enum/user';
import { enumScene, getSceneName } from '@/enum/scene';
import { getSceneName } from '@/enum/scene';
import { enumLocation } from '@/enum/location';
import userApi from '@/api/fytz/userApi';
@@ -133,26 +111,12 @@
const emit = defineEmits(['onSubmit', 'onCancel', 'update:isEdit']);
const { formObj, formRef, edit, onSubmit, onReset } = useFormConfirm({
  defaultForm: {
    isenable: true,
  },
  submit: {
    do: submit,
  },
  cancel: {
    do: cancel,
  },
});
const userTypes = reactive(enumUserNA());
const sceneTypes = reactive(enumScene(1, false));
const _formInfo = ref();
const _edit = ref(false);
const locations = enumLocation(false);
const locationsProps = reactive({
  checkStrictly: true,
});
const loading = ref(false);
const rules = reactive({
  acountname: [
    {
@@ -168,13 +132,13 @@
      trigger: 'blur',
    },
  ],
  password: [
    {
      required: props.create,
      message: '密码不能为空',
      trigger: 'blur',
    },
  ],
  // password: [
  //   {
  //     required: props.create,
  //     message: '密码不能为空',
  //     trigger: 'blur',
  //   },
  // ],
  _usertype: [
    {
      required: true,
@@ -205,49 +169,36 @@
    value: s.usertypeid + '',
  };
  s._scenetype = {
    label: getSceneName(s.extension2, 1),
    value: s.extension2,
  };
  s._scenetype = getSceneName(s.extension2, 1);
  s._locations = [];
  // if (s.provincecode && s.provincecode.length > 0)
  //   s._locations.push([s.provincecode, s.provincename]);
  // if (s.citycode && s.citycode.length > 0)
  //   s._locations.push([s.citycode, s.cityname]);
  // if (s.districtcode && s.districtcode.length > 0)
  //   s._locations.push([s.districtcode, s.districtname]);
  // if (s.towncode && s.towncode.length > 0)
  //   s._locations.push([s.towncode, s.townname]);
  return s;
}
function createUser() {
  loading.value = true;
function createUser(formObj, func) {
  return userApi
    .createUser(formObj.value)
    .then(() => {
      emit('onSubmit', formObj);
    })
    .finally(() => {
      loading.value = false;
      func();
    });
}
function updateUser() {
  loading.value = true;
function updateUser(formObj, func) {
  return userApi
    .updateUserInfo(formObj.value)
    .then(() => {
      emit('onSubmit', formObj);
    })
    .finally(() => {
      loading.value = false;
      func();
    });
}
function submit() {
function submit(formObj, func) {
  // è¡Œæ”¿åŒºåˆ’信息填充
  const a = formObj.value._locations;
  if (a[0]) {
@@ -276,7 +227,7 @@
  const c = formObj.value._scenetype;
  formObj.value.extension2 = c.value;
  return props.create ? createUser() : updateUser();
  return props.create ? createUser(formObj, func) : updateUser(formObj, func);
}
function cancel() {
@@ -286,20 +237,13 @@
watch(
  () => props.formInfo,
  (nValue) => {
    formObj.value = parseUserInfo(nValue);
    _formInfo.value = parseUserInfo(nValue);
  }
);
watch(
  () => props.active,
  (nValue) => {
    if (!nValue) {
      onReset();
    }
  }
);
watch(edit, (nValue) => {
watch(_edit, (nValue) => {
  emit('update:isEdit', nValue);
});
const pwLoading = ref(false);
</script>
vite.config.js
@@ -23,7 +23,7 @@
      include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
      resolvers: [
        ElementPlusResolver({
          importStyle: 'sass',
          importStyle: 'sass'
        }),
      ],
      dts: 'src/components.d.ts',
@@ -72,6 +72,6 @@
    },
  },
  server: {
    host: '0.0.0.0',
  },
    host: '0.0.0.0'
  }
});