lilinfeng пре 2 дана
родитељ
комит
1cc2d597da

+ 119 - 0
src/api/governmentCloud/statisticalCenter/statisticalCenter.js

@@ -0,0 +1,119 @@
+import request from "@/utils/request";
+
+// 获取云上运维统计
+export const getCOMS = (data) => {
+  return request({
+    url: `/api/system/DataInterface/630685585031299077/Actions/Preview`,
+    method: "POST",
+    data: data
+  });
+};
+
+
+// 获取云上运维统计
+export const getBCS = (data) => {
+  return request({
+    url: `/api/system/DataInterface/631517524877377541/Actions/Preview`,
+    method: "POST",
+    data: data
+  });
+};
+
+// 获取事件统计
+export const getES = (data) => {
+  return request({
+    url: `/api/system/DataInterface/631513567119540229/Actions/Preview`,
+    method: "POST",
+    data: data
+  });
+};
+
+
+
+
+// 获取厅局列表
+export const findOrganizeListWithUserRoleExtension = (data) => {
+  return request({
+    url: `/api/itsm/ZwyOrganize/getList`,
+    method: "POST",
+    data: data,
+    headers: {
+      'content-type': 'application/json',
+      'jnpf-origin': 'qw'
+    }
+  });
+};
+
+// 根据厅局查询资产
+export const getAssetByOrgId = (data) => {
+  return request({
+    url: `/api/asset/getAssetByOrgId`,
+    method: "GET",
+    data: data,
+    headers: {
+      'content-type': 'application/json',
+    }
+  });
+};
+
+
+// 资源列表获取系统列表
+export const getSystemInfoByOrgId = (data) => {
+  return request({
+    url: `/api/asset/getSystemInfoByOrgId`,
+    method: "GET",
+    data: data,
+    headers: {
+      'content-type': 'application/json',
+    }
+  });
+};
+
+
+// 根据厅局查询资源监控列表V2
+export const getAssetListByOrgIdV2 = (data) => {
+  return request({
+    url: `/api/asset/getAssetListByOrgIdV2`,
+    method: "POST",
+    data: data,
+    headers: {
+      'content-type': 'application/json',
+    }
+  });
+};
+
+// 实时监控中国地图统计
+export const getMonitorChinaMap = (data) => {
+  return request({
+    url: `/api/cloudSecurity/getMonitorChinaMap`,
+    method: "GET",
+    data: data,
+    headers: {
+      'content-type': 'application/json',
+    }
+  });
+};
+
+// 实时网络攻击统计查询
+export const getCyberattacks = (data) => {
+  return request({
+    url: `/api/cloudSecurity/getCyberattacks`,
+    method: "GET",
+    data: data,
+    headers: {
+      'content-type': 'application/json',
+    }
+  });
+};
+
+// 实时监控世界地图统计查询
+export const getMonitorWorldMap = (data) => {
+  return request({
+    url: `/api/cloudSecurity/getMonitorWorldMap`,
+    method: "GET",
+    data: data,
+    headers: {
+      'content-type': 'application/json',
+    }
+  });
+};

BIN
src/assets/images/check_shifang.png


BIN
src/assets/images/check_yunxing.png


BIN
src/assets/images/icon3.png


BIN
src/assets/images/icon4.png


+ 48 - 48
src/layout/classic/sidebar/Logo.vue

@@ -1,71 +1,71 @@
 <template>
-  <div class="sidebar-logo-container" :class="{'collapse':collapse}">
-    <transition name="sidebarLogoFade">
-      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
-        <el-image class="sidebar-logo" :src="define.comUrl+sysConfig.workLogoIcon"
-          v-if="sysConfig && sysConfig.workLogoIcon">
-          <template slot="error">
-            <img class="sidebar-logo" src="@/assets/images/jnpf.png" alt="">
-          </template>
-        </el-image>
-        <img src="@/assets/images/jnpf.png" class="sidebar-logo" v-else />
-      </router-link>
-      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
-        <el-image class="sidebar-logo" :src="define.comUrl+sysConfig.navigationIcon"
-          v-if="sysConfig && sysConfig.navigationIcon">
-          <template slot="error">
-            <img class="sidebar-logo" src="@/assets/images/jnpf1.png" alt="">
-          </template>
-        </el-image>
-        <img src="@/assets/images/jnpf1.png" class="sidebar-logo" v-else />
-      </router-link>
-    </transition>
-  </div>
+    <div class="sidebar-logo-container" :class="{'collapse':collapse}">
+        <transition name="sidebarLogoFade">
+            <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
+                <el-image class="sidebar-logo" :src="define.comUrl+sysConfig.logoIcon"
+                    v-if="sysConfig && sysConfig.logoIcon">
+                    <template slot="error">
+                        <img class="sidebar-logo" src="@/assets/images/jnpf.png" alt="">
+                    </template>
+                </el-image>
+                <img src="@/assets/images/jnpf.png" class="sidebar-logo" v-else />
+            </router-link>
+            <router-link v-else key="expand" class="sidebar-logo-link" to="/">
+                <el-image class="sidebar-logo" :src="define.comUrl+sysConfig.loginIcon"
+                    v-if="sysConfig && sysConfig.logoIcon">
+                    <template slot="error">
+                        <img class="sidebar-logo" src="@/assets/images/jnpf1.png" alt="">
+                    </template>
+                </el-image>
+                <img src="@/assets/images/jnpf1.png" class="sidebar-logo" v-else />
+            </router-link>
+        </transition>
+    </div>
 </template>
 
 <script>
 export default {
-  name: 'SidebarLogo',
-  props: {
-    collapse: {
-      type: Boolean,
-      required: true
+    name: 'SidebarLogo',
+    props: {
+        collapse: {
+            type: Boolean,
+            required: true
+        }
+    },
+    computed: {
+        sysConfig() {
+            return this.$store.state.settings.sysConfig
+        }
     }
-  },
-  computed: {
-    sysConfig() {
-      return this.$store.state.settings.sysConfig
-    }
-  }
 }
 </script>
 
 <style lang="scss" scoped>
 .sidebarLogoFade-enter-active {
-  transition: opacity 1.5s;
+    transition: opacity 1.5s;
 }
 
 .sidebarLogoFade-enter,
 .sidebarLogoFade-leave-to {
-  opacity: 0;
+    opacity: 0;
 }
 
 .sidebar-logo-container {
-  position: relative;
-  width: 100%;
-  height: 60px;
-  line-height: 60px;
-  background: #031e39;
-  text-align: center;
-  overflow: hidden;
-  .sidebar-logo-link {
-    height: 100%;
+    position: relative;
     width: 100%;
+    height: 60px;
+    line-height: 60px;
+    background: #031e39;
+    text-align: center;
+    overflow: hidden;
+    .sidebar-logo-link {
+        height: 100%;
+        width: 100%;
 
-    .sidebar-logo {
-      height: 100%;
-      width: 100%;
+        .sidebar-logo {
+            height: 100%;
+            width: 100%;
+        }
     }
-  }
 }
 </style>

+ 58 - 58
src/layout/classic/sidebar/index.vue

@@ -1,14 +1,14 @@
 <template>
-  <div :class="classObj">
-    <logo v-if="showLogo" :collapse="isCollapse" />
-    <el-scrollbar wrap-class="scrollbar-wrapper">
-      <el-menu :default-active="activeMenu" :collapse="isCollapse" :unique-opened="true"
-        :collapse-transition="false" mode="vertical" :default-openeds="defaultOpeneds">
-        <sidebar-item v-for="route in menuList" :key="route.enCode" :item="route"
-          :base-path="route.path" />
-      </el-menu>
-    </el-scrollbar>
-  </div>
+    <div :class="classObj">
+        <logo v-if="showLogo" :collapse="isCollapse" />
+        <el-scrollbar wrap-class="scrollbar-wrapper">
+            <el-menu :default-active="activeMenu" :collapse="isCollapse" :unique-opened="true"
+                :collapse-transition="false" mode="vertical" :default-openeds="defaultOpeneds">
+                <sidebar-item v-for="route in menuList" :key="route.enCode" :item="route"
+                    :base-path="route.path" />
+            </el-menu>
+        </el-scrollbar>
+    </div>
 </template>
 
 <script>
@@ -18,58 +18,58 @@ import SidebarItem from './SidebarItem'
 import variables from '@/styles/variables.scss'
 
 export default {
-  components: { SidebarItem, Logo },
-  data() {
-    return {
-      defaultOpeneds: []
-    }
-  },
-  computed: {
-    ...mapGetters([
-      'permission_routes',
-      'sidebar',
-      'menuList'
-    ]),
-    ...mapState({
-      slideClass: state => state.settings.slideClass,
-    }),
-    classObj() {
-      return {
-        [this.slideClass]: true,
-        'has-logo': this.showLogo
-      };
-    },
-    activeMenu() {
-      const route = this.$route
-      const { meta, path } = route
-      // if (meta.activeMenu) {
-      //   return meta.activeMenu
-      // }
-      return path
+    components: { SidebarItem, Logo },
+    data() {
+        return {
+            defaultOpeneds: []
+        }
     },
-    showLogo() {
-      return this.$store.state.settings.sidebarLogo
+    computed: {
+        ...mapGetters([
+            'permission_routes',
+            'sidebar',
+            'menuList'
+        ]),
+        ...mapState({
+            slideClass: state => state.settings.slideClass,
+        }),
+        classObj() {
+            return {
+                [this.slideClass]: true,
+                'has-logo': this.showLogo
+            };
+        },
+        activeMenu() {
+            const route = this.$route
+            const { meta, path } = route
+            // if (meta.activeMenu) {
+            //   return meta.activeMenu
+            // }
+            return path
+        },
+        showLogo() {
+            return this.$store.state.settings.sidebarLogo
+        },
+        variables() {
+            return variables
+        },
+        isCollapse() {
+            return !this.sidebar.opened
+        }
     },
-    variables() {
-      return variables
+    created() {
+        this.setDefaultOpeneds()
     },
-    isCollapse() {
-      return !this.sidebar.opened
-    }
-  },
-  created() {
-    this.setDefaultOpeneds()
-  },
-  methods: {
-    setDefaultOpeneds() {
-      const currPath = this.$route.path
-      if (currPath === '/home' || currPath === '/dashboard' || currPath === '/404') {
-        if (this.menuList.length && this.menuList[0].type === 1) {
-          this.defaultOpeneds.push(this.menuList[0].path)
-        }
-      }
+    methods: {
+        setDefaultOpeneds() {
+            const currPath = this.$route.path
+            if (currPath === '/home' || currPath === '/dashboard' || currPath === '/404') {
+                if (this.menuList.length && this.menuList[0].type === 1) {
+                    this.defaultOpeneds.push(this.menuList[0].path)
+                }
+            }
+        },
     },
-  },
 }
 </script>
 <style lang="scss" >

+ 30 - 30
src/layout/plain/sidebar/Logo.vue

@@ -1,46 +1,46 @@
 <template>
-  <div class="sidebar-logo-container">
-    <router-link class="sidebar-logo-link" to="/">
-      <el-image class="sidebar-logo" :src="define.comUrl+sysConfig.logoIcon"
-        v-if="sysConfig && sysConfig.logoIcon">
-        <template slot="error">
-          <img class="sidebar-logo" src="@/assets/images/jnpf.png" alt="">
-        </template>
-      </el-image>
-      <img src="@/assets/images/jnpf.png" class="sidebar-logo" v-else />
-    </router-link>
-  </div>
+    <div class="sidebar-logo-container">
+        <router-link class="sidebar-logo-link" to="/">
+            <el-image class="sidebar-logo" :src="define.comUrl+sysConfig.logoIcon"
+                v-if="sysConfig && sysConfig.logoIcon">
+                <template slot="error">
+                    <img class="sidebar-logo" src="@/assets/images/jnpf.png" alt="">
+                </template>
+            </el-image>
+            <img src="@/assets/images/jnpf.png" class="sidebar-logo" v-else />
+        </router-link>
+    </div>
 </template>
 
 <script>
 export default {
-  name: 'SidebarLogo',
-  computed: {
-    sysConfig() {
-      return this.$store.state.settings.sysConfig
+    name: 'SidebarLogo',
+    computed: {
+        sysConfig() {
+            return this.$store.state.settings.sysConfig
+        }
     }
-  }
 }
 </script>
 
 <style lang="scss" scoped>
 .sidebar-logo-container {
-  position: relative;
-  width: 100%;
-  height: 60px;
-  line-height: 60px;
-  background: #031e39;
-  text-align: center;
-  overflow: hidden;
-  border-bottom: 1px solid #031e39;
-  .sidebar-logo-link {
-    height: 100%;
+    position: relative;
     width: 100%;
+    height: 60px;
+    line-height: 60px;
+    background: #031e39;
+    text-align: center;
+    overflow: hidden;
+    border-bottom: 1px solid #031e39;
+    .sidebar-logo-link {
+        height: 100%;
+        width: 100%;
 
-    .sidebar-logo {
-      height: 60px;
-      width: auto;
+        .sidebar-logo {
+            height: 60px;
+            width: auto;
+        }
     }
-  }
 }
 </style>

+ 69 - 63
src/layout/plain/sidebar/index.vue

@@ -1,32 +1,38 @@
 <template>
-  <div :class="classObj">
-    <logo v-if="showLogo" :collapse="true" />
-    <el-scrollbar wrap-class="scrollbar-wrapper">
-      <el-menu :default-active="activeMenu" :collapse="true" :unique-opened="true"
-        :collapse-transition="false" mode="vertical">
-        <template v-for="route in menuList">
-          <el-submenu ref="subMenu" :index="route.path" popper-append-to-body
-            v-if="route.children && route.children.length"
-            :popper-class="`${slideClass} ${themeClass} ${layoutType}`" :key="route.path">
-            <template slot="title">
-              <item :icon="route.icon" :title="generateTitle(route.vueName,route.fullName)" />
-              <div class="mainTitle">{{generateTitle(route.vueName,route.fullName)}}</div>
-            </template>
-            <sidebar-item v-for="child in route.children" :key="child.id" :is-nest="true"
-              :item="child" :base-path="child.path" class="nest-menu" />
-          </el-submenu>
-          <template v-else>
-            <app-link :to="route.path" :key="route.path">
-              <el-menu-item :index="route.path" :class="{'submenu-title-noDropdown':true}">
-                <item :icon="route.icon" />
-                <div class="mainTitle">{{generateTitle(route.vueName,route.fullName)}}</div>
-              </el-menu-item>
-            </app-link>
-          </template>
-        </template>
-      </el-menu>
-    </el-scrollbar>
-  </div>
+    <div :class="classObj">
+        <logo v-if="showLogo" :collapse="true" />
+        <el-scrollbar wrap-class="scrollbar-wrapper">
+            <el-menu :default-active="activeMenu" :collapse="true" :unique-opened="true"
+                :collapse-transition="false" mode="vertical">
+                <template v-for="route in menuList">
+                    <el-submenu ref="subMenu" :index="route.path" popper-append-to-body
+                        v-if="route.children && route.children.length"
+                        :popper-class="`${slideClass} ${themeClass} ${layoutType}`"
+                        :key="route.path">
+                        <template slot="title">
+                            <item :icon="route.icon"
+                                :title="generateTitle(route.vueName,route.fullName)" />
+                            <div class="mainTitle">{{generateTitle(route.vueName,route.fullName)}}
+                            </div>
+                        </template>
+                        <sidebar-item v-for="child in route.children" :key="child.id"
+                            :is-nest="true" :item="child" :base-path="child.path"
+                            class="nest-menu" />
+                    </el-submenu>
+                    <template v-else>
+                        <app-link :to="route.path" :key="route.path">
+                            <el-menu-item :index="route.path"
+                                :class="{'submenu-title-noDropdown':true}">
+                                <item :icon="route.icon" />
+                                <div class="mainTitle">
+                                    {{generateTitle(route.vueName,route.fullName)}}</div>
+                            </el-menu-item>
+                        </app-link>
+                    </template>
+                </template>
+            </el-menu>
+        </el-scrollbar>
+    </div>
 </template>
 
 <script>
@@ -39,41 +45,41 @@ import Item from './Item'
 import AppLink from './Link'
 
 export default {
-  components: { SidebarItem, Logo, Item, AppLink },
-  computed: {
-    ...mapGetters([
-      'permission_routes',
-      'sidebar',
-      'menuList'
-    ]),
-    ...mapState({
-      slideClass: state => state.settings.slideClass,
-      themeClass: state => state.settings.themeClass,
-      layoutType: state => state.settings.layoutType
-    }),
-    classObj() {
-      return {
-        [this.slideClass]: true,
-        'has-logo': this.showLogo
-      };
+    components: { SidebarItem, Logo, Item, AppLink },
+    computed: {
+        ...mapGetters([
+            'permission_routes',
+            'sidebar',
+            'menuList'
+        ]),
+        ...mapState({
+            slideClass: state => state.settings.slideClass,
+            themeClass: state => state.settings.themeClass,
+            layoutType: state => state.settings.layoutType
+        }),
+        classObj() {
+            return {
+                [this.slideClass]: true,
+                'has-logo': this.showLogo
+            };
+        },
+        activeMenu() {
+            const route = this.$route
+            const { meta, path } = route
+            // if (meta.activeMenu) {
+            //   return meta.activeMenu
+            // }
+            return path
+        },
+        showLogo() {
+            return this.$store.state.settings.sidebarLogo
+        },
+        variables() {
+            return variables
+        },
     },
-    activeMenu() {
-      const route = this.$route
-      const { meta, path } = route
-      // if (meta.activeMenu) {
-      //   return meta.activeMenu
-      // }
-      return path
-    },
-    showLogo() {
-      return this.$store.state.settings.sidebarLogo
-    },
-    variables() {
-      return variables
-    },
-  },
-  methods: {
-    generateTitle
-  }
+    methods: {
+        generateTitle
+    }
 }
 </script>

+ 1 - 1
src/utils/apiUrl.js

@@ -1,7 +1,7 @@
 module.exports = {
   // 开发环境接口配置
   // APIURl: "http://10.0.0.157:30000",
-  // APIURl: "http://10.21.12.137:30000",
+  // APIURl: "http://10.21.12.228:30000",
   // APIURl: "http://192.168.103.104:58080",
   // APIURl: "https://testapi.shidaiyun.net",
   APIURl: "https://api.shidaiyun.net",

+ 7 - 2
src/views/governmentCloud/computerroommanagement/computerRoomQR/form.vue

@@ -102,8 +102,13 @@ export default {
                 remark: undefined,
             },
             tableRequiredData: {},
-            dataRule:
-            {
+            dataRule: {
+                computerRoom: [
+                    { required: true, message: '机房名称不能为空', trigger: 'change' }
+                ],
+                code: [
+                    { required: true, message: '编码不能为空', trigger: 'click' }
+                ],
             },
             childIndex: -1,
             isEdit: false,

+ 30 - 2
src/views/governmentCloud/labelmanagement/Detail.vue

@@ -20,6 +20,11 @@
                             </span>
                         </jnpf-form-tip-item>
                     </el-col>
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="标签分类" prop="color">
+                            <span>{{getTagClassificationLabel(dataForm.tagClassification)}}</span>
+                        </jnpf-form-tip-item>
+                    </el-col>
                     <el-col :span="24">
                         <jnpf-form-tip-item label="备注" prop="remark">
                             <p>{{dataForm.remark}}</p>
@@ -40,6 +45,8 @@ import request from '@/utils/request'
 import { getConfigData } from '@/api/onlineDev/visualDev'
 import jnpf from '@/utils/jnpf'
 import Detail from '@/views/basic/dynamicModel/list/detail'
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+
 import { thousandsFormat } from "@/components/Generator/utils/index"
 export default {
     components: { Detail },
@@ -62,16 +69,36 @@ export default {
             dataForm: {
 
             },
+            tagClassificationOption: []
 
         }
     },
     computed: {},
     watch: {},
-    created() {
-
+    async created() {
+        await this.getTypeOption()
     },
     mounted() { },
     methods: {
+        getTypeOption() {
+            return new Promise((resolve, reject) => {
+                getDictionaryDataSelector('tagClassification').then(res => {
+                    this.tagClassificationOption = res.data.list
+                    resolve(true)
+                })
+            })
+        },
+        getTagClassificationLabel(tagClassification) {
+            console.log(tagClassification)
+            console.log(this.tagClassificationOption)
+            let label = null
+            this.tagClassificationOption.forEach(o => {
+                if (o.enCode == tagClassification) {
+                    label = o.fullName
+                }
+            })
+            return label
+        },
         toDetail(defaultValue, modelId) {
             if (!defaultValue) return
             getConfigData(modelId).then(res => {
@@ -87,6 +114,7 @@ export default {
         dataInfo(dataAll) {
             let _dataAll = dataAll
             this.dataForm = _dataAll
+            this.dataForm.tagClassification = 1
         },
 
         init(id) {

+ 24 - 5
src/views/governmentCloud/labelmanagement/form.vue

@@ -16,13 +16,17 @@
                     </el-col>
                     <el-col :span="24">
                         <jnpf-form-tip-item label="标签颜色" align="left" prop="color">
-                            <!-- <JnpfInput v-model="dataForm.color" @change="changeData('color',-1)"
-                                placeholder="请输入" clearable :style='{"width":"100%"}'
-                                :maskConfig="maskConfig.color">
-                            </JnpfInput> -->
+
                             <el-color-picker v-model="dataForm.color"></el-color-picker>
                         </jnpf-form-tip-item>
                     </el-col>
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="标签分类" align="left" prop="color">
+                            <JnpfSelect v-model="dataForm.tagClassification"
+                                :options="tagClassificationOption" placeholder="请选择标签分类"
+                                :props="props" style="width:100%" />
+                        </jnpf-form-tip-item>
+                    </el-col>
                     <el-col :span="24">
                         <jnpf-form-tip-item label="备注" align="left" prop="remark">
                             <JnpfTextarea v-model="dataForm.remark"
@@ -92,6 +96,10 @@ export default {
             dataValueAll: {},
             addTableConf: {
             },
+            props: {
+                label: "fullName",
+                value: "enCode"
+            },
             //可选范围默认值
             ableAll: {
             },
@@ -107,6 +115,7 @@ export default {
                 labName: undefined,
                 color: undefined,
                 remark: undefined,
+                tagClassification: undefined
             },
             tableRequiredData: {},
             dataRule:
@@ -145,6 +154,7 @@ export default {
             //定位属性
             locationScope: {
             },
+            tagClassificationOption: []
         }
     },
     computed: {
@@ -153,13 +163,22 @@ export default {
 
     },
     watch: {},
-    created() {
+    async created() {
+        await this.getTypeOption()
         this.dataAll()
         this.initDefaultData()
         this.dataValueAll = JSON.parse(JSON.stringify(this.dataForm))
     },
     mounted() { },
     methods: {
+        getTypeOption() {
+            return new Promise((resolve, reject) => {
+                getDictionaryDataSelector('tagClassification').then(res => {
+                    this.tagClassificationOption = res.data.list
+                    resolve(true)
+                })
+            })
+        },
         prev() {
             this.index--
             if (this.index === 0) {

+ 30 - 1
src/views/governmentCloud/labelmanagement/index.vue

@@ -56,6 +56,12 @@
                             </span>
                         </template>
                     </el-table-column>
+                    <el-table-column prop="tagClassification" label="所属分类" align="left"
+                        show-overflow-tooltip>
+                        <template slot-scope="scope">
+                            <span>{{getTagClassificationLabel(scope.row.tagClassification)}}</span>
+                        </template>
+                    </el-table-column>
                     <el-table-column prop="createBy" label="创建人" align="left" show-overflow-tooltip>
                     </el-table-column>
                     <el-table-column prop="createTime" label="创建时间" align="left"
@@ -175,6 +181,7 @@ export default {
                 labName: { "prefixType": 1, "useUnrealMask": false, "maskType": 1, "unrealMaskLength": 1, "prefixLimit": 0, "suffixLimit": 0, "filler": "*", "prefixSpecifyChar": "", "suffixType": 1, "ignoreChar": "", "suffixSpecifyChar": "" },
                 color: { "prefixType": 1, "useUnrealMask": false, "maskType": 1, "unrealMaskLength": 1, "prefixLimit": 0, "suffixLimit": 0, "filler": "*", "prefixSpecifyChar": "", "suffixType": 1, "ignoreChar": "", "suffixSpecifyChar": "" },
             },
+            tagClassificationOption: [],
         }
     },
     computed: {
@@ -183,7 +190,9 @@ export default {
             return this.$route.meta.modelId || ''
         }
     },
-    created() {
+    async created() {
+        await this.getTypeOption()
+
         this.getColumnList(),
             this.initSearchDataAndListData()
         this.queryData = JSON.parse(JSON.stringify(this.query))
@@ -192,6 +201,25 @@ export default {
         this.setDefaultQuery(this.defaultSortConfig);
     },
     methods: {
+        getTypeOption() {
+            return new Promise((resolve, reject) => {
+                getDictionaryDataSelector('tagClassification').then(res => {
+                    this.tagClassificationOption = res.data.list
+                    resolve(true)
+                })
+            })
+        },
+        getTagClassificationLabel(tagClassification) {
+            console.log(tagClassification)
+            console.log(this.tagClassificationOption)
+            let label = null
+            this.tagClassificationOption.forEach(o => {
+                if (o.enCode == tagClassification) {
+                    label = o.fullName
+                }
+            })
+            return label
+        },
         getHasBatchBtn() {
             let btnsList = []
             this.hasBatchBtn = btnsList.some(o => ['batchRemove', 'batchPrint', 'download'].includes(o))
@@ -396,6 +424,7 @@ export default {
                 this.list = _list.map(o => ({
                     ...o,
                     ...this.expandObj,
+                    tagClassification: 1
                 }))
                 this.total = res.data.pagination.total
                 this.listLoading = false

+ 296 - 0
src/views/governmentCloud/labelmanagementStatistics/index.vue

@@ -0,0 +1,296 @@
+<template>
+    <div class="JNPF-common-layout">
+        <div class="JNPF-common-layout-center">
+            <div class="JNPF-common-layout-main">
+                <div class="search-button">
+                    <el-button type="primary" @click="search()">查询</el-button>
+                </div>
+                <div class="form">
+                    <div class="form-item">
+                        <div class="item-title">标签分类</div>
+                        <div class="item-content">
+                            <el-select v-model="query.tagClassification" placeholder="请选择分类">
+                                <el-option v-for="item in tagClassificationOption"
+                                    :key="item.enCode" :label="item.fullName" :value="item.enCode">
+                                </el-option>
+                            </el-select>
+
+                        </div>
+                    </div>
+
+                </div>
+                <div class="chart" v-loading="loading" element-loading-text="加载中">
+                    <div v-if="chartShow" id="echarts1" style="width: 100%;height: 100%;"></div>
+                    <el-empty style="height: 100%;" v-else description="暂无数据"></el-empty>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+
+
+<script>
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+
+import {
+    getBCS
+} from "@/api/governmentCloud/statisticalCenter/statisticalCenter";
+export default {
+    data() {
+        return {
+            timeNum: '1',
+            timeList: [
+                {
+                    value: '%Y',
+                    label: '按年查询',
+                },
+                {
+                    value: '%Y-%m',
+                    label: '按月查询',
+                },
+                {
+                    value: '%Y-%m-%d',
+                    label: '按日查询',
+                }
+            ],
+
+            query: {
+                tagClassification: null
+            },
+            chartData: {
+                xlist: [],
+                series: []
+            },
+            loading: false,
+            typeList: [],
+            chartShow: false,
+            tagClassificationOption: [],
+        }
+    },
+    async created() {
+        await this.getTypeOption()
+        this.getTypeList()
+    },
+    watch: {
+        timeNum() {
+            this.query.timeRange = ''
+        },
+        chartData(newValue, oldValue) {
+            if (newValue) {
+                this.$nextTick(() => {
+                    this.initEchart1()
+                })
+            }
+        }
+    },
+    mounted() {
+    },
+    methods: {
+        getTypeOption() {
+            return new Promise((resolve, reject) => {
+                getDictionaryDataSelector('tagClassification').then(res => {
+                    this.tagClassificationOption = res.data.list
+                    resolve(true)
+                })
+            })
+        },
+        getTypeName(value) {
+            let obj = this.typeList.find(item => item.enCode == value)
+            return obj.fullName
+        },
+        getTypeList() {
+            getDictionaryDataSelector('zixun').then(res => {
+                this.typeList = res.data.list
+            })
+        },
+        search() {
+            this.chartShow = false
+
+            let str = null
+            if (!this.query.tagClassification) {
+                str = '请选择分类'
+            }
+            if (!str) {
+                let params = {
+                    ...this.query
+                }
+
+                this.chartData = null
+                this.loading = true
+                getBCS(params).then(res => {
+                    if (res.code == 200) {
+
+                        let data = res.data
+                        let xlist = []
+
+                        let dataList = []
+                        data.forEach(item => {
+                            if (!xlist.includes(item.x)) {
+                                xlist.push(item.x)
+                            }
+
+
+                            if (dataList.length == 0) {
+                                let obj = {
+                                    name: item.z,
+                                    data: [{ y: item.y, x: item.x }]
+                                }
+                                dataList.push(obj)
+                            } else {
+                                dataList.forEach(dataItem => {
+                                    // if (dataItem.name == item.z) {
+                                    if (dataList.some(item1 => item1.name == item.z) && dataItem.name == item.z) {
+                                        dataItem.data.push({ y: item.y, x: item.x })
+                                        return
+                                    } else if (!dataList.some(item1 => item1.name == item.z)) {
+                                        let obj = {
+                                            name: item.z,
+                                            data: [{ y: item.y, x: item.x }]
+                                        }
+                                        dataList.push(obj)
+                                        return
+                                    }
+                                })
+                            }
+
+                        })
+
+                        console.log(xlist)
+                        console.log(dataList)
+
+
+                        this.chartData = {
+                            xlist: xlist,
+                            series: [],
+                            legend: []
+                        }
+
+
+                        this.typeList.forEach(item => {
+                            this.chartData.legend.push(this.getTypeName(item.enCode))
+                        })
+
+
+                        dataList.forEach(item => {
+                            let obj = {
+                                name: this.getTypeName(item.name),
+                                type: 'line',
+                                stack: item.name,
+                                data: []
+                            }
+                            xlist.forEach(xItem => {
+                                if (item.data.some(item1 => item1.x == xItem)) {
+                                    obj.data.push(item.data.find(item1 => item1.x == xItem).y)
+                                } else {
+                                    obj.data.push('')
+                                }
+                            })
+
+                            this.chartData.series.push(obj)
+                        })
+
+
+                        console.log(this.chartData)
+
+
+                        this.loading = false
+
+
+                        if (this.chartData.series.length == 0) {
+                            this.$message({ message: '暂无数据', type: 'warning', duration: 1000 })
+                        } else {
+                            this.chartShow = true
+                        }
+                    } else {
+                        this.$message({ message: res.msg, type: 'error', duration: 1000 })
+                        this.chartData = null
+                        this.loading = false
+                    }
+
+                })
+            } else {
+                this.$message({ message: str, type: 'error', duration: 1000 })
+
+            }
+        },
+        initEchart1() {
+            var chartDom = document.getElementById('echarts1');
+            var myChart = this.$echarts.init(chartDom);
+
+
+            let xData = this.chartData.xlist
+            let series = this.chartData.series
+            var option = {
+                xAxis: {
+                    type: 'category',
+                    data: xData
+                },
+                yAxis: {
+                    type: 'value'
+                },
+                tooltip: {
+                    trigger: 'axis'
+                },
+                legend: {
+                    data: this.chartData.legend
+                },
+                grid: {
+                    left: '2%',
+                    right: '2%',
+                    bottom: '2%',
+                    containLabel: true
+                },
+                series: series
+            };
+            myChart.setOption(option);
+        }
+    },
+
+}
+</script>
+
+<style lang="scss" scoped>
+.JNPF-common-layout-main {
+    display: flex;
+    flex-direction: column;
+    .search-button {
+        position: absolute;
+        right: 20px;
+        top: 10px;
+    }
+    .form {
+        width: 100%;
+        padding: 10px 20px;
+        .form-item {
+            font-size: 16px;
+            display: flex;
+            align-items: center;
+            margin-bottom: 20px;
+            .item-title {
+                color: #b1b2b3;
+            }
+            .item-content {
+                margin-left: 30px;
+                display: flex;
+                align-items: center;
+                .content-item {
+                    padding: 10px 30px;
+                    cursor: pointer;
+                    border-radius: 20px;
+                    margin-right: 10px;
+                    text-wrap: nowrap;
+                }
+                .active-content-item {
+                    background-color: #d4e8fb;
+                    color: #409eff;
+                }
+            }
+        }
+    }
+    .chart {
+        height: auto;
+        flex: 1;
+    }
+}
+</style>

+ 9 - 0
src/views/governmentCloud/processConfiguration/index.vue

@@ -305,6 +305,15 @@ export default {
             });
         },
         formSubmit() {
+
+            if (!this.form.itsmProName) {
+                this.$message({
+                    message: "请选择流程",
+                    type: "error",
+                    duration: 1500
+                });
+                return
+            }
             let params = {
                 name: this.form.itsmProName,
                 isFirst: true,

+ 20 - 1
src/views/permission/user/Form.vue

@@ -48,6 +48,14 @@
                                 </el-radio-group>
                             </el-form-item>
                         </el-col>
+                        <el-col :sm="12" :xs="24">
+                            <el-form-item label="人员类型" prop="type">
+                                <el-radio-group v-model="dataForm.type">
+                                    <el-radio :label="item.enCode" v-for="item in typeOptions"
+                                        :key="item.enCode">{{item.fullName}}</el-radio>
+                                </el-radio-group>
+                            </el-form-item>
+                        </el-col>
                         <el-col :sm="12" :xs="24">
                             <el-form-item label="电子邮箱" prop="email">
                                 <el-input v-model="dataForm.email" placeholder="电子邮箱" />
@@ -268,7 +276,8 @@ export default {
                 urgentTelePhone: '',
                 postalAddress: '',
                 ranks: '',
-                itsmUser: false
+                itsmUser: false,
+                type: ''
             },
             ranksList: [],
             roleId: [],
@@ -276,6 +285,9 @@ export default {
             positionId: [],
             positionTreeData: [],
             roleTreeData: [],
+            typeOptions: [
+
+            ],
             groupTreeData: [],
             genderTreeData: [],
             nationTreeData: [],
@@ -327,6 +339,7 @@ export default {
             this.positionId = []
             this.positionTreeData = []
             this.roleTreeData = []
+            this.typeOptions = []
             this.dataForm.organizeIdTree = []
             this.$nextTick(() => {
                 this.formLoading = true
@@ -354,6 +367,12 @@ export default {
                     this.$store.dispatch('base/getDictionaryData', { sort: 'Rank' }).then(res => {
                         this.ranksList = res
                     })
+                    //获取人员类型
+                    this.$store.dispatch('base/getDictionaryData', { sort: 'personnelType' }).then(res => {
+                        this.typeOptions = res
+                        console.log('typeOptions')
+                        console.log(this.typeOptions)
+                    })
                 })
                 if (this.dataForm.id) {
                     getUserInfo(this.dataForm.id).then(res => {

+ 2 - 0
src/views/permission/user/TagForm.vue

@@ -12,6 +12,8 @@
                         </span>
                     </el-option>
                 </el-select>
+                <!-- <JnpfTreeSelect v-model="dataForm.parentId" :options="treeData" placeholder="请选择标签"
+                    :disabled="parentDisabled" /> -->
             </el-form-item>
         </el-form>
         <span slot="footer" class="dialog-footer">

+ 329 - 0
src/views/statisticalCenter/BCS/index.vue

@@ -0,0 +1,329 @@
+<template>
+    <div class="JNPF-common-layout">
+        <div class="JNPF-common-layout-center">
+            <div class="JNPF-common-layout-main">
+                <div class="search-button">
+                    <el-button type="primary" @click="search()">查询</el-button>
+                </div>
+                <div class="form">
+                    <div class="form-item">
+                        <div class="item-title">数据时间</div>
+                        <div class="item-content">
+                            <div class="content-item"
+                                :class="timeNum == item.value ? 'active-content-item' : ''"
+                                v-for="(item,index) in timeList" :key="index"
+                                @click="timeNum = item.value">
+                                {{ item.label }}</div>
+
+                            <el-date-picker style="margin-left: 20px;" v-model="query.timeRange"
+                                type="daterange" range-separator="至" start-placeholder="开始日期"
+                                end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss">
+                            </el-date-picker>
+                        </div>
+                    </div>
+
+                </div>
+                <div class="chart" v-loading="loading" element-loading-text="加载中">
+                    <div v-if="chartShow" id="echarts1" style="width: 100%;height: 100%;"></div>
+                    <el-empty style="height: 100%;" v-else description="暂无数据"></el-empty>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+
+
+<script>
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+
+import {
+    getBCS
+} from "@/api/governmentCloud/statisticalCenter/statisticalCenter";
+export default {
+    data() {
+        return {
+            timeNum: '1',
+            timeList: [
+                {
+                    value: '%Y',
+                    label: '按年查询',
+                },
+                {
+                    value: '%Y-%m',
+                    label: '按月查询',
+                },
+                {
+                    value: '%Y-%m-%d',
+                    label: '按日查询',
+                }
+            ],
+
+            query: {
+                timeRange: ''
+            },
+            chartData: {
+                xlist: [],
+                series: []
+            },
+            loading: false,
+            typeList: [],
+            chartShow: false
+        }
+    },
+    created() {
+        this.getTypeList()
+    },
+    watch: {
+        timeNum() {
+            this.query.timeRange = ''
+        },
+        chartData(newValue, oldValue) {
+            if (newValue) {
+                this.$nextTick(() => {
+                    this.initEchart1()
+                })
+            }
+        }
+    },
+    mounted() {
+    },
+    methods: {
+        getTypeName(value) {
+            let obj = this.typeList.find(item => item.enCode == value)
+            return obj.fullName
+        },
+        getTypeList() {
+            getDictionaryDataSelector('zixun').then(res => {
+                this.typeList = res.data.list
+            })
+        },
+        changeMonth(value) {
+            //查询当前月份月初到月末
+            let myDate = new Date(value[1]);
+            let month = myDate.getMonth() + 1;
+            month = month < 10 ? "0" + month : month;   //格式化月份,补0
+            let dayEnd = new Date(myDate.getFullYear(), month, 0).getDate(); //获取当月一共有多少天
+            value[1] = value[1].split('-')[0] + '-' + value[1].split('-')[1] + '-' + dayEnd
+            this.query.timeRange = [value[0], value[1]]
+        },
+        search() {
+            this.chartShow = false
+
+            let str = null
+            if (!this.query.timeRange) {
+                str = '请选择时间'
+            }
+            if (!str) {
+                let params = {
+                    "paramList": [
+                        {
+                            "id": "cC1kgO2",
+                            "field": "startTime",
+                            "defaultValue": this.query.timeRange[0],
+                            "fieldName": "开始时间",
+                            "dataType": "varchar",
+                            "required": 1
+                        },
+                        {
+                            "id": "YM8kgO2",
+                            "field": "endTime",
+                            "defaultValue": this.query.timeRange[1],
+                            "fieldName": "结束时间",
+                            "dataType": "varchar",
+                            "required": 1
+                        },
+                        {
+                            "id": "wXDkgO2",
+                            "field": "type",
+                            "defaultValue": this.timeNum,
+                            "fieldName": "类型(年:%Y;月:%Y-%m;日:%Y-%m-%d)",
+                            "dataType": "varchar",
+                            "required": 1
+                        }
+                    ],
+                    "tenantId": "",
+                    "origin": "preview"
+                }
+
+
+
+
+                this.chartData = null
+                this.loading = true
+                getBCS(params).then(res => {
+                    if (res.code == 200) {
+
+                        let data = res.data
+                        let xlist = []
+
+                        let dataList = []
+                        data.forEach(item => {
+                            if (!xlist.includes(item.x)) {
+                                xlist.push(item.x)
+                            }
+
+
+                            if (dataList.length == 0) {
+                                let obj = {
+                                    name: item.z,
+                                    data: [{ y: item.y, x: item.x }]
+                                }
+                                dataList.push(obj)
+                            } else {
+                                dataList.forEach(dataItem => {
+                                    // if (dataItem.name == item.z) {
+                                    if (dataList.some(item1 => item1.name == item.z) && dataItem.name == item.z) {
+                                        dataItem.data.push({ y: item.y, x: item.x })
+                                        return
+                                    } else if (!dataList.some(item1 => item1.name == item.z)) {
+                                        let obj = {
+                                            name: item.z,
+                                            data: [{ y: item.y, x: item.x }]
+                                        }
+                                        dataList.push(obj)
+                                        return
+                                    }
+                                })
+                            }
+
+                        })
+
+                        console.log(xlist)
+                        console.log(dataList)
+
+
+                        this.chartData = {
+                            xlist: xlist,
+                            series: [],
+                            legend: []
+                        }
+
+
+                        this.typeList.forEach(item => {
+                            this.chartData.legend.push(this.getTypeName(item.enCode))
+                        })
+
+
+                        dataList.forEach(item => {
+                            let obj = {
+                                name: this.getTypeName(item.name),
+                                type: 'line',
+                                stack: item.name,
+                                data: []
+                            }
+                            xlist.forEach(xItem => {
+                                if (item.data.some(item1 => item1.x == xItem)) {
+                                    obj.data.push(item.data.find(item1 => item1.x == xItem).y)
+                                } else {
+                                    obj.data.push('')
+                                }
+                            })
+
+                            this.chartData.series.push(obj)
+                        })
+
+
+                        console.log(this.chartData)
+
+
+                        this.loading = false
+
+
+                        if (this.chartData.series.length == 0) {
+                            this.$message({ message: '暂无数据', type: 'warning', duration: 1000 })
+                        } else {
+                            this.chartShow = true
+                        }
+                    } else {
+                        this.$message({ message: res.msg, type: 'error', duration: 1000 })
+                        this.chartData = null
+                        this.loading = false
+                    }
+
+                })
+            } else {
+                this.$message({ message: str, type: 'error', duration: 1000 })
+
+            }
+        },
+        initEchart1() {
+            var chartDom = document.getElementById('echarts1');
+            var myChart = this.$echarts.init(chartDom);
+
+
+            let xData = this.chartData.xlist
+            let series = this.chartData.series
+            var option = {
+                xAxis: {
+                    type: 'category',
+                    data: xData
+                },
+                yAxis: {
+                    type: 'value'
+                },
+                tooltip: {
+                    trigger: 'axis'
+                },
+                legend: {
+                    data: this.chartData.legend
+                },
+                grid: {
+                    left: '2%',
+                    right: '2%',
+                    bottom: '2%',
+                    containLabel: true
+                },
+                series: series
+            };
+            myChart.setOption(option);
+        }
+    },
+
+}
+</script>
+
+<style lang="scss" scoped>
+.JNPF-common-layout-main {
+    display: flex;
+    flex-direction: column;
+    .search-button {
+        position: absolute;
+        right: 20px;
+        top: 10px;
+    }
+    .form {
+        width: 100%;
+        padding: 10px 20px;
+        .form-item {
+            font-size: 16px;
+            display: flex;
+            align-items: center;
+            margin-bottom: 20px;
+            .item-title {
+                color: #b1b2b3;
+            }
+            .item-content {
+                margin-left: 30px;
+                display: flex;
+                align-items: center;
+                .content-item {
+                    padding: 10px 30px;
+                    cursor: pointer;
+                    border-radius: 20px;
+                    margin-right: 10px;
+                    text-wrap: nowrap;
+                }
+                .active-content-item {
+                    background-color: #d4e8fb;
+                    color: #409eff;
+                }
+            }
+        }
+    }
+    .chart {
+        height: auto;
+        flex: 1;
+    }
+}
+</style>

+ 330 - 0
src/views/statisticalCenter/COMS/index.vue

@@ -0,0 +1,330 @@
+<template>
+    <div class="JNPF-common-layout">
+        <div class="JNPF-common-layout-center">
+            <div class="JNPF-common-layout-main">
+                <div class="search-button">
+                    <el-button type="primary" @click="search()">查询</el-button>
+                </div>
+                <div class="form">
+                    <div class="form-item">
+                        <div class="item-title">数据时间</div>
+                        <div class="item-content">
+                            <div class="content-item"
+                                :class="timeNum == item.value ? 'active-content-item' : ''"
+                                v-for="(item,index) in timeList" :key="index"
+                                @click="timeNum = item.value">
+                                {{ item.label }}</div>
+
+                            <el-date-picker style="margin-left: 20px;" v-model="query.timeRange"
+                                type="daterange" range-separator="至" start-placeholder="开始日期"
+                                end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss">
+                            </el-date-picker>
+                        </div>
+                    </div>
+
+                </div>
+                <div class="chart" v-loading="loading" element-loading-text="加载中">
+                    <div v-if="chartShow" id="echarts1" style="width: 100%;height: 100%;">
+                    </div>
+                    <el-empty style="height: 100%;" v-else description="暂无数据"></el-empty>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+
+
+<script>
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+
+import {
+    getCOMS
+} from "@/api/governmentCloud/statisticalCenter/statisticalCenter";
+export default {
+    data() {
+        return {
+            timeNum: '1',
+            timeList: [
+                {
+                    value: '%Y',
+                    label: '按年查询',
+                },
+                {
+                    value: '%Y-%m',
+                    label: '按月查询',
+                },
+                {
+                    value: '%Y-%m-%d',
+                    label: '按日查询',
+                }
+            ],
+
+            query: {
+                timeRange: ''
+            },
+            chartData: {
+                xlist: [],
+                series: []
+            },
+            loading: false,
+            typeList: [],
+            chartShow: false
+        }
+    },
+    created() {
+        this.getTypeList()
+    },
+    watch: {
+        timeNum() {
+            this.query.timeRange = ''
+        },
+        chartData(newValue, oldValue) {
+            if (newValue) {
+                this.$nextTick(() => {
+                    this.initEchart1()
+                })
+            }
+        }
+    },
+    mounted() {
+    },
+    methods: {
+        getTypeName(value) {
+            let obj = this.typeList.find(item => item.enCode == value)
+            return obj.fullName
+        },
+        getTypeList() {
+            getDictionaryDataSelector('modetype').then(res => {
+                this.typeList = res.data.list
+            })
+        },
+        changeMonth(value) {
+            //查询当前月份月初到月末
+            let myDate = new Date(value[1]);
+            let month = myDate.getMonth() + 1;
+            month = month < 10 ? "0" + month : month;   //格式化月份,补0
+            let dayEnd = new Date(myDate.getFullYear(), month, 0).getDate(); //获取当月一共有多少天
+            value[1] = value[1].split('-')[0] + '-' + value[1].split('-')[1] + '-' + dayEnd
+            this.query.timeRange = [value[0], value[1]]
+        },
+        search() {
+            this.chartShow = false
+            let str = null
+            if (!this.query.timeRange) {
+                str = '请选择时间'
+            }
+            if (!str) {
+                let params = {
+                    "paramList": [
+                        {
+                            "id": "cC1kgO2",
+                            "field": "startTime",
+                            "defaultValue": this.query.timeRange[0],
+                            "fieldName": "开始时间",
+                            "dataType": "varchar",
+                            "required": 1
+                        },
+                        {
+                            "id": "YM8kgO2",
+                            "field": "endTime",
+                            "defaultValue": this.query.timeRange[1],
+                            "fieldName": "结束时间",
+                            "dataType": "varchar",
+                            "required": 1
+                        },
+                        {
+                            "id": "wXDkgO2",
+                            "field": "type",
+                            "defaultValue": this.timeNum,
+                            "fieldName": "类型(年:%Y;月:%Y-%m;日:%Y-%m-%d)",
+                            "dataType": "varchar",
+                            "required": 1
+                        }
+                    ],
+                    "tenantId": "",
+                    "origin": "preview"
+                }
+
+
+
+
+                this.chartData = null
+                this.loading = true
+                getCOMS(params).then(res => {
+                    if (res.code == 200) {
+
+                        let data = res.data
+                        let xlist = []
+
+                        let dataList = []
+                        data.forEach(item => {
+                            if (!xlist.includes(item.x)) {
+                                xlist.push(item.x)
+                            }
+
+
+                            if (dataList.length == 0) {
+                                let obj = {
+                                    name: item.z,
+                                    data: [{ y: item.y, x: item.x }]
+                                }
+                                dataList.push(obj)
+                            } else {
+                                dataList.forEach(dataItem => {
+                                    // if (dataItem.name == item.z) {
+                                    if (dataList.some(item1 => item1.name == item.z) && dataItem.name == item.z) {
+                                        dataItem.data.push({ y: item.y, x: item.x })
+                                        return
+                                    } else if (!dataList.some(item1 => item1.name == item.z)) {
+                                        let obj = {
+                                            name: item.z,
+                                            data: [{ y: item.y, x: item.x }]
+                                        }
+                                        dataList.push(obj)
+                                        return
+                                    }
+                                })
+                            }
+
+                        })
+
+                        console.log(xlist)
+                        console.log(dataList)
+
+
+                        this.chartData = {
+                            xlist: xlist,
+                            series: [],
+                            legend: []
+                        }
+
+
+                        this.typeList.forEach(item => {
+                            this.chartData.legend.push(this.getTypeName(item.enCode))
+                        })
+
+
+                        dataList.forEach(item => {
+                            let obj = {
+                                name: this.getTypeName(item.name),
+                                type: 'line',
+                                stack: item.name,
+                                data: []
+                            }
+                            xlist.forEach(xItem => {
+                                if (item.data.some(item1 => item1.x == xItem)) {
+                                    obj.data.push(item.data.find(item1 => item1.x == xItem).y)
+                                } else {
+                                    obj.data.push('')
+                                }
+                            })
+
+                            this.chartData.series.push(obj)
+                        })
+
+
+                        console.log(this.chartData)
+
+
+                        this.loading = false
+
+                        if (this.chartData.series.length == 0) {
+                            this.$message({ message: '暂无数据', type: 'warning', duration: 1000 })
+                        } else {
+                            this.chartShow = true
+                        }
+
+
+                    } else {
+                        this.$message({ message: res.msg, type: 'error', duration: 1000 })
+                        this.chartData = null
+                        this.loading = false
+                    }
+
+                })
+            } else {
+                this.$message({ message: str, type: 'error', duration: 1000 })
+
+            }
+        },
+        initEchart1() {
+            var chartDom = document.getElementById('echarts1');
+            var myChart = this.$echarts.init(chartDom);
+
+
+            let xData = this.chartData.xlist
+            let series = this.chartData.series
+            var option = {
+                xAxis: {
+                    type: 'category',
+                    data: xData
+                },
+                yAxis: {
+                    type: 'value'
+                },
+                tooltip: {
+                    trigger: 'axis'
+                },
+                legend: {
+                    data: this.chartData.legend
+                },
+                grid: {
+                    left: '2%',
+                    right: '2%',
+                    bottom: '2%',
+                    containLabel: true
+                },
+                series: series
+            };
+            myChart.setOption(option);
+        }
+    },
+
+}
+</script>
+
+<style lang="scss" scoped>
+.JNPF-common-layout-main {
+    display: flex;
+    flex-direction: column;
+    .search-button {
+        position: absolute;
+        right: 20px;
+        top: 10px;
+    }
+    .form {
+        width: 100%;
+        padding: 10px 20px;
+        .form-item {
+            font-size: 16px;
+            display: flex;
+            align-items: center;
+            margin-bottom: 20px;
+            .item-title {
+                color: #b1b2b3;
+            }
+            .item-content {
+                margin-left: 30px;
+                display: flex;
+                align-items: center;
+                .content-item {
+                    padding: 10px 30px;
+                    cursor: pointer;
+                    border-radius: 20px;
+                    margin-right: 10px;
+                    text-wrap: nowrap;
+                }
+                .active-content-item {
+                    background-color: #d4e8fb;
+                    color: #409eff;
+                }
+            }
+        }
+    }
+    .chart {
+        height: auto;
+        flex: 1;
+    }
+}
+</style>

+ 330 - 0
src/views/statisticalCenter/ES/index.vue

@@ -0,0 +1,330 @@
+<template>
+    <div class="JNPF-common-layout">
+        <div class="JNPF-common-layout-center">
+            <div class="JNPF-common-layout-main">
+                <div class="search-button">
+                    <el-button type="primary" @click="search()">查询</el-button>
+                </div>
+                <div class="form">
+                    <div class="form-item">
+                        <div class="item-title">数据时间</div>
+                        <div class="item-content">
+                            <div class="content-item"
+                                :class="timeNum == item.value ? 'active-content-item' : ''"
+                                v-for="(item,index) in timeList" :key="index"
+                                @click="timeNum = item.value">
+                                {{ item.label }}</div>
+
+                            <el-date-picker style="margin-left: 20px;" v-model="query.timeRange"
+                                type="daterange" range-separator="至" start-placeholder="开始日期"
+                                end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:ss">
+                            </el-date-picker>
+                        </div>
+                    </div>
+
+                </div>
+                <div class="chart" v-loading="loading" element-loading-text="加载中">
+                    <div v-if="chartShow" id="echarts1" style="width: 100%;height: 100%;">
+                    </div>
+                    <el-empty style="height: 100%;" v-else description="暂无数据"></el-empty>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+
+
+<script>
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+
+import {
+    getES
+} from "@/api/governmentCloud/statisticalCenter/statisticalCenter";
+export default {
+    data() {
+        return {
+            timeNum: '1',
+            timeList: [
+                {
+                    value: '%Y',
+                    label: '按年查询',
+                },
+                {
+                    value: '%Y-%m',
+                    label: '按月查询',
+                },
+                {
+                    value: '%Y-%m-%d',
+                    label: '按日查询',
+                }
+            ],
+
+            query: {
+                timeRange: ''
+            },
+            chartData: {
+                xlist: [],
+                series: []
+            },
+            loading: false,
+            typeList: [],
+            chartShow: false
+        }
+    },
+    created() {
+        this.getTypeList()
+    },
+    watch: {
+        timeNum() {
+            this.query.timeRange = ''
+        },
+        chartData(newValue, oldValue) {
+            if (newValue) {
+                this.$nextTick(() => {
+                    this.initEchart1()
+                })
+            }
+        }
+    },
+    mounted() {
+    },
+    methods: {
+        getTypeName(value) {
+            let obj = this.typeList.find(item => item.enCode == value)
+            return obj.fullName
+        },
+        getTypeList() {
+            getDictionaryDataSelector('eventLevel').then(res => {
+                this.typeList = res.data.list
+            })
+        },
+        changeMonth(value) {
+            //查询当前月份月初到月末
+            let myDate = new Date(value[1]);
+            let month = myDate.getMonth() + 1;
+            month = month < 10 ? "0" + month : month;   //格式化月份,补0
+            let dayEnd = new Date(myDate.getFullYear(), month, 0).getDate(); //获取当月一共有多少天
+            value[1] = value[1].split('-')[0] + '-' + value[1].split('-')[1] + '-' + dayEnd
+            this.query.timeRange = [value[0], value[1]]
+        },
+        search() {
+            this.chartShow = false
+            let str = null
+            if (!this.query.timeRange) {
+                str = '请选择时间'
+            }
+            if (!str) {
+                let params = {
+                    "paramList": [
+                        {
+                            "id": "cC1kgO2",
+                            "field": "startTime",
+                            "defaultValue": this.query.timeRange[0],
+                            "fieldName": "开始时间",
+                            "dataType": "varchar",
+                            "required": 1
+                        },
+                        {
+                            "id": "YM8kgO2",
+                            "field": "endTime",
+                            "defaultValue": this.query.timeRange[1],
+                            "fieldName": "结束时间",
+                            "dataType": "varchar",
+                            "required": 1
+                        },
+                        {
+                            "id": "wXDkgO2",
+                            "field": "type",
+                            "defaultValue": this.timeNum,
+                            "fieldName": "类型(年:%Y;月:%Y-%m;日:%Y-%m-%d)",
+                            "dataType": "varchar",
+                            "required": 1
+                        }
+                    ],
+                    "tenantId": "",
+                    "origin": "preview"
+                }
+
+
+
+
+                this.chartData = null
+                this.loading = true
+                getES(params).then(res => {
+                    if (res.code == 200) {
+
+                        let data = res.data
+                        let xlist = []
+
+                        let dataList = []
+                        data.forEach(item => {
+                            if (!xlist.includes(item.x)) {
+                                xlist.push(item.x)
+                            }
+
+
+                            if (dataList.length == 0) {
+                                let obj = {
+                                    name: item.z,
+                                    data: [{ y: item.y, x: item.x }]
+                                }
+                                dataList.push(obj)
+                            } else {
+                                dataList.forEach(dataItem => {
+                                    // if (dataItem.name == item.z) {
+                                    if (dataList.some(item1 => item1.name == item.z) && dataItem.name == item.z) {
+                                        dataItem.data.push({ y: item.y, x: item.x })
+                                        return
+                                    } else if (!dataList.some(item1 => item1.name == item.z)) {
+                                        let obj = {
+                                            name: item.z,
+                                            data: [{ y: item.y, x: item.x }]
+                                        }
+                                        dataList.push(obj)
+                                        return
+                                    }
+                                })
+                            }
+
+                        })
+
+                        console.log(xlist)
+                        console.log(dataList)
+
+
+                        this.chartData = {
+                            xlist: xlist,
+                            series: [],
+                            legend: []
+                        }
+
+
+                        this.typeList.forEach(item => {
+                            this.chartData.legend.push(this.getTypeName(item.enCode))
+                        })
+
+
+                        dataList.forEach(item => {
+                            let obj = {
+                                name: this.getTypeName(item.name),
+                                type: 'line',
+                                stack: item.name,
+                                data: []
+                            }
+                            xlist.forEach(xItem => {
+                                if (item.data.some(item1 => item1.x == xItem)) {
+                                    obj.data.push(item.data.find(item1 => item1.x == xItem).y)
+                                } else {
+                                    obj.data.push('')
+                                }
+                            })
+
+                            this.chartData.series.push(obj)
+                        })
+
+
+                        console.log(this.chartData)
+
+
+                        this.loading = false
+
+                        if (this.chartData.series.length == 0) {
+                            this.$message({ message: '暂无数据', type: 'warning', duration: 1000 })
+                        } else {
+                            this.chartShow = true
+                        }
+
+
+                    } else {
+                        this.$message({ message: res.msg, type: 'error', duration: 1000 })
+                        this.chartData = null
+                        this.loading = false
+                    }
+
+                })
+            } else {
+                this.$message({ message: str, type: 'error', duration: 1000 })
+
+            }
+        },
+        initEchart1() {
+            var chartDom = document.getElementById('echarts1');
+            var myChart = this.$echarts.init(chartDom);
+
+
+            let xData = this.chartData.xlist
+            let series = this.chartData.series
+            var option = {
+                xAxis: {
+                    type: 'category',
+                    data: xData
+                },
+                yAxis: {
+                    type: 'value'
+                },
+                tooltip: {
+                    trigger: 'axis'
+                },
+                legend: {
+                    data: this.chartData.legend
+                },
+                grid: {
+                    left: '2%',
+                    right: '2%',
+                    bottom: '2%',
+                    containLabel: true
+                },
+                series: series
+            };
+            myChart.setOption(option);
+        }
+    },
+
+}
+</script>
+
+<style lang="scss" scoped>
+.JNPF-common-layout-main {
+    display: flex;
+    flex-direction: column;
+    .search-button {
+        position: absolute;
+        right: 20px;
+        top: 10px;
+    }
+    .form {
+        width: 100%;
+        padding: 10px 20px;
+        .form-item {
+            font-size: 16px;
+            display: flex;
+            align-items: center;
+            margin-bottom: 20px;
+            .item-title {
+                color: #b1b2b3;
+            }
+            .item-content {
+                margin-left: 30px;
+                display: flex;
+                align-items: center;
+                .content-item {
+                    padding: 10px 30px;
+                    cursor: pointer;
+                    border-radius: 20px;
+                    margin-right: 10px;
+                    text-wrap: nowrap;
+                }
+                .active-content-item {
+                    background-color: #d4e8fb;
+                    color: #409eff;
+                }
+            }
+        }
+    }
+    .chart {
+        height: auto;
+        flex: 1;
+    }
+}
+</style>

+ 875 - 0
src/views/statisticalCenter/ROS/index.vue

@@ -0,0 +1,875 @@
+<template>
+    <div class="JNPF-common-layout">
+        <div class="top">
+            <div class="left"></div>
+            <div class="center">资源概况</div>
+            <div class="right">
+                <div class="now-time">{{ nowTime }}</div>
+                <el-select v-model="nowOrg" :popper-append-to-body="false" filterable
+                    placeholder="请选择厅局" @change="orgChange">
+                    <div>
+                        <el-option v-for="item in orgList" :key="item.orgId" :label="item.orgName"
+                            :value="item.orgId"> </el-option>
+                    </div>
+                </el-select>
+            </div>
+        </div>
+        <div class="content">
+            <div class="c1">
+                <div class="f1">
+                    <div class="rect-title">主机概况</div>
+                    <div class="chart" v-loading="loading" element-loading-text="加载中">
+                        <template v-if="!loading">
+                            <div class="echarts1" id="echarts1">
+                            </div>
+                            <div class="legend">
+                                <div class="xinchuang item">
+                                    <div class="doc"></div>
+                                    <span>信创主机</span>
+                                    <div class="num">{{chartData.xinChuangBaseInfo.count}}</div>
+                                    <span>个</span>
+                                </div>
+                                <div class="feixinchuang item">
+                                    <div class="doc" style="background-color: #ffca3c"></div>
+                                    <span>非信创主机</span>
+                                    <div class="num" style="color: #ffca3c">
+                                        {{chartData.notXinChuangBaseInfo.count}}</div>
+                                    <span>个</span>
+                                </div>
+                            </div>
+                        </template>
+                        <el-empty style="height: 100%;" v-else description="暂无数据"></el-empty>
+                    </div>
+                </div>
+                <div class="f2">
+                    <div class="rect-title">主机分类情况</div>
+                    <div class="overview-list" v-loading="loading" element-loading-text="加载中">
+                        <div class="overview-item" v-for="(item, index) in overviewList"
+                            :key="index">
+                            <div class="item-top">
+                                <img v-if="index == 1" src="@/assets/images/icon4.png"
+                                    mode=""></img>
+                                <img v-else src="@/assets/images/icon3.png" mode=""></img>
+                                <div class="">
+                                    {{ item.name }}
+                                </div>
+                            </div>
+                            <div class="list">
+                                <div class="list-item" v-for="(item2, index2) in item.children"
+                                    :key="index2">
+                                    <div class="num">
+                                        {{ item2.num || 0 }}
+                                    </div>
+                                    <div class="name">
+                                        {{ item2.label }}
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="c2">
+                <div class="t">
+                    <div class="rect-title">昨日利用率Top10</div>
+                    <div class="buttons">
+                        <span v-for="(item, index) in topButtons" :key="index"
+                            :class="nowTop == index ? 'now' : ''"
+                            @click="changeNowTopButton(index)">{{ item.label }}</span>
+                    </div>
+                </div>
+
+                <div class="list" v-loading="loading" element-loading-text="加载中">
+                    <div class="list-item" v-for="(item, index) in topButtons[nowTop].value"
+                        :key="index">
+                        <div class="left"
+                            :style="{ background: item.color ? item.color + '20' : '#f0f0f0', color: item.color || '#adadad' }">
+                            {{ index < 9 ? '0' + (index + 1) : index + 1 }}
+                        </div>
+                        <div class="center">
+                            <div>{{ item.assetName }}</div>
+                            <el-progress :percentage="item.count" :stroke-width="6"
+                                :color="item.color ? item.color : '#92d050'"></el-progress>
+                        </div>
+                        <div class="right"
+                            :style="{ color: item.color, fontWeight: 'bold', fontSize: '20px' }">
+                            {{ item.count || 0 }}%</div>
+                    </div>
+                </div>
+            </div>
+            <div class="c3">
+                <div class="t">
+                    <div class="rect-title">资源监控列表</div>
+                    <el-select v-model="nowSys" :popper-append-to-body="false" filterable
+                        collapse-tags :multiple="true" placeholder="请选择系统" @change="sysChange">
+                        <div>
+                            <el-option v-for="item in sysList" :key="item.systemName"
+                                :label="item.systemName" :value="item.systemName"> </el-option>
+                        </div>
+                    </el-select>
+
+                </div>
+                <div class="list" v-loading="!list.length" element-loading-text="加载中">
+                    <div class="list-item" v-for="(item, index) in list" :key="index"
+                        @click="toDetail(item)">
+                        <el-card class="box-card">
+                            <div slot="header" class="clearfix"
+                                style="display: flex;align-items: center;justify-content: space-between;">
+                                <div class="item-top">
+                                    <div class="left">
+                                        <div class="asset-name">
+                                            {{ item.assetName }}
+                                        </div>
+                                    </div>
+                                </div>
+                                <span class="status" style="float: right; padding: 3px 0"
+                                    :class="item.downTime ? 'down-status' : 'not-down-status'">
+                                    <img v-if="!item.downTime"
+                                        src="@/assets/images/check_shifang.png" mode="">
+                                    </img>
+                                    <img v-else src="@/assets/images/check_yunxing.png"
+                                        mode=""></img>
+                                    <div :class="item.downTime ? 'down-time' : 'not-down-time'">
+                                        {{ item.downTime ? '运行中' : '已释放' }}
+                                    </div>
+                                </span>
+                            </div>
+                            <div class="center">
+                                <div class="row">
+                                    <div class="label">IP地址</div>
+                                    <div class="value">
+                                        {{ item.ip }}
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="label">创建时间</div>
+                                    <div class="value">
+                                        {{ item.createTime }}
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="label">cpu(核)</div>
+                                    <div class="value">
+                                        {{ item.cpu }}
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="label">内存(G)</div>
+                                    <div class="value">
+                                        {{ item.memory }}
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="label">磁盘(G)</div>
+                                    <div class="value">
+                                        {{ item.disk }}
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="label">备份(G)</div>
+                                    <div class="value">
+                                        {{ item.backup }}
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="label">资源标识</div>
+                                    <div class="value">
+                                        {{ item.uuid }}
+                                    </div>
+                                </div>
+                                <div class="row">
+                                    <div class="label">是否信创</div>
+                                    <div class="value">
+                                        {{ item.xinchuang? '是' : '否' }}
+                                    </div>
+                                </div>
+                            </div>
+                        </el-card>
+
+                    </div>
+                </div>
+                <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
+                    :current-page="pageNum" :page-sizes="[10, 20]" :page-size="pageSize"
+                    layout="total, sizes, prev, pager, next, jumper" :total="total">
+                </el-pagination>
+            </div>
+        </div>
+
+    </div>
+</template>
+
+<script>
+import {
+    findOrganizeListWithUserRoleExtension, getAssetByOrgId, getSystemInfoByOrgId, getAssetListByOrgIdV2
+} from "@/api/governmentCloud/statisticalCenter/statisticalCenter";
+
+export default {
+    data() {
+        return {
+            nowTime: null,
+            orgList: [],
+            nowOrg: null,
+            chartData: {
+                xinChuangBaseInfo: {},
+                notXinChuangBaseInfo: {},
+
+            },
+            loading: false,
+            nowTop: 0,
+            overviewList: [
+                {
+                    name: '信创主机',
+                    children: [
+                        {
+                            label: 'VCPU(核)',
+                            num: '0',
+                            key: 'cpu'
+                        },
+                        {
+                            label: '内存(G)',
+                            num: '0',
+                            key: 'disk'
+                        },
+                        {
+                            label: '磁盘(G)',
+                            num: '0',
+                            key: 'memory'
+                        },
+                        {
+                            label: '备份(G)',
+                            num: '0',
+                            key: 'backup'
+                        }
+                    ]
+                },
+                {
+                    name: '非信创主机',
+                    children: [
+                        {
+                            label: 'VCPU(核)',
+                            num: '4652',
+                            key: 'cpu'
+                        },
+                        {
+                            label: '内存(G)',
+                            num: '14752',
+                            key: 'disk'
+                        },
+                        {
+                            label: '磁盘(G)',
+                            num: '0',
+                            key: 'memory'
+                        },
+                        {
+                            label: '备份(G)',
+                            num: '22219',
+                            key: 'backup'
+                        }
+                    ]
+                }
+            ],
+            colorList: ['#fd7f55', '#fea147', '#ffc936'],
+            topButtons: [
+                {
+                    label: 'CPU',
+                    value: []
+                }, {
+                    label: '内存',
+                    value: []
+                }, {
+                    label: '磁盘',
+                    value: []
+                }
+            ],
+
+            onlyCollect: false,
+            sysList: [],
+            nowSys: [],
+            pageNum: 1,
+            pageSize: 10,
+            total: null,
+            list: []
+        }
+    },
+    watch: {
+        topButtons(newValue) {
+            newValue.map((item, index) => {
+                item.value.map((item2, index2) => {
+                    item2.color = this.colorList[index2] || ''
+                })
+            })
+
+        }
+    },
+    async mounted() {
+        this.loading = true
+        this.timer = setInterval(() => {
+            this.time = new Date()
+            this.nowTime = this.timestampToTime(this.time.toLocaleString('en-US', { hour12: false }).split(' '))
+        }, 1000)
+
+        if (this.orgList.length == 0) {
+            await this.getOrgList()
+        }
+    },
+    methods: {
+        changeNowTopButton(index) {
+            this.nowTop = index;
+        },
+        timestampToTime(times) {
+            let time = times[1]
+            let mdy = times[0]
+            mdy = mdy.split('/')
+            this.month = parseInt(mdy[0])
+            this.day = parseInt(mdy[1])
+            if (mdy[0].length == 1) {
+                this.month = '0' + parseInt(mdy[0])
+            }
+            if (mdy[1].length == 1) {
+                this.day = '0' + parseInt(mdy[1])
+            }
+            this.year = parseInt(mdy[2])
+            return this.year + '-' + this.month + '-' + this.day + ' ' + time
+        },
+        getOrgList() {
+            return new Promise((resolve, reject) => {
+                let params = {
+                    dataType: "1", // 数据类型 0-当前页 1-全部数据
+                    currentPage: 1,
+                    pageSize: 20
+                }
+                findOrganizeListWithUserRoleExtension(params).then((res) => {
+                    this.orgList = res.data
+                    this.nowOrg = this.orgList[0].orgId
+                    this.getInfo()
+                    resolve(true)
+                });
+            })
+
+        },
+
+        getSysList() {
+            let params = {
+                orgId: this.nowOrg
+            }
+            getSystemInfoByOrgId(params).then((res) => {
+                this.sysList = res.data
+                this.nowSys = []
+                this.sysList.forEach((sys) => {
+                    this.nowSys.push(sys.systemName);
+                });
+                this.fetchList()
+            })
+        },
+
+        //切换厅局
+        orgChange(value) {
+            this.getInfo()
+        },
+        //切换系统
+        sysChange(value) {
+            this.fetchList()
+        },
+        //获取数据
+        getInfo() {
+            this.loading = true
+            let params = {
+                orgId: this.nowOrg
+            };
+            getAssetByOrgId(params).then((res) => {
+                if (res.code == 200) {
+                    this.chartData = res.data
+                    this.overviewList[0].children.map(item => {
+                        for (let key in this.chartData.xinChuangBaseInfo) {
+                            if (key == item.key) {
+                                item.num = this.chartData.xinChuangBaseInfo[key]
+                            }
+                        }
+                    })
+                    this.overviewList[1].children.map(item => {
+                        for (let key in this.chartData.notXinChuangBaseInfo) {
+                            if (key == item.key) {
+                                item.num = this.chartData.notXinChuangBaseInfo[key]
+                            }
+                        }
+                    })
+                    this.loading = false
+
+                    this.topButtons = [
+                        {
+                            label: 'CPU',
+                            value: this.chartData.cpuTop
+                        }, {
+                            label: '内存',
+                            value: this.chartData.memoryTop
+                        }, {
+                            label: '磁盘',
+                            value: this.chartData.diskTop
+                        }
+                    ]
+
+
+                    // 绘制图表
+                    this.$nextTick(() => {
+                        this.initEchart1()
+                    })
+                }
+
+            });
+
+            this.getSysList()
+        },
+        fetchList() {
+            this.list = []
+            let params = {
+                orgId: this.nowOrg,
+                pageSize: this.pageSize,
+                currentPage: this.pageNum,
+                systemNameList: this.nowSys,
+            };
+            getAssetListByOrgIdV2(params).then((res) => {
+                if (res.code == 200) {
+                    if (res.data) {
+                        this.list = res.data.assetList.list; //返回的数据列表
+                        this.total = res.data.assetList.pagination.total
+
+                        this.list.map((item) => {
+                            if (item.backup > 9999) {
+                                item.backup = (item.backup / 1024).toFixed(2);
+                                item.backupUnit = 'T';
+                            }
+
+                            if (item.disk > 9999) {
+                                item.disk = (item.disk / 1024).toFixed(2);
+                                item.diskUnit = 'T';
+                            }
+                        });
+                    }
+                }
+            });
+        },
+        //主机概况
+        initEchart1() {
+            var chartDom = document.getElementById('echarts1');
+            var myChart = this.$echarts.init(chartDom);
+
+            let data = [
+                {
+                    value: this.chartData.notXinChuangBaseInfo.count,
+                    name: '非信创主机'
+                },
+                {
+                    value: this.chartData.xinChuangBaseInfo.count,
+                    name: '信创主机'
+                }
+            ]
+
+            let count = this.chartData.notXinChuangBaseInfo.count + this.chartData.xinChuangBaseInfo.count
+
+            var option = {
+                tooltip: {
+                    trigger: 'item'
+                },
+                grid: {
+                    left: '5%',
+                    right: '5%',
+                    bottom: '15%',
+                    top: '15%',
+                    containLabel: true
+                },
+                series: [
+                    {
+                        name: '',
+                        type: 'pie',
+                        radius: ['75%', '90%'],
+                        avoidLabelOverlap: false,
+                        label: {
+                            show: true,
+                            position: 'center',
+                            formatter: function (params) {
+                                return `${count}\n主机总数`;
+                            },
+                            fontSize: 24
+
+                        },
+                        itemStyle: {
+                            normal: {
+                                color: function (params) {
+                                    // 预定义一个颜色数组
+                                    var colorList = [
+                                        '#ffca3c', '#3e7dfe',
+                                    ];
+                                    // 返回每个饼图扇区的颜色
+                                    return colorList[params.dataIndex % colorList.length];
+                                }
+                            }
+                        },
+                        labelLine: {
+                            show: false
+                        },
+                        data: data
+                    }
+                ]
+            };
+            myChart.setOption(option);
+        },
+        handleSizeChange(val) {
+            console.log(`每页 ${val} 条`);
+            this.pageSize = val
+            this.fetchList()
+        },
+        handleCurrentChange(val) {
+            this.pageNum = val
+            console.log(`当前页: ${val}`);
+            this.fetchList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.JNPF-common-layout {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    .top {
+        background-color: #fff;
+        height: 5vh;
+        width: 100%;
+        margin-bottom: 10px;
+        display: flex;
+        justify-content: flex-end;
+        padding: 0 20px;
+        position: relative;
+        .center {
+            font-size: 26px;
+            color: #000;
+            font-weight: bold;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+        }
+        .right {
+            display: flex;
+            align-items: center;
+            justify-content: space-around;
+            gap: 20px;
+            .now-time {
+                font-size: 20px;
+                color: #808080;
+            }
+        }
+    }
+    .content {
+        width: 100%;
+        height: 80vh;
+        display: flex;
+        align-items: center;
+        justify-content: space-around;
+        gap: 10px;
+        .c1,
+        .c2,
+        .c3 {
+            width: 33%;
+            height: 100%;
+            background-color: #fff;
+            padding: 20px;
+        }
+        .c2,
+        .c3 {
+            padding-right: 5px;
+        }
+        .c1 {
+            display: flex;
+            background-color: #ebeef5;
+            flex-direction: column;
+            padding: 0;
+            gap: 10px;
+            justify-content: space-around;
+            align-items: center;
+            .f1,
+            .f2 {
+                width: 100%;
+                height: 49%;
+                padding: 20px;
+                background-color: #fff;
+            }
+            .f1 {
+                .chart {
+                    width: 100%;
+                    height: 100%;
+                    .echarts1 {
+                        width: 100%;
+                        height: 70%;
+                    }
+                    .legend {
+                        width: 100%;
+                        height: 20%;
+                        display: flex;
+                        align-items: center;
+                        justify-content: space-evenly;
+                        .item {
+                            display: flex;
+                            align-items: center;
+                            .doc {
+                                width: 5px;
+                                height: 5px;
+                                background: #2c7bff;
+                                border-radius: 50%;
+                                margin-right: 5px;
+                            }
+                            span {
+                                font-family: PingFang SC;
+                                font-weight: 500;
+                                font-size: 16px;
+                                color: #000000;
+                                opacity: 0.6;
+                            }
+                            .num {
+                                font-weight: bold;
+                                font-size: 24px;
+                                color: #3e7dfe;
+                                margin: 0 5px;
+                            }
+                        }
+                    }
+                }
+            }
+            .f2 {
+                .overview-list {
+                    margin-top: 20px;
+                    height: 90%;
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: space-evenly;
+
+                    .overview-item {
+                        .item-top {
+                            display: flex;
+                            align-items: center;
+                            justify-content: flex-start;
+                            margin-bottom: 10px;
+                            img {
+                                width: 30px;
+                                height: 30px;
+                                margin-right: 10px;
+                            }
+                            div {
+                                font-size: 18px;
+                                color: #000000;
+                            }
+                        }
+                        .list {
+                            display: flex;
+                            align-items: center;
+                            justify-content: space-around;
+                            gap: 20px;
+                            margin-bottom: 15px;
+                            .list-item {
+                                width: 90px;
+                                height: 50px;
+                                background: #f7f7f8;
+                                border-radius: 8px;
+                                text-align: center;
+                                display: flex;
+                                align-items: center;
+                                align-content: center;
+
+                                flex-wrap: wrap;
+
+                                .num {
+                                    width: 100%;
+                                    font-weight: bold;
+                                    font-size: 18px;
+                                    color: #373743;
+                                }
+                                .name {
+                                    width: 100%;
+                                    font-size: 18px;
+                                    color: #000000;
+                                    opacity: 0.5;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        .c2 {
+            display: flex;
+            flex-direction: column;
+            .t {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                padding-bottom: 10px;
+                .buttons {
+                    span {
+                        color: #000000;
+                        opacity: 0.9;
+                        border-radius: 25px;
+                        padding: 5px 10px;
+                        cursor: pointer;
+                    }
+                    .now {
+                        background: #dfecfd;
+                        color: #1c77f2;
+                    }
+                }
+            }
+            .list {
+                padding: 10px 0;
+                padding-right: 10px;
+                overflow-y: auto;
+                .list-item {
+                    display: flex;
+                    align-items: center;
+                    justify-content: space-between;
+
+                    .left {
+                        width: 50px;
+                        height: 50px;
+                        display: flex;
+                        align-items: center;
+                        align-content: center;
+                        justify-content: center;
+                        text-align: center;
+                        font-weight: bold;
+                        font-size: 16px;
+                        border-radius: 8px;
+                    }
+
+                    .center {
+                        width: 70%;
+                        padding: 5px 0;
+                        flex: 1;
+                        margin: 0 12px;
+                        div {
+                            font-size: 20px;
+                            color: #000000;
+                            opacity: 0.7;
+                            margin-bottom: 10px;
+                        }
+                    }
+                    .right {
+                        display: flex;
+                        align-content: flex-end;
+                        align-items: flex-end;
+                    }
+                }
+            }
+        }
+        .c3 {
+            display: flex;
+            flex-direction: column;
+            justify-content: space-between;
+            padding-bottom: 5px;
+            .t {
+                display: flex;
+                align-items: center;
+                justify-content: space-between;
+                padding-bottom: 10px;
+                .right {
+                    display: flex;
+                    align-items: center;
+                    justify-content: space-between;
+                }
+            }
+            .list {
+                flex: 1;
+                padding: 10px 0;
+                padding-right: 10px;
+                overflow-y: auto;
+                .list-item {
+                    margin-bottom: 10px;
+                    padding: 15px;
+                    .item-top {
+                        display: flex;
+                        align-items: center;
+                        justify-content: space-between;
+                        .left {
+                            display: flex;
+                            align-items: center;
+                            .asset-name {
+                                font-size: 18px;
+                                font-weight: bold;
+                                margin-right: 10px;
+                            }
+                        }
+                        img {
+                            width: 40px;
+                            height: 40px;
+                        }
+                    }
+                    .status {
+                        margin: 10px 0;
+                        padding: 5px 10px;
+                        width: 100px;
+                        display: flex;
+                        align-items: center;
+                        justify-content: space-evenly;
+                        align-content: center;
+                        border-radius: 5px;
+                        font-size: 14px;
+                        img {
+                            width: 15px;
+                            height: 15px;
+                            min-width: 15px;
+                            min-height: 15px;
+                            margin-right: 5px;
+                        }
+                        .down-time {
+                            color: #177532;
+                        }
+                        .not-down-time {
+                            color: #d81e06;
+                        }
+                    }
+                    .down-status {
+                        background-color: #1dcaac20;
+                    }
+                    .not-down-status {
+                        background-color: #d81e0620;
+                    }
+                    .center {
+                        color: #838385;
+                        font-size: 14px;
+                        .row {
+                            display: flex;
+                            align-items: center;
+                            justify-content: space-between;
+                            margin: 5px 0;
+                        }
+                    }
+                    .foot {
+                        display: flex;
+                        justify-content: space-between;
+                        align-items: center;
+                        padding-top: 20px;
+                        .ip {
+                            font-size: 14px;
+                            color: #585858;
+                        }
+                        .time {
+                            color: #585858;
+                            font-size: 14px;
+                        }
+                    }
+                }
+            }
+        }
+        .rect-title {
+            font-size: 20px;
+            font-weight: bold;
+            color: #000;
+        }
+    }
+}
+</style>
+
+<style >
+::v-deep .app-main {
+    background-color: #ebeef5;
+}
+</style>

+ 563 - 0
src/views/statisticalCenter/SSS/index.vue

@@ -0,0 +1,563 @@
+<template>
+    <div class="JNPF-common-layout">
+        <div class="top">
+            <div class="left"></div>
+            <div class="center">安全服务统计</div>
+            <div class="right">
+                <div class="now-time">{{ nowTime }}</div>
+                <el-select v-model="nowOrg" :popper-append-to-body="false" filterable
+                    placeholder="请选择厅局" @change="orgChange">
+                    <div>
+                        <el-option v-for="item in orgList" :key="item.orgId" :label="item.orgName"
+                            :value="item.orgId"> </el-option>
+                    </div>
+                </el-select>
+            </div>
+        </div>
+        <div class="f1">
+            <div class="c1 card" v-loading="loading" element-loading-text="加载中">
+                <div class="rect-title">境外攻击区域分布(次)</div>
+                <template v-if="chartShow">
+                    <div class="chart" id="echarts1">
+                    </div>
+                </template>
+                <el-empty v-else description="暂无数据"></el-empty>
+
+            </div>
+            <div class="c2 card" v-loading="loading" element-loading-text="加载中">
+                <div class="rect-title">境内攻击区域分布(次)</div>
+                <template v-if="chartShow">
+                    <div class="chart" id="echarts2">
+                    </div>
+                </template>
+                <el-empty v-else description="暂无数据"></el-empty>
+            </div>
+        </div>
+        <div class="f2 card" v-loading="loading" element-loading-text="加载中">
+            <div class="rect-title">实时网络攻击情况(次)</div>
+            <template v-if="chartShow">
+                <div class="chart" id="echarts3">
+                </div>
+
+            </template>
+            <el-empty v-else description="暂无数据"></el-empty>
+        </div>
+    </div>
+    </div>
+</template>
+
+<script>
+import {
+    findOrganizeListWithUserRoleExtension, getMonitorChinaMap, getMonitorWorldMap, getCyberattacks
+} from "@/api/governmentCloud/statisticalCenter/statisticalCenter";
+import dayjs from 'dayjs'
+
+export default {
+    data() {
+        return {
+            nowTime: null,
+            orgList: [],
+            nowOrg: null,
+            cloudSecurity: {},
+            loading: true,
+            chartShow: false
+        }
+    },
+    async mounted() {
+        this.loading = true
+        this.timer = setInterval(() => {
+            this.time = new Date()
+            this.nowTime = this.timestampToTime(this.time.toLocaleString('en-US', { hour12: false }).split(' '))
+        }, 1000)
+
+        if (this.orgList.length == 0) {
+            await this.getOrgList()
+        }
+    },
+    methods: {
+        timestampToTime(times) {
+            let time = times[1]
+            let mdy = times[0]
+            mdy = mdy.split('/')
+            this.month = parseInt(mdy[0])
+            this.day = parseInt(mdy[1])
+            if (mdy[0].length == 1) {
+                this.month = '0' + parseInt(mdy[0])
+            }
+            if (mdy[1].length == 1) {
+                this.day = '0' + parseInt(mdy[1])
+            }
+            this.year = parseInt(mdy[2])
+            return this.year + '-' + this.month + '-' + this.day + ' ' + time
+        },
+        //切换厅局
+        orgChange(value) {
+            this.getInfo()
+        },
+        getInfo() {
+            let getDataList = []
+            let timeParams = {
+                orgId: this.nowOrg,
+                enCode: 90
+            }
+            this.loading = true
+            this.chartShow = false
+
+            // 国内攻击
+            getDataList.push(
+                new Promise((resolve, reject) => {
+                    getMonitorChinaMap(timeParams).then(res => {
+                        if (res.code == 200) {
+                            this.cloudSecurity.monitorChinaMap = res.data
+                            resolve(res);
+                        } else {
+                            reject(false)
+                        }
+
+                    })
+                })
+            )
+            //境外攻击
+            getDataList.push(
+                new Promise((resolve, reject) => {
+                    getMonitorWorldMap(timeParams).then(res => {
+                        if (res.code == 200) {
+                            this.cloudSecurity.monitorWorldMap = res.data
+                            resolve(res);
+                        } else {
+                            reject(false)
+                        }
+
+                    })
+                })
+            )
+            //实时网络
+            getDataList.push(
+                new Promise((resolve, reject) => {
+                    getCyberattacks(timeParams).then(res => {
+                        if (res.code == 200) {
+                            this.cloudSecurity.cyberattacks = res.data
+                            resolve(res);
+                        } else {
+                            reject(false)
+                        }
+
+                    })
+                })
+            )
+
+
+            Promise.all(getDataList).then((r) => {
+                this.loading = false
+                this.chartShow = true
+                this.$nextTick(() => {
+                    this.drawAllLines()
+                });
+            }).catch(reject => {
+                this.loading = false
+                this.chartShow = false
+
+                console.log(reject);
+            })
+        },
+        drawAllLines() {
+            this.drawLines1(this.cloudSecurity.cyberattacks, 'echarts3');
+            this.drawLines2(this.cloudSecurity.monitorWorldMap, 'echarts2');
+            this.drawLines3(this.cloudSecurity.monitorChinaMap, 'echarts1');
+        },
+        drawLines1(data, id) {
+            console.log('data');
+            console.log(data);
+            // 这里是初始化的方式,通过id查询找到你的canvas标签
+            var chartDom = document.getElementById(id);
+            var myChart = this.$echarts.init(chartDom);
+            // 数据处理
+            let xData = [];
+            let seriesData = [];
+            console.log(data);
+            data.xaxis.map((element) => {
+                let dateString = dayjs(element).format('MM-DD');
+                xData.push(dateString);
+            });
+            data.yaxis.map((element) => {
+                seriesData.push(element);
+            });
+            // 这里开始就是echarts的配置项了
+            let option = {
+                // x轴数据
+                xAxis: {
+                    type: 'category',
+                    data: xData,
+                    // boundaryGap: false,
+                    axisLabel: {
+                        show: true,
+                        color: '#101010',
+                        fontSize: 14,
+                        align: 'center',
+                        interval: Math.floor(xData.length / 4),
+                        showMaxLabel: true
+                    },
+                    axisLine: {
+                        lineStyle: {
+                            color: '#d4d4d4'
+                        }
+                    },
+                    splitLine: {
+                        show: false // 网格线
+                    },
+                    axisTick: { alignWithLabel: true }
+                },
+                tooltip: {
+                    trigger: 'axis'
+                },
+                // y轴数据
+                yAxis: {
+                    type: 'value',
+                    name: '',
+                    nameTextStyle: {//y轴上方单位的颜色
+                        color: '#101010'
+                    },
+                    axisLabel: {
+                        show: true,
+                        color: '#101010',
+                        fontSize: 14
+                    },
+                    axisLine: {
+                        show: true,
+                        lineStyle: {
+                            color: '#d4d4d4'
+                        }
+                    },
+                    splitLine: {
+                        show: true // 网格线
+                    }
+                },
+                // 这里写2个对象是2条线条,3个则是3条
+                series: [
+                    {
+                        // data使用刚才定义的数组,数据从后端接口中取得
+                        data: seriesData,
+                        type: 'line',
+                        smooth: true,
+                        symbolSize: 0,
+                        itemStyle: {
+                            color: '#4e9fdd'
+                        },
+                    }
+                ],
+                grid: {
+                    top: 40,
+                    right: 20,
+                    left: 0,
+                    bottom: 0,
+                    // 这里可以防止Y轴显示不全
+                    containLabel: true
+                }
+            };
+
+            // 这里不要忘记把option设置给echarts实例
+            myChart.setOption(option);
+            // 这里是用于窗口变化时的自适应,利用的是echarts自带的resize方法
+            // 如果你打印出来这个echarts实例,可以在函数里面找到这个方法
+            window.addEventListener('resize', () => {
+                myChart.resize();
+            });
+        },
+        drawLines2(data, id) {
+            // 这里是初始化的方式,通过id查询找到你的canvas标签
+            var chartDom = document.getElementById(id);
+            var myChart = this.$echarts.init(chartDom);
+            // 数据处理
+            let xData = [];
+            let seriesData = [];
+            console.log(data);
+            data.map((element) => {
+                element.value = element.num
+            });
+            xData = data
+            // 这里开始就是echarts的配置项了
+            let option = {
+                legend: {
+                    top: 'center',
+                    right: '50',
+                    width: '50',
+                    formatter: (name) => {
+                        let target;
+                        const data = xData;
+                        for (let i = 0; i < data.length; i++) {
+                            if (data[i].name == name) {
+                                target = `${data[i].value}`;
+                            }
+                        }
+                        const arr = [
+                            `${name}:${target}`,
+                        ];
+                        return arr.join('');
+                    }
+
+                },
+                tooltip: {
+                    trigger: 'item'
+                },
+                series: [
+                    {
+                        name: '境内攻击区域分布(次)',
+                        type: 'pie',
+                        radius: ['40%', '80%'],
+                        center: ['50%', '50%'],
+                        avoidLabelOverlap: false,
+                        itemStyle: {
+                            borderRadius: 5,
+                            borderColor: '#fff',
+                            borderWidth: 2
+                        },
+                        label: {
+                            show: false,
+                            position: 'center'
+                        },
+                        labelLine: {
+                            show: false
+                        },
+                        data: xData
+                    }
+                ]
+            };
+
+            // 这里不要忘记把option设置给echarts实例
+            myChart.setOption(option);
+            // 这里是用于窗口变化时的自适应,利用的是echarts自带的resize方法
+            // 如果你打印出来这个echarts实例,可以在函数里面找到这个方法
+            window.addEventListener('resize', () => {
+                myChart.resize();
+            });
+        },
+        drawLines3(data, id) {
+            // 这里是初始化的方式,通过id查询找到你的canvas标签
+            var chartDom = document.getElementById(id);
+            var myChart = this.$echarts.init(chartDom);
+            // 数据处理
+            let xData = [];
+            let seriesData = [];
+            console.log(data);
+            data.map((element) => {
+                xData.push(element.name);
+                seriesData.push(element.num);
+            });
+            // 这里开始就是echarts的配置项了
+            let option = {
+                // x轴数据
+                xAxis: {
+                    type: 'category',
+                    data: xData,
+                    // boundaryGap: false,
+                    axisLabel: {
+                        show: true,
+                        color: '#101010',
+                        fontSize: 14,
+                        align: 'center',
+                        interval: Math.floor(xData.length / 4),
+                        interval: 0,
+                        showMaxLabel: true
+                    },
+                    axisLine: {
+                        lineStyle: {
+                            color: '#d4d4d4'
+                        }
+                    },
+                    splitLine: {
+                        show: false // 网格线
+                    },
+                    axisTick: { alignWithLabel: true }
+                },
+                tooltip: {
+                    trigger: 'item'
+                },
+                // y轴数据
+                yAxis: {
+                    type: 'value',
+                    name: '',
+                    nameTextStyle: {//y轴上方单位的颜色
+                        color: '#101010'
+                    },
+                    axisLabel: {
+                        show: true,
+                        color: '#101010',
+                        fontSize: 14
+                    },
+                    axisLine: {
+                        show: true,
+                        lineStyle: {
+                            color: '#d4d4d4'
+                        }
+                    },
+                    splitLine: {
+                        show: true // 网格线
+                    }
+                },
+                // 这里写2个对象是2条线条,3个则是3条
+                series: [
+                    {
+                        // data使用刚才定义的数组,数据从后端接口中取得
+                        data: seriesData,
+                        type: 'bar',
+                        barWidth: '30%',
+                        itemStyle: {
+                            color: '#5184fb'
+                        },
+                        areaStyle: {
+                            color: {
+                                type: 'linear',
+                                x: 0,
+                                y: 0,
+                                x2: 0,
+                                y2: 1,
+                                colorStops: [
+                                    {
+                                        offset: 0,
+                                        color: '#127cdb50' // 上面的颜色
+                                    },
+                                    {
+                                        offset: 1,
+                                        color: '#127cdb20' // 下面的颜色
+                                    }
+                                ],
+                                globalCoord: false // 缺省为 false
+                            }
+                        },
+                    }
+                ],
+                dataZoom: [
+                    // x轴滑动条
+                    {
+                        type: 'slider',
+                        show: false,
+                        xAxisIndex: [0],
+                        start: 0, // 初始化时,滑动条宽度开始标度
+                        bottom: '7%',
+                        end: 40,
+                        height: 10,
+                        fillerColor: 'transparent',
+                        borderColor: 'transparent',
+                        backgroundColor: 'transparent', // 两边未选中的滑动条区域的颜色
+                        showDataShadow: false, // 是否显示数据阴影 默认auto
+                        showDetail: false // 即拖拽时候是否显示详细数值信息 默认true
+                    },
+                    {
+                        type: 'inside',
+                        xAxisIndex: 0,
+                        zoomOnMouseWheel: false, // 滚轮是否触发缩放
+                        moveOnMouseMove: true, // 鼠标滚轮触发滚动
+                        moveOnMouseWheel: true
+                    }
+                ],
+                grid: {
+                    top: 40,
+                    right: 20,
+                    left: 0,
+                    bottom: 0,
+                    // 这里可以防止Y轴显示不全
+                    containLabel: true
+                }
+            };
+
+            // 这里不要忘记把option设置给echarts实例
+            myChart.setOption(option);
+            // 这里是用于窗口变化时的自适应,利用的是echarts自带的resize方法
+            // 如果你打印出来这个echarts实例,可以在函数里面找到这个方法
+            window.addEventListener('resize', () => {
+                myChart.resize();
+            });
+        },
+
+
+        getOrgList() {
+            return new Promise((resolve, reject) => {
+                let params = {
+                    dataType: "1", // 数据类型 0-当前页 1-全部数据
+                    currentPage: 1,
+                    pageSize: 20
+                }
+                findOrganizeListWithUserRoleExtension(params).then((res) => {
+                    this.orgList = res.data
+                    this.nowOrg = this.orgList[0].orgId
+                    this.getInfo()
+                    resolve(true)
+                });
+            })
+
+        },
+    },
+
+}
+</script>
+
+<style lang="scss" scoped>
+.JNPF-common-layout {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    justify-content: space-around;
+    gap: 10px;
+    .top {
+        background-color: #fff;
+        height: 5vh;
+        width: 100%;
+        display: flex;
+        justify-content: flex-end;
+        padding: 0 20px;
+        position: relative;
+        .center {
+            font-size: 26px;
+            color: #000;
+            font-weight: bold;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+        }
+        .right {
+            display: flex;
+            align-items: center;
+            justify-content: space-around;
+            gap: 20px;
+            .now-time {
+                font-size: 20px;
+                color: #808080;
+            }
+        }
+    }
+
+    .f1,
+    .f2 {
+        flex: 1;
+    }
+    .f1 {
+        display: flex;
+        justify-content: space-around;
+        gap: 10px;
+        .c1,
+        .c2 {
+            flex: 1;
+            background-color: #fff;
+        }
+    }
+    .f2 {
+        background-color: #fff;
+    }
+    .card {
+        padding: 10px;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-around;
+        gap: 10px;
+        .rect-title {
+            font-size: 20px;
+            font-weight: bold;
+            color: #000;
+        }
+        .chart {
+            flex: 1;
+        }
+    }
+}
+</style>