浏览代码

人事管理驾驶舱静态页面

wuhongbo 1 年之前
父节点
当前提交
3f8abf5805

二进制
client/src/assets/imgs/oaView/card-title.png


二进制
client/src/assets/imgs/oaView/down.png


二进制
client/src/assets/imgs/oaView/girl.png


二进制
client/src/assets/imgs/oaView/lsg.png


二进制
client/src/assets/imgs/oaView/man.png


二进制
client/src/assets/imgs/oaView/sxs.png


二进制
client/src/assets/imgs/oaView/syg.png


二进制
client/src/assets/imgs/oaView/up.png


二进制
client/src/assets/imgs/oaView/zsg.png


+ 0 - 1
client/src/store/modules/user.ts

@@ -61,7 +61,6 @@ export const useUserStore = defineStore('admin-user', {
       this.roles = userInfo.roles
       this.user = userInfo.user
       this.isSetUser = true
-      debugger
       wsCache.set(CACHE_KEY.USER, userInfo)
       wsCache.set(CACHE_KEY.ROLE_ROUTERS, userInfo.menus)
     },

+ 157 - 0
client/src/views/OaSystem/oaViews/components/cardView.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="card-view">
+    <div class="card-title">
+      <p>{{ props.title }}</p>
+    </div>
+    <div class="tabs-Box" v-if="initTabsShow()">
+      <div class="tabs-left">
+        <ul v-if="props.leftTabs">
+          <el-radio-group v-model="radioModel" @change="radioGroupChange">
+            <el-radio :label="0" size="large">{{ props.leftTabs[0] }}</el-radio>
+            <el-radio :label="1" size="large">{{ props.leftTabs[1] }}</el-radio>
+          </el-radio-group>
+        </ul>
+      </div>
+      <div class="tabs-right">
+        <ul v-if="props.rightTabs">
+          <li
+            v-for="(item, index) in props.rightTabs"
+            :key="index"
+            :class="index == rightTabsIndex ? 'liActive' : ''"
+            @click="rightTabsClick(item, index)"
+          >
+            <p>{{ item }}</p>
+          </li>
+        </ul>
+      </div>
+    </div>
+    <div class="card-content">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+const props = defineProps({
+  title: String,
+  leftTabs: Array,
+  rightTabs: Array
+})
+const emit = defineEmits(['leftClick', 'rightClick'])
+
+const radioModel = ref(0)
+const rightTabsIndex = ref(0)
+const initTabsShow = () => {
+  if (props.leftTabs || props.rightTabs) {
+    return true
+  } else {
+    return false
+  }
+}
+const rightTabsClick = (item, index) => {
+  rightTabsIndex.value = index
+  emit('rightClick', index)
+}
+const radioGroupChange = (v) => {
+  emit('leftClick', props.leftTabs[v])
+}
+
+/** 初始化 **/
+onMounted(() => {
+  initTabsShow()
+})
+</script>
+<style lang="scss" scoped>
+p,
+span,
+h4,
+h5,
+h6 {
+  font-family: AlibabaPuHuiTiR;
+}
+.card-view {
+  width: 100%;
+  height: auto;
+
+  .card-title {
+    width: 100%;
+    height: 46px;
+    background: url(@/assets/imgs/oaView/card-title.png) no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    align-items: center;
+    padding-left: 45px;
+
+    p {
+      color: #fff;
+      font-size: 20px;
+      font-family: AlibabaPuHuiTiB;
+      font-weight: 600;
+      color: #ffffff;
+      background: linear-gradient(0deg, #ffff 20%, #def1ff 100%);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+      margin-top: 8px;
+    }
+  }
+  .tabs-Box {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 10px;
+    height: 40px;
+    .tabs-left {
+      ul {
+        display: flex;
+        align-items: center;
+        li {
+        }
+      }
+    }
+    .tabs-right {
+      ul {
+        display: flex;
+        align-items: center;
+        li {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          width: 64px;
+          height: 24px;
+          border: 2px solid #0d6795;
+          background-color: transparent;
+          cursor: pointer;
+          margin-right: 5px;
+          p {
+            color: #7ac1ec;
+            font-size: 16px;
+          }
+        }
+        li:last-child {
+          margin-right: 0;
+        }
+        .liActive {
+          background-color: #29a4e4;
+          p {
+            color: #fff;
+          }
+        }
+      }
+    }
+  }
+  .card-content {
+    width: 100%;
+    height: calc(100% - 86px);
+  }
+}
+:deep(.tabs-left) {
+  .el-radio__label {
+    color: #7ac1ec;
+    font-size: 16px;
+    font-family: AlibabaPuHuiTiR;
+  }
+  .el-radio {
+    margin-right: 15px;
+  }
+}
+</style>

+ 19 - 2
client/src/views/OaSystem/oaViews/personnelView/index.vue

@@ -1,8 +1,25 @@
 <template>
-  <div> 人事 111</div>
+  <div class="personnel publicView">
+    <div class="left-view">
+      <LeftView />
+    </div>
+    <div class="chart-map-view">
+      <MapView />
+    </div>
+    <div class="right-view">
+      <rightView />
+    </div>
+  </div>
 </template>
 <script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import LeftView from './leftView/leftView.vue'
+import MapView from './mapView/mapView.vue'
+import rightView from './rightView/rightView.vue'
+
 /** 初始化 **/
 onMounted(() => {})
 </script>
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+@import '../publicView.scss';
+</style>

+ 98 - 0
client/src/views/OaSystem/oaViews/personnelView/leftView/chart1.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="w-100% h-100%" id="myChart"></div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import * as echarts from 'echarts'
+const initChart = () => {
+  let myChart = echarts.init(document.getElementById('myChart'))
+  let datas = [
+    {
+      name: '25岁以下',
+      value: 90
+    },
+    {
+      name: '25-30岁',
+      value: 121
+    },
+    {
+      name: '30-35岁',
+      value: 32
+    },
+    {
+      name: '35-40岁',
+      value: 16
+    },
+    {
+      name: '40岁以上',
+      value: 20
+    }
+  ]
+  let option = {
+    color: ['#00D488', '#46FFEA', '#3AAEFF', '#E4A306', '#91C7AE'],
+    legend: {
+      itemHeight: 14,
+      itemWidth: 14,
+      icon: 'none',
+      showLegendSymbol: false,
+      orient: 'vertical',
+      top: 'center',
+      right: '5%',
+      textStyle: {
+        align: 'left',
+        color: '#',
+        verticalAlign: 'middle',
+        rich: {
+          name: {
+            width: 80,
+            fontSize: 14,
+            fontFamily: 'AlibabaPuHuiTiR'
+          }
+        }
+      },
+      data: datas,
+      formatter: (name) => {
+        if (datas.length) {
+          const item = datas.filter((item) => item.name === name)[0]
+          return `{name|${name}}`
+        }
+      }
+    },
+    tooltip: {
+      trigger: 'item',
+      textStyle: {
+        color: '#fff',
+        fontFamily: 'AlibabaPuHuiTiR'
+      },
+      backgroundColor: '#102755',
+      borderRadius: 4,
+      borderColor: '#0A30AD',
+      borderWidth: 1,
+      axisPointer: {
+        type: 'none'
+      }
+    },
+    series: [
+      {
+        name: '',
+        type: 'pie',
+        radius: ['30%', '80%'],
+        center: ['35%', '50%'],
+        roseType: 'radius',
+        label: {
+          show: false,
+          formatter: '{d}%'
+        },
+        data: datas
+      }
+    ]
+  }
+  myChart.setOption(option)
+}
+/** 初始化 **/
+
+onMounted(() => {
+  initChart()
+})
+</script>
+<style lang="scss" scoped></style>

+ 187 - 0
client/src/views/OaSystem/oaViews/personnelView/leftView/chart2.vue

@@ -0,0 +1,187 @@
+<template>
+  <div class="w-100% h-100%" id="myChart1"></div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import * as echarts from 'echarts'
+const initChart = () => {
+  let myChart = echarts.init(document.getElementById('myChart1'))
+  let option = {
+    tooltip: {
+      trigger: 'axis',
+      textStyle: {
+        color: '#fff'
+      },
+      backgroundColor: '#102755',
+      borderRadius: 4,
+      borderColor: '#0A30AD',
+      borderWidth: 1,
+      axisPointer: {
+        type: 'none'
+      }
+    },
+    grid: {
+      left: '10%',
+      top: '10%',
+      right: '5%',
+      bottom: '25%'
+    },
+    xAxis: {
+      data: ['研发岗', 'UI设计', '实施岗', '测试岗', '售前岗', '销售岗', '行政岗', '管理岗'],
+      axisLine: {
+        show: true, //隐藏X轴轴线
+        lineStyle: {
+          color: '#163a5f',
+          width: 2
+        }
+      },
+      axisTick: {
+        show: false, //隐藏X轴刻度
+        alignWithLabel: true
+      },
+      axisLabel: {
+        show: true,
+        textStyle: {
+          color: '#fff', //X轴文字颜色
+          fontSize: 12,
+          fontFamily: 'AlibabaPuHuiTiR'
+        },
+        rotate: 45,
+        interval: 0,
+        formatter: function (value) {
+          var ret = '' //拼接加\n返回的类目项
+          var maxLength = 4 //每项显示文字个数
+          var valLength = value.length //X轴类目项的文字个数
+          var rowN = Math.ceil(valLength / maxLength) //类目项需要换行的行数
+          if (rowN > 1) {
+            //如果类目项的文字大于5,
+            for (var i = 0; i < rowN; i++) {
+              var temp = '' //每次截取的字符串
+              var start = i * maxLength //开始截取的位置
+              var end = start + maxLength //结束截取的位置
+              //这里也可以加一个是否是最后一行的判断,但是不加也没有影响,那就不加吧
+              temp = value.substring(start, end) + '\n'
+              ret += temp //凭借最终的字符串
+            }
+            return ret
+          } else {
+            return value
+          }
+        }
+      }
+    },
+    yAxis: [
+      {
+        type: 'value',
+        name: '',
+        nameTextStyle: {
+          color: '#BDD8FB',
+          fontSize: 12,
+          fontFamily: 'AlibabaPuHuiTiR'
+        },
+
+        splitLine: {
+          show: false,
+          lineStyle: {
+            color: 'rgba(255, 255, 255, 0.15)'
+            // type: 'dashed', // dotted 虚线
+          }
+        },
+        axisTick: {
+          show: false
+        },
+        axisLine: {
+          show: true, //隐藏X轴轴线
+          lineStyle: {
+            color: '#163a5f',
+            width: 1
+          }
+        },
+        axisLabel: {
+          show: true,
+          textStyle: {
+            color: '#BDD8FB',
+            fontSize: 12,
+            fontFamily: 'AlibabaPuHuiTiR'
+          }
+        }
+      },
+      {
+        type: 'value',
+        name: '',
+        nameTextStyle: {
+          color: '#BDD8FB',
+          fontSize: 12,
+          fontFamily: 'AlibabaPuHuiTiR'
+        },
+        splitLine: {
+          show: false,
+          lineStyle: {
+            width: 1,
+            color: '#CED2DB'
+          }
+        },
+        axisTick: {
+          show: false
+        },
+        axisLine: {
+          show: false, //隐藏X轴轴线
+          lineStyle: {
+            color: '#163a5f',
+            width: 2
+          }
+        },
+        axisLabel: {
+          show: false,
+          textStyle: {
+            color: '#797A7F',
+            fontSize: 12,
+            fontFamily: 'AlibabaPuHuiTiR'
+          }
+        }
+      }
+    ],
+    series: [
+      {
+        name: '人数',
+        type: 'bar',
+        barWidth: 15,
+        itemStyle: {
+          color: {
+            type: 'linear',
+            x: 0, //右
+            y: 0, //下
+            x2: 0, //左
+            y2: 1, //上
+            colorStops: [
+              {
+                offset: 0.1,
+                color: '#ffffff' // 0% 处的颜色
+              },
+              {
+                offset: 1,
+                color: '#29D2E4' // 100% 处的颜色
+              }
+            ]
+          }
+        },
+        label: {
+          show: false,
+          position: 'top',
+          distance: 0,
+          color: '#1ACDDC',
+          formatter: '{c}'
+        },
+        data: [63, 10, 36, 2, 10, 23, 9, 10]
+      }
+    ]
+  }
+  myChart.setOption(option)
+}
+/** 初始化 **/
+
+onMounted(() => {
+  initChart()
+})
+</script>
+<style lang="scss" scoped></style>

+ 173 - 0
client/src/views/OaSystem/oaViews/personnelView/leftView/chart3.vue

@@ -0,0 +1,173 @@
+<template>
+  <div class="w-100% h-100%" id="myChart2"></div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import * as echarts from 'echarts'
+const initChart = () => {
+  let myChart = echarts.init(document.getElementById('myChart2'))
+  let option = {
+    tooltip: {
+      trigger: 'axis',
+      textStyle: {
+        color: '#fff'
+      },
+      backgroundColor: '#102755',
+      borderRadius: 4,
+      borderColor: '#0A30AD',
+      borderWidth: 1,
+      axisPointer: {
+        type: 'none'
+      }
+    },
+    grid: {
+      left: '10%',
+      top: '10%',
+      right: '5%',
+      bottom: '25%'
+    },
+    xAxis: {
+      data: ['博士', '硕士', '本科', '大专', '大专\n以下'],
+      axisLine: {
+        show: false, //隐藏X轴轴线
+        lineStyle: {
+          color: '#163a5f',
+          width: 2
+        }
+      },
+      axisTick: {
+        show: false, //隐藏X轴刻度
+        alignWithLabel: true
+      },
+      axisLabel: {
+        show: true,
+        textStyle: {
+          color: '#fff', //X轴文字颜色
+          fontSize: 12,
+          fontFamily: 'AlibabaPuHuiTiR'
+        },
+        interval: 0
+      }
+    },
+    yAxis: [
+      {
+        type: 'value',
+        name: '',
+        nameTextStyle: {
+          color: '#BDD8FB',
+          fontSize: 12,
+          fontFamily: 'AlibabaPuHuiTiR'
+        },
+
+        splitLine: {
+          show: false,
+          lineStyle: {
+            color: 'rgba(255, 255, 255, 0.15)'
+            // type: 'dashed', // dotted 虚线
+          }
+        },
+        axisTick: {
+          show: false
+        },
+        axisLine: {
+          show: false, //隐藏X轴轴线
+          lineStyle: {
+            color: '#163a5f',
+            width: 1
+          }
+        },
+        axisLabel: {
+          show: false,
+          textStyle: {
+            color: '#BDD8FB',
+            fontSize: 12,
+            fontFamily: 'AlibabaPuHuiTiR'
+          }
+        }
+      },
+      {
+        type: 'value',
+        name: '',
+        nameTextStyle: {
+          color: '#BDD8FB',
+          fontSize: 12,
+          fontFamily: 'AlibabaPuHuiTiR'
+        },
+        splitLine: {
+          show: false,
+          lineStyle: {
+            width: 1,
+            color: '#CED2DB'
+          }
+        },
+        axisTick: {
+          show: false
+        },
+        axisLine: {
+          show: false, //隐藏X轴轴线
+          lineStyle: {
+            color: '#163a5f',
+            width: 2
+          }
+        },
+        axisLabel: {
+          show: false,
+          textStyle: {
+            color: '#797A7F',
+            fontSize: 12,
+            fontFamily: 'AlibabaPuHuiTiR'
+          }
+        }
+      }
+    ],
+    series: [
+      {
+        name: '人数',
+        type: 'bar',
+        barWidth: 15,
+
+        itemStyle: {
+          normal: {
+            color: function (params) {
+              var colorList = [
+                'rgba(228,163,6,1)',
+                'rgba(0,212,136,1)',
+                'rgba(9,217,205,1)',
+                'rgba(38,212,255,1)',
+                'rgba(28,162,255,1)'
+              ]
+
+              var color = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  color: colorList[params.dataIndex % 6]
+                },
+                {
+                  offset: 1,
+                  color: colorList[params.dataIndex % 6]
+                }
+              ])
+              return color //大概最新的柱子颜色始终一样的 不太理解
+            }
+          }
+        },
+        label: {
+          show: false,
+          position: 'top',
+          distance: 0,
+          color: '#1ACDDC',
+          formatter: '{c}'
+        },
+        data: [1, 32, 214, 27, 5]
+      }
+    ]
+  }
+  myChart.setOption(option)
+}
+/** 初始化 **/
+
+onMounted(() => {
+  initChart()
+})
+</script>
+<style lang="scss" scoped></style>

+ 198 - 0
client/src/views/OaSystem/oaViews/personnelView/leftView/leftView.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="left-view-com">
+    <CardView
+      class="CardViewC1"
+      :title="'性别、年龄'"
+      :rightTabs="['总览', '按板块']"
+      @rightClick="rightClick1"
+    >
+      <div class="cardBox">
+        <ul class="ulList">
+          <li class="m-b20px">
+            <img src="@/assets/imgs/oaView/man.png" />
+            <div class="text">
+              <div class="top">
+                <span>男 163人</span>
+              </div>
+              <div class="bom">
+                <div></div>
+                <span>58%</span>
+              </div>
+            </div>
+          </li>
+          <li>
+            <img src="@/assets/imgs/oaView/girl.png" />
+            <div class="text">
+              <div class="top">
+                <span>女 116人</span>
+              </div>
+              <div class="bom">
+                <div></div>
+                <span>42%</span>
+              </div>
+            </div>
+          </li>
+        </ul>
+        <div class="chart">
+          <Chart1 />
+        </div>
+      </div>
+    </CardView>
+    <CardView
+      class="CardViewC2"
+      :title="'岗位组成'"
+      :leftTabs="['规划类', '软件类']"
+      @leftClick="leftClick1"
+    >
+      <div class="cardBox">
+        <Chart2 />
+      </div>
+    </CardView>
+    <CardView
+      class="CardViewC3"
+      :title="'学历构成'"
+      :rightTabs="['总览', '按板块']"
+      @rightClick="rightClick2"
+    >
+      <div class="cardBox">
+        <ul>
+          <li v-for="(item, index) in cardview3List" :key="index">
+            <div class="dian">
+              <div></div>
+            </div>
+            <div class="text">
+              <p>{{ item.name }}</p>
+              <span>{{ item.value }} 人</span>
+            </div>
+          </li>
+        </ul>
+        <div class="chart">
+          <Chart3 />
+        </div>
+      </div>
+    </CardView>
+  </div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import CardView from '../../components/cardView.vue'
+import Chart1 from './chart1.vue'
+import Chart2 from './chart2.vue'
+import Chart3 from './chart3.vue'
+
+const cardview3List = ref([
+  {
+    name: '普通院校',
+    value: '198'
+  },
+  {
+    name: '“双一流”院校',
+    value: '81'
+  }
+])
+
+const leftClick1 = (value) => {
+  console.log(value)
+}
+const rightClick1 = (value) => {
+  console.log(value)
+}
+const rightClick2 = (value) => {
+  console.log(value)
+}
+/** 初始化 **/
+onMounted(() => {})
+</script>
+<style lang="scss" scoped>
+@import '../../publicView.scss';
+
+.left-view-com {
+  display: flex;
+  flex-wrap: wrap;
+  .CardViewC1 {
+    height: 30%;
+    .cardBox {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      .ulList {
+        li {
+          display: flex;
+          align-items: center;
+          .text {
+            margin-left: 5px;
+            .bom {
+              display: flex;
+              align-items: center;
+              div {
+                width: 41px;
+                height: 8px;
+                background: rgba(9, 217, 205, 0.8);
+                margin-right: 5px;
+              }
+            }
+          }
+        }
+      }
+      .chart {
+        flex: 1;
+        height: 100%;
+      }
+    }
+  }
+  .CardViewC2 {
+    height: 35%;
+    .cardBox {
+    }
+  }
+  .CardViewC3 {
+    height: calc(35%);
+    .cardBox {
+      position: relative;
+      ul {
+        width: 140px;
+        height: 100%;
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        position: absolute;
+        left: 10px;
+        top: -20px;
+        li {
+          width: 100%;
+          display: flex;
+          align-items: center;
+          .dian {
+            width: 14px;
+            height: 14px;
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            background-color: #0a434a;
+            margin-right: 10px;
+            div {
+              width: 8px;
+              height: 8px;
+              border-radius: 50%;
+              background-color: #1ca2ff;
+            }
+          }
+          .text {
+            span {
+              color: #1ca2ff;
+            }
+          }
+        }
+      }
+      .chart {
+        width: calc(100% - 140px);
+        height: 100%;
+        position: absolute;
+        left: 150px;
+        top: -10px;
+      }
+    }
+  }
+}
+</style>

+ 106 - 0
client/src/views/OaSystem/oaViews/personnelView/mapView/chart1.vue

@@ -0,0 +1,106 @@
+<template>
+  <div class="w-100% h-100%" id="myChart3"></div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import * as echarts from 'echarts'
+const initChart = () => {
+  let myChart = echarts.init(document.getElementById('myChart3'))
+  var symbolSizeArr = [78, 65, 98, 71, 97, 61]
+  let option = {
+    tooltip: {
+      show: false
+    },
+    xAxis: {
+      show: false
+    },
+    yAxis: {
+      show: false
+    },
+    series: [
+      {
+        type: 'graph',
+        layout: 'force',
+        force: {
+          repulsion: 200,
+          edgeLength: 10
+        },
+        label: {
+          show: true,
+          textStyle: {
+            color: '#fff', // 标签字体颜色
+            fontSize: 14, // 标签字体大小
+            fontFamily: 'AlibabaPuHuiTiR' // 标签字体
+          },
+          // formatter: ['{title|{c}人}', '{name|{b}}'].join('\n'),
+          formatter: function (value) {
+            if (value.name.length > 2) {
+              return (
+                value.name.substring(0, 2) +
+                '\n' +
+                value.name.substring(2, value.name.length) +
+                '\n' +
+                value.value +
+                '人'
+              )
+            } else {
+              return value.name + '\n' + value.value + '人'
+            }
+          }
+        },
+        itemStyle: {
+          color: function (params) {
+            var colorList = [
+              'rgba(9,217,205,0.5)',
+              'rgba(28,162,255,0.5)',
+              'rgba(228,163,6,0.5)',
+              'rgba(238,102,102,0.5)'
+            ]
+
+            var color = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+              {
+                offset: 0,
+                color: colorList[params.dataIndex % 6]
+              },
+              {
+                offset: 1,
+                color: colorList[params.dataIndex % 6]
+              }
+            ])
+            return color //大概最新的柱子颜色始终一样的 不太理解
+          }
+        },
+        symbolSize: function (value, params) {
+          return symbolSizeArr[params.dataIndex]
+        },
+        draggable: true, //设置是否可拖动
+        data: [
+          {
+            name: '系统分析师',
+            value: 233
+          },
+          {
+            name: '项目管理师',
+            value: 549
+          },
+          {
+            name: '系统架构师',
+            value: 229
+          },
+          {
+            name: '其他',
+            value: 218
+          }
+        ]
+      }
+    ]
+  }
+  myChart.setOption(option)
+}
+/** 初始化 **/
+
+onMounted(() => {
+  initChart()
+})
+</script>
+<style lang="scss" scoped></style>

+ 18 - 0
client/src/views/OaSystem/oaViews/personnelView/mapView/mapChart.vue

@@ -0,0 +1,18 @@
+<template>
+  <div class="w-100% h-100%" id="myChart4"></div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import * as echarts from 'echarts'
+const initChart = () => {
+  let myChart = echarts.init(document.getElementById('myChart4'))
+  let option = {}
+  myChart.setOption(option)
+}
+/** 初始化 **/
+
+onMounted(() => {
+  initChart()
+})
+</script>
+<style lang="scss" scoped></style>

+ 268 - 0
client/src/views/OaSystem/oaViews/personnelView/mapView/mapView.vue

@@ -0,0 +1,268 @@
+<template>
+  <div class="map-view-com">
+    <div class="map-chart">
+      <MapChart />
+    </div>
+
+    <div class="bomCard">
+      <div class="card1">
+        <CardView
+          class="CardViewC1"
+          :title="'职称构成'"
+          :rightTabs="['总览', '按板块']"
+          @rightClick="rightClick1"
+        >
+          <div class="cardBox">
+            <ul>
+              <li v-for="(item, index) in cardview1List" :key="index">
+                <div class="dian">
+                  <div></div>
+                </div>
+                <div class="text">
+                  <p>{{ item.name }}</p>
+                  <span>{{ item.value }} 人</span>
+                </div>
+              </li>
+            </ul>
+            <div class="chart">
+              <Chart1 />
+            </div>
+          </div>
+        </CardView>
+      </div>
+
+      <div class="card2">
+        <CardView class="CardViewC1" :title="'职业培训'">
+          <div class="cardBox">
+            <ul>
+              <li v-for="(item, index) in card2List" :key="index">
+                <div class="box1">
+                  <div class="dian">
+                    <div></div>
+                  </div>
+                  <p>{{ item.name }}</p>
+                </div>
+                <div class="box2">
+                  <p>{{ item.ykz }}次</p>
+                  <div>
+                    <span>已开展</span>
+                  </div>
+                </div>
+                <div class="box3">
+                  <p>{{ item.dkz }}次</p>
+                  <div>
+                    <span>待开展</span>
+                  </div>
+                </div>
+              </li>
+            </ul>
+          </div>
+        </CardView>
+      </div>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import CardView from '../../components/cardView.vue'
+import Chart1 from './chart1.vue'
+import MapChart from './mapChart.vue'
+const cardview1List = ref([
+  {
+    name: '高级职称',
+    value: '28'
+  },
+  {
+    name: '中级职称',
+    value: '28'
+  },
+  {
+    name: '初级职称',
+    value: '28'
+  }
+])
+const card2List = ref([
+  {
+    name: '通识类',
+    ykz: '100',
+    dkz: '10'
+  },
+  {
+    name: '规划类',
+    ykz: '6',
+    dkz: '10'
+  },
+  {
+    name: '研发类',
+    ykz: '6',
+    dkz: '10'
+  }
+])
+const rightClick1 = (value) => {
+  console.log(value)
+}
+/** 初始化 **/
+onMounted(() => {})
+</script>
+<style lang="scss" scoped>
+@import '../../publicView.scss';
+
+.map-view-com {
+  position: relative;
+
+  .map-chart {
+    width: 100%;
+    height: calc(100% - 100px);
+  }
+  .bomCard {
+    height: 35%;
+    width: 100%;
+    position: absolute;
+    bottom: 10px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .card1,
+    .card2 {
+      width: 358px;
+      height: 100%;
+    }
+    .card1 {
+      margin-right: 200px;
+
+      .CardViewC1 {
+        height: 100%;
+        .cardBox {
+          position: relative;
+          ul {
+            width: 140px;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            flex-wrap: wrap;
+            position: absolute;
+            left: 10px;
+            top: -20px;
+            li {
+              width: 100%;
+              display: flex;
+              align-items: center;
+              .dian {
+                width: 14px;
+                height: 14px;
+                border-radius: 50%;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                background-color: #0f505e;
+                margin-right: 10px;
+                div {
+                  width: 8px;
+                  height: 8px;
+                  border-radius: 50%;
+                  background-color: #09d9cd;
+                }
+              }
+              .text {
+                p {
+                  color: #09d9cd;
+                }
+                span {
+                  color: #fff;
+                }
+              }
+            }
+          }
+          .chart {
+            width: calc(100% - 140px);
+            height: 100%;
+            position: absolute;
+            left: 150px;
+            top: -10px;
+          }
+        }
+      }
+    }
+
+    .card2 {
+      .CardViewC1 {
+        height: 100%;
+        .cardBox {
+          height: calc(100% + 40px);
+          ul {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            li {
+              display: flex;
+              align-items: center;
+              justify-content: space-between;
+              flex-wrap: wrap;
+              width: 100px;
+              height: calc(100% - 30px);
+              .box1 {
+                width: 100%;
+                display: flex;
+                align-items: center;
+                .dian {
+                  width: 14px;
+                  height: 14px;
+                  border-radius: 50%;
+                  display: flex;
+                  align-items: center;
+                  justify-content: center;
+                  background-color: #0f505e;
+                  margin-right: 10px;
+                  div {
+                    width: 8px;
+                    height: 8px;
+                    border-radius: 50%;
+                    background-color: #09d9cd;
+                  }
+                }
+                p {
+                  font-size: 18px;
+                }
+              }
+              .box2,
+              .box3 {
+                width: 100%;
+                display: flex;
+                align-items: center;
+                flex-wrap: wrap;
+                justify-content: center;
+                p {
+                  width: 100%;
+                  font-family: AlibabaPuHuiTiB;
+                  font-size: 18px;
+                  text-align: center;
+                  margin-bottom: 10px;
+                }
+                div {
+                  width: 72px;
+                  height: 24px;
+                  background: rgba(2, 182, 122, 0.5);
+                  border-radius: 10px;
+                  display: flex;
+                  align-items: center;
+                  justify-content: center;
+                  span {
+                    font-size: 14px;
+                  }
+                }
+              }
+              .box3 {
+                div {
+                  background: rgba(228, 163, 6, 0.5);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 452 - 0
client/src/views/OaSystem/oaViews/personnelView/rightView/rightView.vue

@@ -0,0 +1,452 @@
+<template>
+  <div class="right-view-com">
+    <CardView
+      class="CardViewC1"
+      :title="'试用、正式、临时、实习'"
+      :rightTabs="['总览', '按板块']"
+      @rightClick="rightClick1"
+    >
+      <div class="cardBox">
+        <ul>
+          <li v-for="(item, index) in card1List" :key="index">
+            <div class="topBj">
+              <p>{{ item.value }} 人</p>
+            </div>
+            <p>{{ item.name }}</p>
+          </li>
+        </ul>
+      </div>
+    </CardView>
+    <CardView
+      class="CardViewC2"
+      :title="'工龄、司龄'"
+      :leftTabs="['规划类', '软件类']"
+      @leftClick="leftClick1"
+      :rightTabs="['总览', '按板块']"
+      @rightClick="rightClick2"
+    >
+      <div class="cardBox">
+        <ul>
+          <li v-for="(item, index) in card2List" :key="index">
+            <p class="p1a">{{ item.name }}</p>
+            <div class="perBox">
+              <div class="per">
+                <div class="bfb" :style="{ background: item.color, width: item.per + '%' }">
+                  <div :style="{ left: 96 + '%', borderTopColor: item.color }"></div>
+                </div>
+              </div>
+              <p :style="{ color: item.color }">{{ item.per }}%</p>
+            </div>
+            <p class="p2a">{{ item.value }}人</p>
+          </li>
+        </ul>
+      </div>
+    </CardView>
+
+    <CardView class="CardViewC3" :title="'人员流动'">
+      <div class="cardBox">
+        <div class="ldBox">
+          <div class="ldTop1">
+            <div class="dian"> <div></div></div>
+            <p style="color: #fff; margin-right: 25px">净流入</p>
+            <p>16人</p>
+          </div>
+          <div class="ldTop2">
+            <div class="rz">
+              <div></div>
+              <p>入职</p>
+            </div>
+            <div class="lz">
+              <div></div>
+              <p>离职</p>
+            </div>
+          </div>
+        </div>
+        <ul>
+          <li v-for="(item, index) in card3List" :key="index">
+            <p style="margin-right: 10px">{{ item.name }}</p>
+            <div class="perBox1">
+              <div class="per">
+                <div :style="{ width: item.rzPer + '%' }"></div>
+              </div>
+              <p>{{ item.rz }}人</p>
+            </div>
+            <div class="perBox2">
+              <div class="per">
+                <div :style="{ width: item.lzPer + '%' }"></div>
+              </div>
+              <p>{{ item.lz }}人</p>
+            </div>
+            <div class="tenBox" v-if="item.upTen != ''">
+              <img src="@/assets/imgs/oaView/up.png" alt="" />
+              <p>{{ item.upTen }}%</p>
+            </div>
+            <div class="tenBox" v-else>
+              <img src="@/assets/imgs/oaView/down.png" alt="" />
+              <p>{{ item.downTen }}%</p>
+            </div>
+          </li>
+        </ul>
+      </div>
+    </CardView>
+  </div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import CardView from '../../components/cardView.vue'
+
+const card1List = ref([
+  {
+    name: '试用工',
+    value: 3
+  },
+  {
+    name: '正式工',
+    value: 276
+  },
+  {
+    name: '临时工',
+    value: 12
+  },
+  {
+    name: '实习生',
+    value: 6
+  }
+])
+const card2List = ref([
+  {
+    name: '1年以下',
+    per: '23',
+    color: '#09D9CD',
+    value: '65'
+  },
+  {
+    name: '1-3年',
+    per: '30',
+    color: '#3AAEFF',
+    value: '84'
+  },
+  {
+    name: '3-5年',
+    per: '18',
+    color: '#E4A306',
+    value: '53'
+  },
+  {
+    name: '5-7年',
+    per: '7',
+    color: '#00D488',
+    value: '21'
+  },
+  {
+    name: '7年以上',
+    per: '22',
+    color: '#717BF9',
+    value: '56'
+  }
+])
+const card3List = ref([
+  {
+    name: '规划板块',
+    rz: '36',
+    lz: '28',
+    rzPer: '56',
+    lzPer: '44',
+    upTen: '50',
+    downTen: ''
+  },
+  {
+    name: '信息板块',
+    rz: '3',
+    lz: '21',
+    rzPer: '13',
+    lzPer: '87',
+    upTen: '',
+    downTen: '32'
+  },
+  {
+    name: '招商板块',
+    rz: '9',
+    lz: '4',
+    rzPer: '69',
+    lzPer: '31',
+    upTen: '31',
+    downTen: ''
+  },
+  {
+    name: '其他部门',
+    rz: '28',
+    lz: '7',
+    rzPer: '80',
+    lzPer: '20',
+    upTen: '80',
+    downTen: ''
+  }
+])
+
+const leftClick1 = (value) => {
+  console.log(value)
+}
+const rightClick1 = (value) => {
+  console.log(value)
+}
+const rightClick2 = (value) => {
+  console.log(value)
+}
+/** 初始化 **/
+onMounted(() => {})
+</script>
+<style lang="scss" scoped>
+@import '../../publicView.scss';
+
+.right-view-com {
+  display: flex;
+  flex-wrap: wrap;
+
+  .CardViewC1 {
+    height: 25%;
+    .cardBox {
+      ul {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        li {
+          width: 27%;
+          .topBj {
+            width: 100%;
+            height: 89px;
+            background: url(@/assets/imgs/oaView/syg.png) no-repeat;
+            background-size: 100% 100%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            p {
+              font-size: 18px;
+              margin-top: -18px;
+              margin-left: 10px;
+            }
+          }
+          p {
+            text-align: center;
+            margin-left: 10px;
+          }
+        }
+        li:nth-child(2) {
+          .topBj {
+            background: url(@/assets/imgs/oaView/zsg.png) no-repeat;
+            background-size: 100% 100%;
+          }
+        }
+        li:nth-child(3) {
+          .topBj {
+            background: url(@/assets/imgs/oaView/lsg.png) no-repeat;
+            background-size: 100% 100%;
+          }
+        }
+        li:nth-child(4) {
+          .topBj {
+            background: url(@/assets/imgs/oaView/sxs.png) no-repeat;
+            background-size: 100% 100%;
+          }
+        }
+      }
+    }
+  }
+  .CardViewC2 {
+    height: 38%;
+    .cardBox {
+      ul {
+        width: 100%;
+        height: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        flex-wrap: wrap;
+        li {
+          width: 100%;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          p {
+            font-size: 16px;
+          }
+          .p1a {
+            width: 57px;
+            text-align: right;
+          }
+          .perBox {
+            width: 215px;
+            display: flex;
+            align-items: center;
+            margin-left: 10px;
+            .per {
+              width: calc(100%);
+              height: 8px;
+              background: rgba(9, 217, 205, 0.16);
+              .bfb {
+                height: 100%;
+                position: relative;
+                div {
+                  position: absolute;
+                  top: -9px;
+                  width: 0;
+                  height: 0;
+                  border-left: 5px solid transparent;
+                  border-right: 5px solid transparent;
+                  border-top: 10px solid red;
+                }
+              }
+            }
+            p {
+              margin: 0 10px;
+            }
+          }
+          .p2a {
+            flex: 1;
+          }
+        }
+      }
+    }
+  }
+  .CardViewC3 {
+    height: 35%;
+
+    .cardBox {
+      padding: 10px;
+      height: calc(100% + 86px);
+      .ldBox {
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .ldTop1 {
+          display: flex;
+          align-items: center;
+          .dian {
+            width: 14px;
+            height: 14px;
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            background-color: #0a434a;
+            margin-right: 10px;
+            div {
+              width: 8px;
+              height: 8px;
+              border-radius: 50%;
+              background-color: #1ca2ff;
+            }
+          }
+          p {
+            color: #1ca2ff;
+            font-size: 18px;
+            font-family: AlibabaPuHuiTiB;
+          }
+        }
+        .ldTop2 {
+          display: flex;
+          align-items: center;
+          .rz {
+            display: flex;
+            align-items: center;
+            margin-right: 15px;
+            div {
+              width: 6px;
+              height: 10px;
+              background: #09d9cd;
+              margin-right: 5px;
+            }
+            p {
+              font-size: 16px;
+            }
+          }
+          .lz {
+            display: flex;
+            align-items: center;
+            div {
+              width: 6px;
+              height: 10px;
+              background: #e4a306;
+              margin-right: 5px;
+            }
+            p {
+              font-size: 16px;
+            }
+          }
+        }
+      }
+      ul {
+        width: 100%;
+        height: calc(100% - 60px);
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        flex-wrap: wrap;
+        li {
+          width: 100%;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          p {
+            color: #fff;
+          }
+
+          .perBox1 {
+            display: flex;
+            align-items: center;
+            .per {
+              width: 50px;
+              height: 10px;
+              background-color: rgba($color: #09d9cd, $alpha: 0.2);
+              div {
+                height: 100%;
+                background-color: #09d9cd;
+              }
+            }
+            p {
+              width: 40px;
+              color: #09d9cd;
+              margin-left: 10px;
+              overflow: hidden;
+              white-space: nowrap;
+              text-overflow: ellipsis;
+              -o-text-overflow: ellipsis;
+            }
+          }
+          .perBox2 {
+            display: flex;
+            align-items: center;
+            .per {
+              width: 50px;
+              height: 10px;
+              background-color: rgba($color: #e4a306, $alpha: 0.2);
+              div {
+                background-color: #e4a306;
+                height: 100%;
+              }
+            }
+            p {
+              width: 40px;
+              color: #e4a306;
+              margin-left: 10px;
+              overflow: hidden;
+              white-space: nowrap;
+              text-overflow: ellipsis;
+              -o-text-overflow: ellipsis;
+            }
+          }
+          .tenBox {
+            display: flex;
+            align-items: center;
+            flex: 1;
+            img {
+              margin-right: 5px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 64 - 0
client/src/views/OaSystem/oaViews/publicView.scss

@@ -0,0 +1,64 @@
+p,
+li,
+div,
+span,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-family: AlibabaPuHuiTiR;
+  color: #fff;
+}
+
+.publicView {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+
+  .left-view {
+    width: 358px;
+    height: calc(100% + 30px);
+    position: absolute;
+    left: 10px;
+    top: -30px;
+
+    .left-view-com {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .chart-map-view {
+    width: calc(100% - 736px);
+    height: 100%;
+
+    .map-view-com {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .right-view {
+    width: 358px;
+    height: calc(100% + 30px);
+    position: absolute;
+    right: 10px;
+    top: -30px;
+
+    .right-view-com {
+      width: 100%;
+      height: 100%;
+    }
+  }
+}
+
+.cardBox {
+  width: 100%;
+  height: 100%;
+  padding: 0 10px;
+}