(系列十三)Vue3+Echarts搭建超好看的系統皮膚

陈逸子风發表於2024-12-02

說明

該文章是屬於OverallAuth2.0系列文章,每週更新一篇該系列文章(從0到1完成系統開發)。

該系統文章,我會盡量說的非常詳細,做到不管新手、老手都能看懂。

說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+視覺化流程管理系統。

友情提醒:本篇文章是屬於系列文章,看該文章前,建議先看之前文章,可以更好理解專案結構。

qq群:801913255,進群有什麼不懂的儘管問,群主都會耐心解答。

有興趣的朋友,請關注我吧(*^▽^*)。

關注我,學不會你來打我

實現功能

  1、系統登入後的視覺化皮膚頁面

  原本不想寫這篇文章,因為它和我們的系統許可權、框架沒有實質性的關係,但耐不住群友的軟磨硬泡,便答應了下來。

  我起初的設想,這塊功能直接上傳到碼雲上,群友可以根據自己搭建的系統,酌情修改。

安裝echarts

  npm install echarts --save

搭建皮膚頁面

在panel資料夾,開啟index頁面,編寫佈局程式碼

<template>
  <div style="display: flex; height: 1080px">
    <div style="width: 80%">
      <div
        style="height: 25%; display: flex; margin-bottom: 5px"
        class="boxStyle"
      >
        <div style="width: 50%" class="boxStyle">
        </div>
        <div
          style="width: 50%; margin-left: 5px;"
          class="boxStyle"
        >
         2
        </div>
      </div>
      <div style="height: 45% ;margin-bottom: 5px"  class="boxStyle">
       3
      </div>
      <div style="height: 28%" class="boxStyle">
        4
      </div>
    </div>
    <div
      style="width: 20%; margin-left: 5px; display: flow-root"
      class="panelContent boxStyle"
    >
      <div
        style="width: 100%; height: 50%; margin-bottom: 5px"
        class="boxStyle"
      >
        特色功能
      </div>
      <div style="width: 100%; height: 50%" class="boxStyle">關於作者</div>
    </div>
  </div>
</template>
<script  lang="ts" >
import * as echarts from "echarts";
import { defineComponent } from "vue";
export default defineComponent({
  props: {
    // openPageData: {
    //   type: Object as PropType<buttonModel>,
    //   required: true,
    // },
  },

  setup(props, context) {  
    
    return {};
  },
  components: {},
});
</script>
<style scoped>
.panelContent {
  font-size: 12px;
  justify-content: right;
  align-items: center;
}
.boxStyle {
  border: 1px solid #00152914;
  box-shadow: 0 1px 4px #00152914;
}
</style>

    

佈局樣式如下

填充盒子1的內容

在api資料夾下,建立一個資料夾panel,然後再該資料夾下建立一個echarts.ts的檔案,編寫如下程式碼

export const echartsOne = {
    title: {
      text: "系統六芒星圖",
    },
    color: ['#67F9D8'],
    //backgroundColor: "#013954",  //背景樣式
    radar: {
      // 雷達圖的指示器,表示多個變數的標籤
      indicator: [
        { name: "好用", max: 5 },
        { name: "易懂", max: 5 },
        { name: "簡單", max: 5 },
        { name: "通用", max: 5 },
        { name: "靈活", max: 5 },
        { name: "學習", max: 5 },
      ],
      splitArea: {
        areaStyle: {
          color: ['#adbecf','#77EADF', '#26C3BE', '#64AFE9', '#428BD4','#2177cd'],
          shadowColor: 'rgba(0, 0, 0, 0.2)',
          shadowBlur: 10
        }
      },
      axisName: {
        formatter: '【{value}】',
        color: '#428BD4'
      },
    },
    series: [
      {
        type: "radar",
        // 雷達圖的資料
        data: [
          {
            value: [5, 5, 5, 5, 5, 5],
          },
        ],
      },
    ],
  };

該程式碼是echarts的雷達圖程式碼。

回到頁面,我們把盒子1裡的內容替換成

  <div id="echarts-one" style="width: 100%; height: 100%"></div>
然後再setup下新增如下程式碼
  onMounted(() => {
      GetEchartsOneData();
    });
    function GetEchartsOneData() {
      var myChart = echarts.init(document.getElementById("echarts-one"));
      myChart.setOption(echartsOne);
    }

注意這裡的onMounted鉤子函式需要新增引用,echartsOne也需要匯入。點選vue的提示即可。

完成以上程式碼,我們預覽看下效果

照葫蘆畫瓢,我們開始編寫盒子2、3、4的內容

在echarts.ts中新增

//南丁格爾玫瑰圖
export const echartsTWO = {
    title: {
        text: "模組訪問佔比",
    },
    toolbox: {
        show: true,
    },
    legend: {
        bottom: "10",
    },
    // backgroundColor: "#013954",  //背景樣式
    series: [
        {
            name: "Nightingale Chart",
            type: "pie",
            radius: [25, 80],
            center: ["50%", "50%"],
            roseType: "area",
            // itemStyle: {
            //   borderRadius: 8,
            // },
            data: [
                { value: 40, name: "選單許可權" },
                { value: 38, name: "角色許可權" },
                { value: 32, name: "列許可權" },
                { value: 30, name: "行許可權" },
                { value: 28, name: "按鈕許可權" },
                { value: 18, name: "介面許可權" },
                { value: 26, name: "流程" },
                { value: 22, name: "表單" },
            ],
        },
    ],
};

//中國地圖
export const chinaGeoCoordMap = ref<any>({
    黑龍江: [127.9688, 45.368],
    內蒙古: [110.3467, 41.4899],
    吉林: [125.8154, 44.2584],
    北京市: [116.4551, 40.2539],
    遼寧: [123.1238, 42.1216],
    河北: [114.4995, 38.1006],
    天津: [117.4219, 39.4189],
    山西: [112.3352, 37.9413],
    陝西: [109.1162, 34.2004],
    甘肅: [103.5901, 36.3043],
    寧夏: [106.3586, 38.1775],
    青海: [101.4038, 36.8207],
    新疆: [87.9236, 43.5883],
    西藏: [91.11, 29.97],
    四川: [103.9526, 30.7617],
    重慶: [108.384366, 30.439702],
    山東: [117.1582, 36.8701],
    河南: [113.4668, 34.6234],
    江蘇: [118.8062, 31.9208],
    安徽: [117.29, 32.0581],
    湖北: [114.3896, 30.6628],
    浙江: [119.5313, 29.8773],
    福建: [119.4543, 25.9222],
    江西: [116.0046, 28.6633],
    湖南: [113.0823, 28.2568],
    貴州: [106.6992, 26.7682],
    雲南: [102.9199, 25.4663],
    廣東: [113.12244, 23.009505],
    廣西: [108.479, 23.1152],
    海南: [110.3893, 19.8516],
    上海: [121.4648, 31.2891],
});
export const chinaDatas = [
    [
        {
            name: "黑龍江",
            value: 0,
        },
    ],
    [
        {
            name: "內蒙古",
            value: 0,
        },
    ],
    [
        {
            name: "吉林",
            value: 0,
        },
    ],
    [
        {
            name: "遼寧",
            value: 0,
        },
    ],
    [
        {
            name: "河北",
            value: 0,
        },
    ],
    [
        {
            name: "天津",
            value: 0,
        },
    ],
    [
        {
            name: "山西",
            value: 0,
        },
    ],
    [
        {
            name: "陝西",
            value: 0,
        },
    ],
    [
        {
            name: "甘肅",
            value: 0,
        },
    ],
    [
        {
            name: "寧夏",
            value: 0,
        },
    ],
    [
        {
            name: "青海",
            value: 0,
        },
    ],
    [
        {
            name: "新疆",
            value: 0,
        },
    ],
    [
        {
            name: "西藏",
            value: 0,
        },
    ],
    [
        {
            name: "四川",
            value: 0,
        },
    ],
    [
        {
            name: "重慶",
            value: 0,
        },
    ],
    [
        {
            name: "山東",
            value: 0,
        },
    ],
    [
        {
            name: "河南",
            value: 0,
        },
    ],
    [
        {
            name: "江蘇",
            value: 0,
        },
    ],
    [
        {
            name: "安徽",
            value: 0,
        },
    ],
    [
        {
            name: "湖北",
            value: 0,
        },
    ],
    [
        {
            name: "浙江",
            value: 0,
        },
    ],
    [
        {
            name: "福建",
            value: 0,
        },
    ],
    [
        {
            name: "江西",
            value: 0,
        },
    ],
    [
        {
            name: "湖南",
            value: 0,
        },
    ],
    [
        {
            name: "貴州",
            value: 0,
        },
    ],
    [
        {
            name: "廣西",
            value: 0,
        },
    ],
    [
        {
            name: "海南",
            value: 0,
        },
    ],
    [
        {
            name: "上海",
            value: 1,
        },
    ],
];
var convertData = function (data: string | any[]) {
    var res = [];
    for (var i = 0; i < data.length; i++) {
        var dataItem = data[i];
        var fromCoord = chinaGeoCoordMap.value[dataItem[0].name];
        var toCoord = [103.9526, 30.7617];
        if (fromCoord && toCoord) {
            res.push([
                {
                    coord: fromCoord,
                    value: dataItem[0].value,
                },
                {
                    coord: toCoord,
                },
            ]);
        }
    }
    return res;
};
export const series: {
    type: string;
    zlevel: number;
    coordinateSystem: string;
    effect: {
        show: boolean;
        period: number; //箭頭指向速度,值越小速度越快
        trailLength: number; //特效尾跡長度[0,1]值越大,尾跡越長重
        symbol: string; //箭頭圖示
        symbolSize: number;
        brushType: string;
        scale: number
    };
    rippleEffect:any;
    label: {},
    symbol: string;
    symbolSize: {},
    itemStyle: {},
    lineStyle: {
        normal: {
            width: number; //尾跡線條寬度
            opacity: number; //尾跡線條透明度
            curveness: number; //尾跡線條曲直度
        };
    };
    data: any
}[] = [];
[["四川", chinaDatas as any]].forEach(function (item, i) {
    series.push(
        {
            type: "lines",
            coordinateSystem: "geo",
            zlevel: 2,
            rippleEffect:[],
            effect: {
                show: true,
                period: 4, //箭頭指向速度,值越小速度越快
                trailLength: 0.02, //特效尾跡長度[0,1]值越大,尾跡越長重
                symbol: "arrow", //箭頭圖示
                symbolSize: 5, //圖示大小
                brushType: "",
                scale: 0
            },
            label: [],
            symbol: "",
            symbolSize: [],
            itemStyle: [],
            lineStyle: {
                normal: {
                    width: 1, //尾跡線條寬度
                    opacity: 1, //尾跡線條透明度
                    curveness: 0.3, //尾跡線條曲直度
                },
            },
            data: convertData(item[1]),
        },
        {
            type: "effectScatter",
            coordinateSystem: "geo".toString(),
            zlevel: 2,
            effect:{} as any,
            rippleEffect: {
                //漣漪特效
                period: 4, //動畫時間,值越小速度越快
                brushType: "stroke", //波紋繪製方式 stroke, fill
                scale: 4,
                show: false,
                trailLength: 0,
                symbol: "",
                symbolSize: 0
            },
            label: {
                normal: {
                    show: true,
                    position: "right", //顯示位置
                    offset: [5, 0], //偏移設定
                    formatter: function (params: { data: { name: any } }) {
                        //圓環顯示文字
                        return params.data.name;
                    },
                    fontSize: 13,
                },
                emphasis: {
                    show: true,
                },
            },
            symbol: "circle",
            symbolSize: function (val: number[]) {
                return 5 + val[2] * 5; //圓環大小
            },
            itemStyle: {
                normal: {
                    show: false,
                    color: "#f00",
                },
            },
            lineStyle: {
                normal: {
                    width: 1, //尾跡線條寬度
                    opacity: 1, //尾跡線條透明度
                    curveness: 0.3, //尾跡線條曲直度
                },
            },
            data: item[1].map(function (
                dataItem: {
                    name: any;
                    value: any;
                }[]
            ) {
                return {
                    name: dataItem[0].name,
                    value: chinaGeoCoordMap.value[dataItem[0].name].concat([dataItem[0].value]),
                };
            }),
        },
        //被攻擊點
        {
            type: "scatter",
            coordinateSystem: "geo",
            zlevel: 2,
            rippleEffect:{} as any,
            effect: {
                period: 4,
                brushType: "stroke",
                scale: 4,
                show: false,
                trailLength: 0,
                symbol: "",
                symbolSize: 0
            },
            label: {
                normal: {
                    show: true,
                    position: "right",
                    //offset:[5, 0],
                    color: "#0f0",
                    formatter: "{b}",
                    textStyle: {
                        color: "#0f0",
                    },
                },
                emphasis: {
                    show: true,
                    color: "#f60",
                },
            },
            symbol: "pin",
            symbolSize: 50,
            itemStyle: [],
            lineStyle: '' as any,
            data: [
                {
                    name: item[0],
                    value: chinaGeoCoordMap.value[item[0].toString()].concat([10]),
                },
            ],
        }
    );
});
export const echartsThree = {
    title: {
        text: "各省訪問數量",
    },
    tooltip: {
      trigger: "item",
      backgroundColor: "rgba(166, 200, 76, 0.82)",
      borderColor: "#FFFFCC",
      showDelay: 0,
      hideDelay: 0,
      enterable: true,
      transitionDuration: 0,
      extraCssText: "z-index:100",
      formatter: function (
        params: { name: any; value: { [x: string]: any }; seriesIndex: number },
        ticket: any,
        callback: any
      ) {
        //根據業務自己擴充要顯示的內容
        var res = "";
        var name = params.name;
        var value = params.value[params.seriesIndex + 1];
        res =
          "<span style='color:#fff;'>" + name + "</span><br/>資料:" + value;
        return res;
      },
    },
    //backgroundColor: "#013954",
    visualMap: {
      //圖例值控制
      min: 0,
      max: 1,
      calculable: true,
      show: true,
      color: ["#f44336", "#fc9700", "#ffde00", "#ffde00", "#00eaff"],
      textStyle: {
        color: "#fff",
      },
    },
    geo: {
      map: "china",
      zoom: 1.2,
      label: {
        emphasis: {
          show: false,
        },
      },
      roam: false, //是否允許縮放
      itemStyle: {
        normal: {
          color: "rgba(51, 69, 89, .5)", //地圖背景色
          borderColor: "#516a89", //省市邊界線00fcff 516a89
          borderWidth: 1,
        },
        emphasis: {
          color: "rgba(37, 43, 61, .5)", //懸浮背景
        },
      },
    },
    series: series,
  };

  //堆疊圖
  export const echartsFour = {
    title: {
      text: "系統訪問量走勢圖",
    },
   // backgroundColor: "#6a7985",  //背景樣式
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "cross",
        label: {
          backgroundColor: "#6a7985",
        },
      },
    },
    legend: {
      data: ["選單許可權", "角色許可權", "按鈕許可權", "行許可權", "列許可權"],
    },
    toolbox: {
      // feature: {
      //   saveAsImage: {},
      // },
    },
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
    },
    xAxis: [
      {
        type: "category",
        boundaryGap: false,
        data: ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"],
      },
    ],
    yAxis: [
      {
        type: "value",
      },
    ],
    series: [
      {
        name: "選單許可權",
        type: "line",
        stack: "Total",
        areaStyle: {},
        emphasis: {
          focus: "series",
        },
        data: [120, 132, 101, 134, 90, 230, 210],
      },
      {
        name: "角色許可權",
        type: "line",
        stack: "Total",
        areaStyle: {},
        emphasis: {
          focus: "series",
        },
        data: [220, 182, 191, 234, 290, 330, 310],
      },
      {
        name: "按鈕許可權",
        type: "line",
        stack: "Total",
        areaStyle: {},
        emphasis: {
          focus: "series",
        },
        data: [150, 232, 201, 154, 190, 330, 410],
      },
      {
        name: "行許可權",
        type: "line",
        stack: "Total",
        areaStyle: {},
        emphasis: {
          focus: "series",
        },
        data: [320, 332, 301, 334, 390, 330, 320],
      },
      {
        name: "列許可權",
        type: "line",
        stack: "Total",
        label: {
          show: true,
          position: "top",
        },
        areaStyle: {},
        emphasis: {
          focus: "series",
        },
        data: [820, 932, 901, 934, 1290, 1330, 1320],
      },
    ],
  };

在頁面新增

把盒子2所在的地方替換成<div id="echarts-tow" style="width: 100%; height: 100%"></div>
把盒子3所在的地方替換成<div id="echarts-three" style="width: 100%; height: 100%"></div>
把盒子4所在的地方替換成<div id="echarts-four" style="width: 100%; height: 100%"></div>
 onMounted(() => {
      GetEchartsOneData();
      GetEchartsTwoData();
      GetEchartsThreeData();
      GetEchartsFourData();
    });

    //六芒星圖
    function GetEchartsOneData() {
      var myChart = echarts.init(document.getElementById("echarts-one"));
      myChart.setOption(echartsOne);
    }

    //南丁格爾玫瑰圖
    function GetEchartsTwoData() {
      var myChart = echarts.init(document.getElementById("echarts-tow"));
      myChart.setOption(echartsTWO);
    }

    //中國地圖
    function GetEchartsThreeData() {
      var myChart = echarts.init(document.getElementById("echarts-three"));
      echarts.registerMap("china", chinaJson as any); //註冊可用的地圖
      myChart.setOption(echartsThree);
    }

    //堆疊圖
    function GetEchartsFourData() {
      var myChart = echarts.init(document.getElementById("echarts-four"));
      myChart.setOption(echartsFour);
    }

特別注意

  1、在新增中國地圖時,需要下載一箇中國地圖的json包,放在專案中(我是放在和echarts.ts同級目錄下)。下載地址:https://datav.aliyun.com/portal/school/atlas/area_selector

  2、在tsconfig.json檔案中需要新增"resolveJsonModule": true,的配置,該配置可以讓系統允許匯入json。

預覽

相容性調整

  對應echarts來說,每個圖表,它是固定的,就算設定的是百分比,也不隨著窗體的大小而自適應螢幕,如下圖

要解決以上問題,我們只需要新增一個方法即可

 //圖示相容性調整
    function resizeEchart(myChart:any)
    {
      //監聽視窗大小變化(適用於一個頁面多個圖形)
      window.addEventListener('resize',()=>{myChart.resize();})
    }

然後再myChart.setOption()方法後面新增resizeEchart(myChart);即可解決相容性問題,如圖

  //堆疊圖
    function GetEchartsFourData() {
      var myChart = echarts.init(document.getElementById("echarts-four"));
      myChart.setOption(echartsFour);
      resizeEchart(myChart);
    }

結語

我們的OverallAuth2.0專案也正式邁入功能開發階段,可能文章內容逐漸開始複雜化,如果你感興趣的話,也有跟著博主從0到1搭建許可權管理系統的興趣。

那麼請加qq群:801913255,進群有什麼不懂的儘管問,群主都會耐心解答。

後端WebApi 預覽地址:http://139.155.137.144:8880/swagger/index.html

前端vue 預覽地址:http://139.155.137.144:8881

關注公眾號:傳送【許可權】,獲取前後端程式碼

有興趣的朋友,請關注我微信公眾號吧(*^▽^*)。

關注我:一個全棧多端的寶藏博主,定時分享技術文章,不定時分享開源專案。關注我,帶你認識不一樣的程式世界

相關文章