当前位置:   article > 正文

vue3(5):数据大屏+菜单权限+按钮权限_vue3 大屏

vue3 大屏

一、数据大屏

国内echarts镜像站:ISQQW.COM x ECharts 文档(国内同步镜像) - 配置项

echarts图表集:echarts图表集

1.数据大屏适配问题解决

(1)vw与vh单位解决适配问题

vw/vh:新增单位,要求ie8
比如box内部有两个自适应的盒子top和bottom,他们的宽和高都是随着父容器的变化而变化.将box的宽和高设为视图的宽高100vw,100vh.

那么top和bottom的宽高就要计算一个vw是多少。比如100vw=1920px,那么1vw=19.2px,5.2vw=100px.宽就是5.2vw。

缺点:需要计算子容器的宽高;子容器里面的文字不能进行缩放。

  1. .box{
  2. width: 100vw;
  3. height: 100vh;
  4. background-color: blue;
  5. }
  6. .top{
  7. width: 5.2vw;
  8. height: 9.26vh;
  9. background-color: black;
  10. margin-bottom: 2.6vw;
  11. }
  12. .bottom{
  13. width: 5.2vw;
  14. height: 9.26vh;
  15. background-color: white;
  16. }

(2)css3:scale缩放实现

需要计算出缩放比例(返回的比例需要判断用ww还是wh,原则是根据小的这样不会破坏比例),根据比例放大缩小。

缺点:会留白

  1. <style>
  2. * {
  3. margin: 0;
  4. padding: 0;
  5. }
  6. .container {
  7. width: 100vw;
  8. height: 100vh;
  9. background: url(./bg.png) no-repeat;
  10. background-size: cover;
  11. }
  12. .box {
  13. position: fixed;
  14. width: 1920px;
  15. height: 1080px;
  16. background-color: red;
  17. transform-origin: left top;
  18. left: 50%;
  19. top: 50%;
  20. }
  21. .top {
  22. width: 100px;
  23. height: 100px;
  24. background-color: hotpink;
  25. margin-left: 50px;
  26. }
  27. .bottom {
  28. width: 100px;
  29. height: 100px;
  30. background-color: skyblue;
  31. margin-left: 50px;
  32. margin-top: 50px;
  33. }
  34. </style>
  1. <script>
  2. // 控制数据大屏缩放
  3. let box = document.querySelector('.box')
  4. box.style.transform=`scale(${getScale()}) translate(-50%,-50%)`
  5. // 计算缩放比例
  6. function getScale (w=1920,h=1080){
  7. // 宽缩放比例
  8. const ww = window.innerWidth/w;
  9. // 高缩放比例
  10. const wh = window.innerHeight/h;
  11. return ww<wh?ww:wh;
  12. }
  13. window.onresize = () => {
  14. box.style.transform = `scale(${getScale()}) translate(-50%,-50%)`
  15. }
  16. </script>

2.screen

(1)写好静态,引入image,screen/images

(2)将子组件封装再引入

src/views/screen/index.vue

  1. <template>
  2. <div class="container">
  3. <!-- 数据大屏展示内容区域 -->
  4. <div class="screen" ref="screen">
  5. <!-- 数据大屏顶部 -->
  6. <div class="top">
  7. <Top />
  8. </div>
  9. <div class="bottom">
  10. <div class="left">
  11. <Tourist class="tourist"></Tourist>
  12. <Sex class="sex"></Sex>
  13. <Age class="age"></Age>
  14. </div>
  15. <div class="center">
  16. <Map class="map"></Map>
  17. <Line class="line"></Line>
  18. </div>
  19. <div class="right">
  20. <Rank class="rank"></Rank>
  21. <Year class="year"></Year>
  22. <Counter class="count"></Counter>
  23. </div>
  24. </div>
  25. </div>
  26. </div>
  27. </template>
  28. <script setup lang="ts">
  29. import { ref, onMounted } from "vue";
  30. //引入顶部的子组件
  31. import Top from './components/top/index.vue';
  32. //引入左侧三个子组件
  33. import Tourist from './components/tourist/index.vue';
  34. import Sex from './components/sex/index.vue';
  35. import Age from './components/age/index.vue'
  36. //引入中间两个子组件
  37. import Map from './components/map/index.vue';
  38. import Line from './components/line/index.vue';
  39. //引入右侧三个子组件
  40. import Rank from './components/rank/index.vue';
  41. import Year from './components/year/index.vue';
  42. import Counter from './components/couter/index.vue'
  43. //获取数据大屏展示内容盒子的DOM元素
  44. let screen = ref();
  45. onMounted(() => {
  46. screen.value.style.transform = `scale(${getScale()}) translate(-50%,-50%)`
  47. });
  48. //定义大屏缩放比例
  49. function getScale(w = 1920, h = 1080) {
  50. const ww = window.innerWidth / w;
  51. const wh = window.innerHeight / h;
  52. return ww < wh ? ww : wh;
  53. }
  54. //监听视口变化
  55. window.onresize = () => {
  56. screen.value.style.transform = `scale(${getScale()}) translate(-50%,-50%)`
  57. }
  58. </script>
  59. <style scoped lang="scss">
  60. .container {
  61. width: 100vw;
  62. height: 100vh;
  63. background: url(./images/bg.png) no-repeat;
  64. background-size: cover;
  65. .screen {
  66. position: fixed;
  67. width: 1920px;
  68. height: 1080px;
  69. left: 50%;
  70. top: 50%;
  71. transform-origin: left top;
  72. .top {
  73. width: 100%;
  74. height: 40px;
  75. }
  76. .bottom {
  77. display: flex;
  78. .right {
  79. flex: 1;
  80. display: flex;
  81. flex-direction: column;
  82. margin-left: 40px;
  83. .rank {
  84. flex: 1.5;
  85. }
  86. .year {
  87. flex: 1;
  88. }
  89. .count {
  90. flex: 1;
  91. }
  92. }
  93. .left {
  94. flex: 1;
  95. height: 1040px;
  96. display: flex;
  97. flex-direction: column;
  98. .tourist {
  99. flex: 1.2;
  100. }
  101. .sex {
  102. flex: 1;
  103. }
  104. .age {
  105. flex: 1;
  106. }
  107. }
  108. .center {
  109. flex: 1.5;
  110. display: flex;
  111. flex-direction: column;
  112. .map {
  113. flex: 4;
  114. }
  115. .line {
  116. flex: 1;
  117. }
  118. }
  119. }
  120. }
  121. }
  122. </style>

3.顶部组件Top

(1)pnpm i moment

(2)关于时间动态展示》先存储当前时间》组件挂载完毕利用定时器,每隔一秒刷新时间》展示时间

(3)跳转首页

src/views/screen/components/top/index.vue

  1. <template>
  2. <div class="top">
  3. <div class="left">
  4. <span class="lbtn" @click="goHome">首页</span>
  5. </div>
  6. <div class="center">
  7. <div class="title">智慧旅游可视化大数据平台</div>
  8. </div>
  9. <div class="right">
  10. <span class="rbtn">统计报告</span>
  11. <span class="time">当前时间:{{ time }}</span>
  12. </div>
  13. </div>
  14. </template>
  15. <script setup lang="ts">
  16. //@ts-ignore
  17. import moment from 'moment';
  18. //点击首页按钮回到首页
  19. import { useRouter } from 'vue-router';
  20. import { ref, onMounted, onBeforeUnmount } from 'vue';
  21. //获取路由器对象
  22. let $router = useRouter();
  23. //存储当前时间
  24. let time = ref(moment().format('YYYY年MM月DD日 hh:mm:ss'));
  25. let timer = ref(0);
  26. //按钮的点击回调
  27. const goHome = () => {
  28. $router.push('/home')
  29. }
  30. //组件挂载完毕更新当前的事件
  31. onMounted(() => {
  32. timer.value = setInterval(() => {
  33. time.value = moment().format('YYYY年MM月DD日 hh:mm:ss');
  34. }, 1000);
  35. });
  36. // 销毁定时器
  37. onBeforeUnmount(() => {
  38. clearInterval(timer.value);
  39. })
  40. </script>
  41. <style scoped lang="scss">
  42. .top {
  43. width: 100%;
  44. height: 40px;
  45. display: flex;
  46. .left {
  47. flex: 1.5;
  48. background: url(../../images/dataScreen-header-left-bg.png) no-repeat;
  49. background-size: cover;
  50. .lbtn {
  51. width: 150px;
  52. height: 40px;
  53. float: right;
  54. background: url(../../images/dataScreen-header-btn-bg-l.png) no-repeat;
  55. background-size: 100% 100%;
  56. text-align: center;
  57. line-height: 40px;
  58. color: #29fcff;
  59. font-size: 20px;
  60. }
  61. }
  62. .center {
  63. flex: 2;
  64. .title {
  65. width: 100%;
  66. height: 74px;
  67. background: url(../../images/dataScreen-header-center-bg.png) no-repeat;
  68. background-size: 100% 100%;
  69. text-align: center;
  70. line-height: 74px;
  71. color: #29fcff;
  72. font-size: 30px;
  73. }
  74. }
  75. .right {
  76. flex: 1.5;
  77. background: url(../../images/dataScreen-header-left-bg.png) no-repeat;
  78. background-size: cover;
  79. display: flex;
  80. justify-content: space-between;
  81. align-items: center;
  82. .rbtn {
  83. width: 150px;
  84. height: 40px;
  85. background: url(../../images/dataScreen-header-btn-bg-r.png) no-repeat;
  86. background-size: 100% 100%;
  87. text-align: center;
  88. line-height: 40px;
  89. color: #29fcff;
  90. }
  91. .time {
  92. color: #29fcff;
  93. font-size: 20px;
  94. }
  95. }
  96. }
  97. </style>

4.左侧三个子组件

4.1游客统计组件tourist

(1)pnpm i echarts

(2)pnpm i echarts-liquidfill水球图组件

Apache ECharts

GitHub - ecomfe/echarts-liquidfill: Liquid Fill Chart for Apache ECharts

src/views/screen/components/tourist/index.vue 

  1. <template>
  2. <div class="box">
  3. <div class="top">
  4. <p class="title">实时游客统计</p>
  5. <p class="bg"></p>
  6. <p class="right">可预约总量<span>99999</span></p>
  7. </div>
  8. <div class="number">
  9. <span v-for="(item, index) in people" :key="index">{{ item }}</span>
  10. </div>
  11. <!-- 盒子将来echarts展示图形图标的节点 -->
  12. <div class="charts" ref="charts">123</div>
  13. </div>
  14. </template>
  15. <script setup lang="ts">
  16. import 'echarts-liquidfill'
  17. import * as echarts from 'echarts';
  18. import { ref, onMounted } from 'vue';
  19. let people = ref('215908人');
  20. //水球图拓展插件
  21. //获取节点
  22. let charts = ref();
  23. onMounted(() => {
  24. //获取echarts类的实例
  25. let mycharts = echarts.init(charts.value);
  26. //设置实例的配置项
  27. mycharts.setOption({
  28. //标题组件
  29. title: {
  30. text: '水球图'
  31. },
  32. //x|y轴组件
  33. xAxis: {},
  34. yAxis: {},
  35. //系列:决定你展示什么样的图形图标
  36. series: {
  37. type: 'liquidFill',//系列
  38. data: [0.6, 0.4, 0.2],//展示的数据
  39. waveAnimation: true,//动画
  40. animationDuration: 3,
  41. animationDurationUpdate: 0,
  42. radius: '100%',//半径
  43. outline: {//外层边框颜色设置
  44. show: true,
  45. borderDistance: 8,
  46. itemStyle: {
  47. color: 'skyblue',
  48. borderColor: '#294D99',
  49. borderWidth: 8,
  50. shadowBlur: 20,
  51. shadowColor: 'rgba(0, 0, 0, 0.25)'
  52. }
  53. },
  54. },
  55. //布局组件
  56. grid: {
  57. left: 0,
  58. right: 0,
  59. top: 0,
  60. bottom: 0
  61. }
  62. })
  63. })
  64. </script>
  65. <style scoped lang="scss">
  66. .box {
  67. background: url(../../images/dataScreen-main-lb.png) no-repeat;
  68. background-size: 100% 100%;
  69. margin-top: 10px;
  70. .top {
  71. margin-left: 20px;
  72. .title {
  73. color: white;
  74. font-size: 20px;
  75. }
  76. .bg {
  77. width: 68px;
  78. height: 7px;
  79. background: url(../../images/dataScreen-title.png) no-repeat;
  80. background-size: 100% 100%;
  81. margin-top: 10px;
  82. }
  83. .right {
  84. float: right;
  85. color: white;
  86. font-size: 20px;
  87. span {
  88. color: yellowgreen;
  89. }
  90. }
  91. }
  92. .number {
  93. padding: 10px;
  94. margin-top: 30px;
  95. display: flex;
  96. span {
  97. flex: 1;
  98. height: 40px;
  99. text-align: center;
  100. line-height: 40px;
  101. background: url(../../images/total.png) no-repeat;
  102. background-size: 100% 100%;
  103. color: #29fcff;
  104. }
  105. }
  106. .charts {
  107. width: 100%;
  108. height: 270px;
  109. }
  110. }
  111. </style>

 4.2Sex组件

(1)基础柱状图 - 柱状图 - 常用图表类型 - 应用篇 - 使用手册 - Apache ECharts

(2)柱状图 的使用

 src/views/screen/components/sex/index.vue

  1. <template>
  2. <div class="box1">
  3. <div class="title">
  4. <p>男女比例</p>
  5. <img src="../../images/dataScreen-title.png" alt="">
  6. </div>
  7. <div class="sex">
  8. <div class="man">
  9. <img src="../../images/man.png" alt="">
  10. </div>
  11. <div class="women">
  12. <img src="../../images/woman.png" alt="">
  13. </div>
  14. </div>
  15. <div class="rate">
  16. <p>男士58%</p>
  17. <p>女士42%</p>
  18. </div>
  19. <div class="charts" ref='charts'></div>
  20. </div>
  21. </template>
  22. <script setup lang="ts">
  23. import { ref, onMounted } from 'vue';
  24. import * as echarts from 'echarts';
  25. //获取图形图标的DOM节点
  26. let charts = ref();
  27. onMounted(() => {
  28. //初始化echarts实例
  29. let mycharts = echarts.init(charts.value);
  30. //设置配置项
  31. mycharts.setOption({
  32. //组件标题
  33. title: {
  34. text: '男女比例',//主标题
  35. textStyle: {//主标题颜色
  36. color: 'skyblue'
  37. },
  38. left: '40%'
  39. },
  40. //x|y
  41. xAxis: {
  42. show: false,
  43. min: 0,
  44. max: 100
  45. },
  46. yAxis: {
  47. show: false,
  48. type: 'category'
  49. },
  50. series: [
  51. {
  52. type: 'bar',
  53. data: [58],
  54. barWidth: 20,
  55. z: 100,
  56. itemStyle: {
  57. color: 'skyblue',
  58. borderRadius: 20
  59. }
  60. }
  61. ,
  62. {
  63. type: 'bar',
  64. data: [100],
  65. barWidth: 20,
  66. //调整女士柱条位置
  67. barGap: '-100%',
  68. itemStyle: {
  69. color: 'pink',
  70. borderRadius: 20
  71. }
  72. }
  73. ],
  74. grid: {
  75. left: 0,
  76. top: 0,
  77. right: 0,
  78. bottom: 0
  79. }
  80. });
  81. })
  82. </script>
  83. <style scoped lang="scss">
  84. .box1 {
  85. width: 100%;
  86. height: 100%;
  87. background: url(../../images/dataScreen-main-cb.png) no-repeat;
  88. background-size: 100% 100%;
  89. margin: 20px 0px;
  90. .title {
  91. margin-left: 20px;
  92. p {
  93. color: white;
  94. font-size: 20px;
  95. }
  96. }
  97. .sex {
  98. display: flex;
  99. justify-content: center;
  100. .man {
  101. margin: 20px;
  102. width: 111px;
  103. height: 115px;
  104. background: url(../../images/man-bg.png) no-repeat;
  105. display: flex;
  106. justify-content: center;
  107. align-items: center;
  108. }
  109. .women {
  110. margin: 20px;
  111. width: 111px;
  112. height: 115px;
  113. background: url(../../images/woman-bg.png) no-repeat;
  114. display: flex;
  115. justify-content: center;
  116. align-items: center;
  117. }
  118. }
  119. .rate {
  120. display: flex;
  121. justify-content: space-between;
  122. color: white;
  123. }
  124. .charts {
  125. height: 100px;
  126. }
  127. }
  128. </style>

4.3Age组件

 (1)饼图

圆环图 - 饼图 - 常用图表类型 - 应用篇 - 使用手册 - Apache ECharts

 src/views/screen/components/age/index.vue

  1. <template>
  2. <div class="box2">
  3. <div class="title">
  4. <p>年龄比例</p>
  5. <img src="../../images/dataScreen-title.png" alt="">
  6. </div>
  7. <!-- 图形图标的容器 -->
  8. <div class="charts" ref="charts"></div>
  9. </div>
  10. </template>
  11. <script setup lang="ts">
  12. import { ref, onMounted } from 'vue';
  13. //引入echarts
  14. import * as echarts from 'echarts';
  15. let charts = ref();
  16. //组件挂载完毕初始化图形图标
  17. onMounted(() => {
  18. let mychart = echarts.init(charts.value);
  19. //设置配置项
  20. let option = {
  21. tooltip: {
  22. trigger: 'item'
  23. },
  24. legend: {
  25. right: 30,
  26. top: 40,
  27. orient: 'vertical',//图例组件方向的设置
  28. textStyle: {
  29. color: 'white',
  30. fontSize: 14
  31. }
  32. },
  33. series: [
  34. {
  35. name: 'Access From',
  36. type: 'pie',
  37. radius: ['40%', '70%'],
  38. avoidLabelOverlap: false,
  39. itemStyle: {
  40. borderRadius: 10,
  41. borderColor: '#fff',
  42. borderWidth: 2
  43. },
  44. label: {
  45. show: true,
  46. position: 'inside',
  47. color:'white'
  48. },
  49. labelLine: {
  50. show: false
  51. },
  52. data: [
  53. { value: 1048, name: '军事' },
  54. { value: 735, name: '新闻' },
  55. { value: 580, name: '直播' },
  56. { value: 484, name: '娱乐' },
  57. { value: 300, name: '财经' }
  58. ]
  59. }
  60. ],
  61. //调整图形图标的位置
  62. grid: {
  63. left: 0,
  64. top: 0,
  65. right: 0,
  66. bottom: 0
  67. }
  68. };
  69. mychart.setOption(option);
  70. });
  71. </script>
  72. <style scoped lang="scss">
  73. .box2 {
  74. width: 100%;
  75. height: 100%;
  76. background: url(../../images/dataScreen-main-cb.png) no-repeat;
  77. background-size: 100% 100%;
  78. .title {
  79. margin-left: 20px;
  80. p {
  81. color: white;
  82. font-size: 20px;
  83. }
  84. }
  85. .charts {
  86. height: 260px;
  87. }
  88. }
  89. </style>

5.中间两个子组件

5.1Map组件中国地图

(1)ISQQW.COM x ECharts 文档(国内同步镜像) - 配置项

(2)阿里云网站提供中国地图数据

http://datav.aliyun.com/portal/school/atlas/area_selector

将数据放入map/china.json

注册中国地图》引入json数据》onMounted设置配置项》地图组件

  src/views/screen/components/map/index.vue

  1. <template>
  2. <div class="box4" ref="map">
  3. 我是地图组件
  4. </div>
  5. </template>
  6. <script setup lang="ts">
  7. import { ref, onMounted } from 'vue';
  8. import * as echarts from 'echarts';
  9. //引入中国地图的JSON数据
  10. import chinaJSON from './china.json'
  11. //获取DOM元素
  12. let map = ref();
  13. //注册中国地图
  14. echarts.registerMap('china', chinaJSON as any)
  15. onMounted(() => {
  16. let mychart = echarts.init(map.value);
  17. //设置配置项
  18. mychart.setOption({
  19. //地图组件
  20. geo: {
  21. map: 'china',//中国地图
  22. roam: true,//鼠标缩放的效果
  23. //地图的位置调试
  24. left: 150,
  25. top: 150,
  26. right: 150,
  27. zoom:1.2,
  28. bottom: 0,
  29. //地图上的文字的设置
  30. label: {
  31. show: true,//文字显示出来
  32. color: 'white',
  33. fontSize: 14
  34. },
  35. itemStyle: {
  36. //每一个多边形的样式
  37. color: {
  38. type: 'linear',
  39. x: 0,
  40. y: 0,
  41. x2: 0,
  42. y2: 1,
  43. colorStops: [{
  44. offset: 0, color: 'red' // 0% 处的颜色
  45. }, {
  46. offset: 1, color: 'blue' // 100% 处的颜色
  47. }],
  48. global: false // 缺省为 false
  49. },
  50. opacity: .8
  51. },
  52. //地图高亮的效果
  53. emphasis: {
  54. itemStyle: {
  55. color: 'red'
  56. },
  57. label: {
  58. fontSize: 40
  59. }
  60. }
  61. },
  62. //布局位置
  63. grid: {
  64. left: 0,
  65. top: 0,
  66. right: 0,
  67. bottom: 0
  68. },
  69. series: [
  70. {
  71. type: 'lines',//航线的系列
  72. data: [
  73. {
  74. coords: [
  75. [116.405285, 39.904989], // 起点
  76. [119.306239, 26.075302] // 终点
  77. ],
  78. // 统一的样式设置
  79. lineStyle: {
  80. color: 'orange',
  81. width: 5
  82. }
  83. },
  84. {
  85. coords: [
  86. [116.405285, 39.904989], // 起点
  87. [114.298572,30.584355] // 终点
  88. ],
  89. // 统一的样式设置
  90. lineStyle: {
  91. color: 'yellow',
  92. width: 5
  93. }
  94. }
  95. ],
  96. //开启动画特效
  97. effect: {
  98. show: true,
  99. symbol: 'arrow',
  100. color: 'black',
  101. symbolSize: 10
  102. }
  103. }
  104. ]
  105. })
  106. });
  107. </script>
  108. <style scoped></style>

 5.2Line组件游客趋势折线图

 src/views/screen/components/line/index.vue

  1. <template>
  2. <div class="box5">
  3. <div class="title">
  4. <p>未来七天游客数量趋势图</p>
  5. <img src="../../images/dataScreen-title.png" alt="">
  6. </div>
  7. <div class="charts" ref='line'></div>
  8. </div>
  9. </template>
  10. <script setup lang="ts">
  11. import * as echarts from 'echarts';
  12. import { ref, onMounted } from 'vue';
  13. //获取图形图标的节点
  14. let line = ref();
  15. onMounted(() => {
  16. let mycharts = echarts.init(line.value);
  17. //设置配置项
  18. mycharts.setOption({
  19. //标题组件
  20. title: {
  21. text: '访问量'
  22. },
  23. //x|y轴
  24. xAxis: {
  25. type: 'category',
  26. //两侧不留白
  27. boundaryGap: false,
  28. //分割线不要
  29. splitLine: {
  30. show: false
  31. },
  32. data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
  33. //轴线的设置
  34. axisLine: {
  35. show: true
  36. },
  37. //刻度
  38. axisTick: {
  39. show: true
  40. }
  41. },
  42. yAxis: {
  43. splitLine: {
  44. show: false
  45. },
  46. //轴线的设置
  47. axisLine: {
  48. show: true
  49. },
  50. //刻度
  51. axisTick: {
  52. show: true
  53. }
  54. },
  55. grid: {
  56. left: 40,
  57. top: 0,
  58. right: 20,
  59. bottom: 20
  60. },
  61. //系列
  62. series: [
  63. {
  64. type: 'line',
  65. data: [120, 1240, 66, 2299, 321, 890, 1200],
  66. //平滑曲线的设置
  67. smooth: true,
  68. //区域填充样式
  69. areaStyle: {
  70. color: {
  71. type: 'linear',
  72. x: 0,
  73. y: 0,
  74. x2: 0,
  75. y2: 1,
  76. colorStops: [{
  77. offset: 0, color: 'red' // 0% 处的颜色
  78. }, {
  79. offset: 1, color: 'blue' // 100% 处的颜色
  80. }],
  81. global: false // 缺省为 false
  82. }
  83. }
  84. }
  85. ]
  86. })
  87. })
  88. </script>
  89. <style scoped lang="scss">
  90. .box5 {
  91. width: 100%;
  92. height: 100%;
  93. background: url(../../images/dataScreen-main-cb.png) no-repeat;
  94. background-size: 100% 100%;
  95. margin: 0px 20px;
  96. .title {
  97. margin-left: 10px;
  98. p {
  99. color: white;
  100. font-size: 20px;
  101. }
  102. }
  103. .charts {
  104. height: calc(100% - 40px);
  105. }
  106. }
  107. </style>

6.右侧三个子组件

6.1Rank组件

 src/views/screen/components/rank/index.vue

  1. <template>
  2. <div class="box6">
  3. <div class="title">
  4. <p>热门景区排行</p>
  5. <img src="../../images/dataScreen-title.png" alt="">
  6. </div>
  7. <!-- 图形图标的容器 -->
  8. <div class="charts" ref='charts'></div>
  9. </div>
  10. </template>
  11. <script setup lang="ts">
  12. import * as echarts from 'echarts';
  13. import { ref, onMounted } from 'vue';
  14. //获取DOM节点
  15. let charts = ref();
  16. //组件挂载完毕
  17. onMounted(() => {
  18. //一个容器可以同时展示多种类型的图形图标
  19. let mychart = echarts.init(charts.value);
  20. //设置配置项
  21. mychart.setOption({
  22. //标题组件
  23. title: {
  24. //主标题
  25. text: '景区排行',
  26. link: 'http://www.baidu.com',
  27. //标题的位置
  28. left: '50%',
  29. //主标题文字样式
  30. textStyle: {
  31. color: 'yellowgreen',
  32. fontSize: 20
  33. },
  34. //子标题
  35. subtext: "各大景区排行",
  36. //子标题的样式
  37. subtextStyle: {
  38. color: 'yellowgreen',
  39. fontSize: 16
  40. }
  41. },
  42. //x|y轴组件
  43. xAxis: {
  44. type: 'category',//图形图标在x轴均匀分布展示
  45. },
  46. yAxis: {},
  47. //布局组件
  48. grid: {
  49. left: 20,
  50. bottom: 20,
  51. right: 20
  52. },
  53. //系列:决定显示图形图标是哪一种的
  54. series: [
  55. {
  56. type: 'bar',
  57. data: [10, 20, 30, 40, 50, 60, 70],
  58. //柱状图的:图形上的文本标签,
  59. label: {
  60. show: true,
  61. //文字的位置
  62. position: 'insideTop',
  63. //文字颜色
  64. color: 'yellowgreen'
  65. },
  66. //是否显示背景颜色
  67. showBackground: true,
  68. backgroundStyle: {
  69. //底部背景的颜色
  70. color: {
  71. type: 'linear',
  72. x: 0,
  73. y: 0,
  74. x2: 0,
  75. y2: 1,
  76. colorStops: [{
  77. offset: 0, color: 'black' // 0% 处的颜色
  78. }, {
  79. offset: 1, color: 'blue' // 100% 处的颜色
  80. }],
  81. global: false // 缺省为 false
  82. }
  83. },
  84. //柱条的样式
  85. itemStyle: {
  86. borderRadius:[10, 10, 0, 0],
  87. //柱条颜色
  88. color:function(data:any){
  89. //给每一个柱条这是背景颜色
  90. let arr =['red','orange','yellowgreen','green','purple','hotpink','skyblue']
  91. return arr[data.dataIndex];
  92. }
  93. }
  94. },
  95. {
  96. type:'line',
  97. data:[10,20,30,40,50,60,90],
  98. smooth:true,//平滑曲线
  99. },
  100. {
  101. type: 'bar',
  102. data: [10, 20, 30, 40, 50, 60, 70],
  103. //柱状图的:图形上的文本标签,
  104. label: {
  105. show: true,
  106. //文字的位置
  107. position: 'insideTop',
  108. //文字颜色
  109. color: 'yellowgreen'
  110. },
  111. //是否显示背景颜色
  112. showBackground: true,
  113. backgroundStyle: {
  114. //底部背景的颜色
  115. color: {
  116. type: 'linear',
  117. x: 0,
  118. y: 0,
  119. x2: 0,
  120. y2: 1,
  121. colorStops: [{
  122. offset: 0, color: 'black' // 0% 处的颜色
  123. }, {
  124. offset: 1, color: 'blue' // 100% 处的颜色
  125. }],
  126. global: false // 缺省为 false
  127. }
  128. },
  129. //柱条的样式
  130. itemStyle: {
  131. borderRadius:[10, 10, 0, 0],
  132. //柱条颜色
  133. color:function(data:any){
  134. //给每一个柱条这是背景颜色
  135. let arr =['red','orange','yellowgreen','green','purple','hotpink','skyblue']
  136. return arr[data.dataIndex];
  137. }
  138. }
  139. },
  140. ],
  141. tooltip:{
  142. backgroundColor:'rgba(50,50,50,0.7)'
  143. }
  144. })
  145. })
  146. </script>
  147. <style scoped lang="scss">
  148. .box6 {
  149. width: 100%;
  150. height: 100%;
  151. background: url(../../images/dataScreen-main-cb.png) no-repeat;
  152. background-size: 100% 100%;
  153. margin: 20px 0px;
  154. .title {
  155. margin-left: 5px;
  156. p {
  157. color: white;
  158. font-size: 20px;
  159. }
  160. }
  161. .charts {
  162. height: calc(100% - 30px);
  163. }
  164. }
  165. </style>

6.2Year组件

 src/views/screen/components/year/index.vue

  1. <template>
  2. <div class="box7">
  3. <div class="title">
  4. <p>年度游客量对比</p>
  5. <img src="../../images/dataScreen-title.png" alt="">
  6. </div>
  7. <div class="charts" ref="charts"></div>
  8. </div>
  9. </template>
  10. <script setup lang="ts">
  11. import * as echarts from 'echarts';
  12. import { ref, onMounted } from 'vue';
  13. //获取DOM节点
  14. let charts = ref();
  15. //组件挂载完毕
  16. onMounted(() => {
  17. //一个容器可以同时展示多种类型的图形图标
  18. let mychart = echarts.init(charts.value);
  19. //设置配置项
  20. mychart.setOption({
  21. title: {
  22. text: '散点图',
  23. left: '40%',
  24. textStyle: {
  25. color: 'white'
  26. }
  27. },
  28. xAxis: {
  29. type: 'category',
  30. show: true,
  31. },
  32. yAxis: {
  33. show: false
  34. },
  35. grid: {
  36. left: 20,
  37. top: 20,
  38. right: 0,
  39. bottom: 20
  40. },
  41. series: {
  42. type: 'scatter',
  43. data: [33, 88, 21, 9, 88, 234, 113, 1231, 674, 3, 88, 33, 21, 888, 3332, 313, 123, 5, 657, 7],
  44. //标记图形设置
  45. symbol: 'diamond',
  46. symbolSize: 16,
  47. //图文标签
  48. label: {
  49. show: true,
  50. position: 'top',
  51. color: 'red'
  52. },
  53. //散点图标记的颜色
  54. itemStyle: {
  55. color: {
  56. type: 'linear',
  57. x: 0,
  58. y: 0,
  59. x2: 0,
  60. y2: 1,
  61. colorStops: [{
  62. offset: 0, color: 'red' // 0% 处的颜色
  63. }, {
  64. offset: 1, color: 'blue' // 100% 处的颜色
  65. }],
  66. global: false // 缺省为 false
  67. }
  68. }
  69. }
  70. })
  71. })
  72. </script>
  73. <style scoped lang="scss">
  74. .box7 {
  75. width: 100%;
  76. height: 100%;
  77. background: url(../../images/dataScreen-main-cb.png) no-repeat;
  78. background-size: 100% 100%;
  79. margin: 20px 0px;
  80. .title {
  81. p {
  82. color: white;
  83. font-size: 18px;
  84. }
  85. }
  86. .charts {
  87. height: calc(100% - 30px);
  88. }
  89. }
  90. </style>

6.3Couter组件

 src/views/screen/components/couter/index.vue

  1. <template>
  2. <div class="box8">
  3. <div class="title">
  4. <p>数据统计</p>
  5. <img src="../../images/dataScreen-title.png" alt="">
  6. </div>
  7. <div class="charts" ref="charts"></div>
  8. </div>
  9. </template>
  10. <script setup lang="ts">
  11. import * as echarts from 'echarts';
  12. import { ref, onMounted } from 'vue';
  13. //获取DOM节点
  14. let charts = ref();
  15. //组件挂载完毕
  16. onMounted(() => {
  17. //一个容器可以同时展示多种类型的图形图标
  18. let mychart = echarts.init(charts.value);
  19. let option = {
  20. title: {
  21. text: '游客消费统计',
  22. textStyle:{
  23. color:'white'
  24. }
  25. },
  26. radar: {
  27. // shape: 'circle',
  28. indicator: [
  29. { name: '消费', max: 6500 },
  30. { name: '好感', max: 16000 },
  31. { name: '出行', max: 30000 },
  32. { name: '小吃', max: 38000 },
  33. { name: '爱好', max: 52000 },
  34. { name: '景点', max: 25000 }
  35. ]
  36. },
  37. series: [
  38. {
  39. name: 'Budget vs spending',
  40. type: 'radar',
  41. data: [
  42. {
  43. value: [4200, 3000, 20000, 35000, 50000, 18000],
  44. name: '购物'
  45. },
  46. {
  47. value: [5000, 14000, 28000, 26000, 42000, 21000],
  48. name: '吃饭'
  49. }
  50. ]
  51. }
  52. ]
  53. };
  54. //设置配置项
  55. mychart.setOption(option)
  56. })
  57. </script>
  58. <style scoped lang="scss">
  59. .box8 {
  60. width: 100%;
  61. height: 100%;
  62. background: url(../../images/dataScreen-main-cb.png) no-repeat;
  63. background-size: 100% 100%;
  64. margin-top: 20px;
  65. .title {
  66. p {
  67. color: white;
  68. font-size: 18px;
  69. }
  70. }
  71. .charts {
  72. height: calc(100% - 30px);
  73. }
  74. }
  75. </style>

效果图 

二、菜单权限

1. 路由拆分 

将项目路由拆分为:

  • 静态路由:login、404、home、screen
  • 异步路由:权限管理(包含三个子路由)、商品管理(包含四个子路由)
  • 任意路由:任意路由

src/router/routes.ts

  1. // 对外暴露配置路由(常量路由):全部用户都可以访问到的路由
  2. export const constantRoute = [
  3. {
  4. // 登录
  5. path: '/login',
  6. component: () => import('@/views/login/index.vue'),
  7. name: 'login',
  8. meta: {
  9. title: '登录',
  10. hidden: true,
  11. icon: 'Moon',
  12. },
  13. },
  14. {
  15. // 登录成功以后展示数据的路由
  16. path: '/',
  17. component: () => import('@/layout/index.vue'),
  18. name: 'layout',
  19. meta: {
  20. title: '',
  21. hidden: false,
  22. icon: '', // 菜单文字左侧的图标,支持element-plus全部图标
  23. },
  24. redirect: '/home',
  25. children: [
  26. {
  27. path: '/home',
  28. component: () => import('@/views/home/index.vue'),
  29. meta: {
  30. title: '首页',
  31. hidden: false,
  32. icon: 'Sunny',
  33. },
  34. },
  35. ],
  36. },
  37. {
  38. // 404
  39. path: '/404',
  40. component: () => import('@/views/404/index.vue'),
  41. name: '404',
  42. meta: {
  43. title: '404',
  44. hidden: true,
  45. icon: 'Moon',
  46. },
  47. },
  48. {
  49. path: '/screen',
  50. component: () => import('@/views/screen/index.vue'),
  51. name: 'Screen',
  52. meta: {
  53. title: '数据大屏',
  54. hidden: false,
  55. icon: 'Platform',
  56. },
  57. }
  58. ]
  59. //异步路由
  60. export const asnycRoute = [
  61. {
  62. path: '/acl',
  63. component: () => import('@/layout/index.vue'),
  64. name: 'Acl',
  65. meta: {
  66. title: '权限管理',
  67. icon: 'Lock',
  68. },
  69. redirect: '/acl/user',
  70. children: [
  71. {
  72. path: '/acl/user',
  73. component: () => import('@/views/acl/user/index.vue'),
  74. name: 'User',
  75. meta: {
  76. title: '用户管理',
  77. icon: 'User',
  78. },
  79. },
  80. {
  81. path: '/acl/role',
  82. component: () => import('@/views/acl/role/index.vue'),
  83. name: 'Role',
  84. meta: {
  85. title: '角色管理',
  86. icon: 'UserFilled',
  87. },
  88. },
  89. {
  90. path: '/acl/permission',
  91. component: () => import('@/views/acl/permission/index.vue'),
  92. name: 'Permission',
  93. meta: {
  94. title: '菜单管理',
  95. icon: 'Monitor',
  96. },
  97. },
  98. ],
  99. },
  100. {
  101. path: '/product',
  102. component: () => import('@/layout/index.vue'),
  103. name: 'Product',
  104. meta: {
  105. title: '商品管理',
  106. icon: 'Goods',
  107. },
  108. redirect: '/product/trademark',
  109. children: [
  110. {
  111. path: '/product/trademark',
  112. component: () => import('@/views/product/trademark/index.vue'),
  113. name: 'Trademark',
  114. meta: {
  115. title: '品牌管理',
  116. icon: 'ShoppingCartFull',
  117. },
  118. },
  119. {
  120. path: '/product/attr',
  121. component: () => import('@/views/product/attr/index.vue'),
  122. name: 'Attr',
  123. meta: {
  124. title: '属性管理',
  125. icon: 'ChromeFilled',
  126. },
  127. },
  128. {
  129. path: '/product/spu',
  130. component: () => import('@/views/product/spu/index.vue'),
  131. name: 'Spu',
  132. meta: {
  133. title: 'SPU管理',
  134. icon: 'Calendar',
  135. },
  136. },
  137. {
  138. path: '/product/sku',
  139. component: () => import('@/views/product/sku/index.vue'),
  140. name: 'Sku',
  141. meta: {
  142. title: 'SKU管理',
  143. icon: 'Orange',
  144. },
  145. },
  146. ],
  147. },
  148. ]
  149. // 任意路由
  150. export const anyRoute =
  151. {
  152. // 任意路由
  153. path: '/:pathMatch(.*)*',
  154. redirect: '/404',
  155. name: 'Any',
  156. meta: {
  157. title: '任意路由',
  158. hidden: true,
  159. icon: 'Moon',
  160. },
  161. }

 2 . 菜单权限业务实现 

src/store/modules/user.ts

  1. //创建用户相关的小仓库
  2. import { defineStore } from 'pinia'
  3. //引入接口
  4. import { reqLogin, reqUserInfo, reqLogout } from '@/api/user'
  5. import type {
  6. loginFormData,
  7. loginResponseData,
  8. userInfoResponseData,
  9. } from '@/api/user/type'
  10. import type { UserState } from './types/type'
  11. //引入操作本地存储的工具方法
  12. import { SET_TOKEN, GET_TOKEN, REMOVE_TOKEN } from '@/utils/token'
  13. //引入路由(常量路由)
  14. import { constantRoute, asnycRoute, anyRoute } from '@/router/routes'
  15. //引入深拷贝方法
  16. //@ts-expect-error
  17. import cloneDeep from 'lodash/cloneDeep'
  18. import router from '@/router'
  19. //用于过滤当前用户需要展示的异步路由
  20. function filterAsyncRoute(asnycRoute: any, routes: any) {
  21. return asnycRoute.filter((item: any) => {
  22. if (routes.includes(item.name)) {
  23. if (item.children && item.children.length > 0) {
  24. //硅谷333账号:product\trademark\attr\sku
  25. item.children = filterAsyncRoute(item.children, routes)
  26. }
  27. return true
  28. }
  29. })
  30. }
  31. //创建用户小仓库
  32. const useUserStore = defineStore('User', {
  33. //小仓库存储数据地方
  34. state: (): UserState => {
  35. return {
  36. token: GET_TOKEN(), //用户唯一标识token
  37. menuRoutes: constantRoute, //仓库存储生成菜单需要数组(路由)
  38. username: '',
  39. avatar: '',
  40. //存储当前用户是否包含某一个按钮
  41. buttons: [],
  42. }
  43. },
  44. //异步|逻辑的地方
  45. actions: {
  46. //用户登录的方法
  47. async userLogin(data: loginFormData) {
  48. //登录请求
  49. const result: loginResponseData = await reqLogin(data)
  50. //登录请求:成功200->token
  51. //登录请求:失败201->登录失败错误的信息
  52. if (result.code == 200) {
  53. //pinia仓库存储一下token
  54. //由于pinia|vuex存储数据其实利用js对象
  55. this.token = result.data as string
  56. //本地存储持久化存储一份
  57. SET_TOKEN(result.data as string)
  58. //能保证当前async函数返回一个成功的promise
  59. return 'ok'
  60. } else {
  61. return Promise.reject(new Error(result.data))
  62. }
  63. },
  64. //获取用户信息方法
  65. async userInfo() {
  66. //获取用户信息进行存储仓库当中[用户头像、名字]
  67. const result: userInfoResponseData = await reqUserInfo()
  68. //如果获取用户信息成功,存储一下用户信息
  69. if (result.code == 200) {
  70. this.username = result.data.name
  71. this.avatar = result.data.avatar
  72. this.buttons = result.data.buttons
  73. //计算当前用户需要展示的异步路由
  74. const userAsyncRoute = filterAsyncRoute(
  75. cloneDeep(asnycRoute),
  76. result.data.routes,
  77. )
  78. //菜单需要的数据整理完毕
  79. this.menuRoutes = [...constantRoute, ...userAsyncRoute, anyRoute]
  80. //目前路由器管理的只有常量路由:用户计算完毕异步路由、任意路由动态追加
  81. ;[...userAsyncRoute, anyRoute].forEach((route: any) => {
  82. router.addRoute(route)
  83. })
  84. return 'ok'
  85. } else {
  86. return Promise.reject(new Error(result.message))
  87. }
  88. },
  89. //退出登录
  90. async userLogout() {
  91. //退出登录请求
  92. const result: any = await reqLogout()
  93. if (result.code == 200) {
  94. //目前没有mock接口:退出登录接口(通知服务器本地用户唯一标识失效)
  95. this.token = ''
  96. this.username = ''
  97. this.avatar = ''
  98. REMOVE_TOKEN()
  99. return 'ok'
  100. } else {
  101. return Promise.reject(new Error(result.message))
  102. }
  103. },
  104. },
  105. getters: {},
  106. })
  107. //对外暴露获取小仓库方法
  108. export default useUserStore

刷新的时候是异步路由,有可能获取到用户信息,异步路由还没有加载完毕,出现空白的效果!!

解决方法:在全局前置守卫中,获取用户信息后改成next({...to})

next() :直接放行(这种写法会导致刷新产生空白的效果)
next({...to}):等待路由加载完毕再放行

 src/permission.ts

三、按钮权限

1.用户按钮权限信息存储

src/store/modules/user.ts 

  1. ......
  2. state: (): UserState => {
  3. return {
  4. ......
  5. //存储当前用户是否包含某一个按钮
  6. buttons: [],
  7. }
  8. ......
  9. async userInfo() {
  10. ......
  11. // 如果获取信息成功,存储下用户信息
  12. if (result.code === 200) {
  13. ......
  14. this.buttons = result.data.buttons
  15. ......
  16. }

2.定义全局自定义指令

src/directive/has.ts 

  1. import pinia from "@/store"
  2. import useUserStore from "@/store/modules/user"
  3. const userStore = useUserStore(pinia)
  4. export const isHasButton = (app: any) => {
  5. // 获取对应的用户仓库
  6. // 全局自定义指令:实现按钮的权限
  7. app.directive('has', {
  8. // 代表使用这个全局指令的DOM|组件挂载完毕的时候会执行一次
  9. mounted(el: any, options: any) {
  10. // 自定义指令右侧的数值:如果在用户信息buttons数组中没有
  11. // 从DOM树上干掉
  12. if (!userStore.buttons.includes(options.value)) {
  13. el.parentNode.removeChild(el)
  14. }
  15. },
  16. })
  17. }

 在main.ts文件中引入自定义指令文件

  1. // 引入自定义指令文件
  2. import { isHasButton } from '@/directive/has'
  3. isHasButton(app)

3.使用自定义指令配置按钮权限

此处以trademark作为例子,项目中其他按钮的权限都需要配置

src/views/product/trademark/index.vue

<el-button type="primary" size="default" icon="Plus" @click="addTrademark" v-has="'btn.Trademark.add'">

完结!后续还会复习项目总结一波!也会在做一个项目,有需要的可以关注!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/774062
推荐阅读
相关标签
  

闽ICP备14008679号