前端开发

更新时间:
2024-04-26

前端开发

本节介绍电机数据可视化应用案例前端开发流程。

步骤 1:创建工程

  1. 在后端工程的同级目录下打开终端,执行命令 npm init vue@latest ,按照下图来勾选选项,构建初始项目。

    img

  2. 勾选完成后,按照指示命令进入目录,依次执行安装依赖环境。

    img

  3. 完成上述步骤后,将得到如下项目目录:

    web                    工程根目录
    ├── node_modules       所有的项目依赖包都放在这个目录下
    ├── public             公共文件夹
        └── favicon.ico        网站的显示图标
    ├── index.html         入口的 html 文件
    └── src                源文件目录,编写的代码基本都在这个目录下
        ├── assets             放置静态资源的目录
        ├── components         Vue 的组件文件,自定义的组件都会放到这
        ├── router             Vue-router Vue 路由的配置文件
        ├── store              存放 Pinia 为 Vue 专门开发的状态管理器
        ├── views              存放视图文件
        ├── App.vue            根组件,这个在 Vue 中也有
        └── main.js            入口文件
    ├── .eslintrc.js       Eslint 配置文件
    ├── .gitignore         配置 git 管理
    ├── .prettierrc.json   prettierrc 配置文件
    ├── package.json       命令配置和包管理文件
    ├── README.md          项目的说明文件
    └── vite.config.js     代理、webpack 打包、插件配置等都会在这里进行
    

步骤 2:EdgerOS 安全校验

  1. utils 文件夹下中创建 auth.js 文件,其负责获取工业边缘计算机前后端通讯的密钥和授权码。

    const auth = {
      token: '',
      srand: ''
    };
    
    export function getAuth() {
      return { token: auth.token, srand: auth.srand };
    }
    
    export function setHeaders(payload) {
      auth.srand = payload.srand;
      auth.token = payload.token;
    }
    
    export function getHeaders() {
      return {
        'edger-token': auth.token,
        'edger-srand': auth.srand
      };
    }
    
  2. utils 文件夹下创建 permission.js 文件,其负责向工业边缘计算机申请一系列权限。

    import { edger } from '@edgeros/web-sdk';
    
    let permsObtain = {};
    
    export function setPerms(perms) {
      permsObtain = perms;
    }
    
    export function checkPerms(perms) {
      const code = [];
      perms.forEach(perm => {
        code.push(perm);
      });
      return code;
    }
    // all types of permission
    const permissionArray = [
      'ainn',
      'alarm',
      'share',
      'notify',
      'advnwc',
      'network',
      'display',
      'rtsp',
      'lora',
      'coap',
      'wallpaper',
      'account',
      'printer',
      'auxstorage',
      'vpn',
      'mqtt.publish',
      'mqtt.subscribe',
      'mediacenter.readable',
      'mediacenter.writable',
      'mediacenter.removable'
    ];
    // all types of phone
    const phoneArray = ['camera', 'contacts', 'microphone', 'geolocation', 'browser'];
    
    export function requestPerm() {
      let permission = [];
      let phone = [];
      permsNeed.forEach(item => {
        if (permissionArray.indexOf(item) !== -1) {
          permission.push(item);
        }
        if (phoneArray.indexOf(item) !== -1) {
          phone.push(item);
        }
      });
    
      let configPrem = {
        code: permission,
        type: 'permissions'
      };
    
      let configPhone = {
        code: phone,
        type: 'phone'
      };
    
      // request permission type
      edger.permission.request(configPrem).then(data => {
        console.log(`permissionRequest:${JSON.stringify(data)}`);
        // request phone type
        edger.permission.request(configPhone).then(data => {
          console.log(`phoneRequest:${JSON.stringify(data)}`);
        });
      });
    }
    
  3. main.js 中,使用 @edgeros/web-sdk 获取前后端通讯需要的 token 和 srand,并使用 auth.js 文件中的 setHeaders 设置它们的值。

    import { createApp } from 'vue';
    import App from './App.vue';
    import { edger } from '@edgeros/web-sdk';
    import axios from 'axios';
    import { addScoket } from '@/utils/socket.js';
    import { setToken, setSrand, getHeaders } from './utils/auth.js';
    import { setPerms, checkPerms, requestPerm } from './lib/permission';
    
    // Create Virtual Dom.
    const app = createApp(App);
    // Use web-sdk.
    edger
      .token()
      .then(result => {
        // Get Headers and set.
        const { token, srand } = result;
        // Set token and srand
        setHeaders({ token, srand });
        // Create WebSocket.
        addSocket(token, srand);
      })
      .then(() => {
        const ret = checkPerms(['network', 'advnwc']);
        if (ret.length > 0) {
          requestPerm();
        }
      })
      .catch(err => {
        console.error(err);
      })
      .finally(() => {
        // Mount Virtual Dom.
        app.mount('#app');
      });
    

步骤 3:配置 axios

  1. main.js 文件中,添加 axios 的请求头设置,并在请求头中附加校验信息。

    import axios from 'axios';
    
    axios.interceptors.request.use(config => {
      // Get headers and set axios
      config.headers = getHeaders();
      return config;
    });
    
  2. 使用 Vue 存储库 Pinia 管理需要全局访问的变量。在 stores 文件夹中新建 store.js 文件,并配置需要的全局变量 : resData 及 isClicked。

    import { ref, computed } from 'vue';
    import { defineStore } from 'pinia';
    export const useStore = defineStore('counter', () => {
      const resData = ref({ cycleData: [] });
      const isClicked = ref(false);
    
      return {
        resData,
        isClicked
      };
    });
    

步骤 4:封装图表组件

  1. components 文件夹下新建 SpeedChart.vue 文件,根据自身需求配置好 Echart 图表参数。此处设计了一张示例图表。

    const option = {
      xAxis: {
        type: 'time',
        splitNumber: 4
      },
      yAxis: {
        type: 'value',
        max: 60,
        min: -60,
        splitLine: {
          show: false
        }
      },
      grid: {
        top: 30,
        left: 35,
        right: 5,
        bottom: 24
      },
      series: []
    };
    
  2. 完善创建 Echarts 图表代码。

    <template>
      <div id="chart_panel">
        <div class="title">
          <img src="@/assets/tips.svg" />
          <div class="text">电机转速监控模块</div>
          <div class="scale">(单位:rad/s)</div>
        </div>
        <div class="decorate"></div>
        <div id="chart-board"></div>
      </div>
    </template>
    
    <script setup>
    import * as echarts from 'echarts'
    import { useCounterStore } from '@/stores/counter'
    import { onMounted } from 'vue'
    var store = useCounterStore()
    var chart = null
    
    const buildChart = () => {
      chart = echarts.init(document.getElementById('chart-board'))
      // Use Option Config
      const option = option
      chart.setOption(option)
    }
    
    var series = []
    
    const updateChart = () => {
      const item = {
        type: 'line',
        smooth: true,
        sampling: 'average',
        areaStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            {
              offset: 0,
              color: 'rgba(3,169,244,0.8)'
            },
            {
              offset: 1,
              color: 'rgba(58,77,233,0.3)'
            }
          ])
        },
        data: JSON.parse(JSON.stringify(store.resData.cycleData))
      }
      series = []
      series.push(item)
    
      chart.setOption({
        series
      })
    
      setTimeout(() => {
        chart.resize()
      }, 200)
    }
    
    onMounted(() => {
      buildChart()
    })
    
    store.$subscribe(() => {
      if (store.resData.cycleData.length > 10) {
        store.resData.cycleData.shift()
      }
      updateChart()
    })
    </script>
    <style scoped lang="scss">
    #chart_panel {
      width: 453px;
      height: 337px;
    }
    </style>
    

步骤 5:封装控制组件

  1. components 文件夹下新建 ControlPanel.vue 文件用于编写控制界面。

    <template>
      <div id="control_panel">
        <div class="title">
          <img src="@/assets/tips.svg" />
          <div class="text">设备启停控制模块</div>
        </div>
        <div
          :class="['control_btn', { 'control_btn-active': store.isClicked }, btnClickStyle]"
          @click="controlMotor()"
          @mousedown="mouseDown()"
          @mouseup="mouseDown()"
        >
          <div class="btn_color"></div>
          <img src="@/assets/button.png" style="width: 100%; height: 100%" />
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    import { ref } from 'vue'
    import axios from 'axios'
    import { useCounterStore } from '@/stores/counter'
    const store = useCounterStore()
    const btnClickStyle = ref({ 'control_btn-click': false })
    const mouseDown = () => {
      btnClickStyle.value['control_btn-click'] = !btnClickStyle.value['control_btn-click']
    }
    // 改变电机运行状态的请求函数
    const controlMotor = async () => {
      store.isClicked = !store.isClicked
      let res = await axios.post('/state', { run: store.isClicked })
      console.log(res)
    }
    </script>
    

步骤 6:编写主页内容

  1. views 文件夹下新建 HomeView.vue 文件用于编写主页内容,并将封装好的两个组件引入其中。

    <template>
      <div class="content">
        <div class="left">
          <SpeedChart></SpeedChart>
        </div>
    
        <div class="right">
          <ProductCycle></ProductCycle>
        </div>
      </div>
    </template>
    
    <script setup lang="ts">
    import { useStore } from '@/stores/store.js'
    import SpeedChart from './components/SpeedChart.vue'
    import ProductCycle from './components/ControlPanel.vue'
    
    import axios from 'axios'
    const store = useStore()
    
    // 每秒获取电机转速的请求函数
    setInterval(async () => {
      let resData = await axios.get('/state')
      console.log(resData)
      if (resData.data) {
        // 请求成功
        if (resData.data.time && resData.data.data.rotateSpeed) {
          store.resData.cycleData.push([resData.data.time, resData.data.data.rotateSpeed])
        }
      } else {
        console.error('connect error :', resData.data.message)
      }
    }, 1000)
    </script>
    
    <style scoped lang="scss">
    @import '@/assets/style/base.scss';
    // 模块
    .content {
      display: grid;
      grid-template-columns: 1fr 1fr;
      margin: 0 auto;
      font-weight: normal;
      box-sizing: border-box;
    
      .left,
      .right {
        margin-top: vh(192);
      }
    
      .left {
        margin-right: 0px;
      }
    
      .right {
        margin-left: 37px;
      }
    }
    </style>
    

步骤 7:前端全览工程

说明:

如果直接使用全览工程,需要在当前目录打开终端,输入 npm install 安装依赖。

完整前端工程结构及代码请参考 visual_develop_web 工程示例open in new window

文档内容是否对您有所帮助?
有帮助
没帮助