[vue]跨域|部署|大屏|地圖|樣式|等問題的解決

最近項目一個接著一個 好久沒有總結了 記錄以下問題的解決方式

  • vue + springboot 開發環境跨域
  • vue + nginx 生產環境部署
  • vue + axios 配置
  • vue + element-ui 修改默認樣式
  • vue + echarts 實現散點路徑地圖
  • vue + filesaver + xlsx 實現前端導出報表
  • vue 拖動生成圖表思路
  • vue 大屏適配方案
  • vue 多設備適配方案

vue + springboot 開發環境跨域

之前用的解決方式一直是后臺加注解

  @CrossOrigin

這次我在配置文件中增加了代理

vue.config.js

module.exports = {
  devServer: {
    // 設置代理
    proxy: {
      //  代理所有/api/cleannet開頭的請求 其中api為自定義的虛擬前綴
      //  虛擬前綴的作用是解決代理地址和路由地址沖突的情況
      '/api/cleannet': {
        // 目標地址 手動?打碼
        target: 'http://10.?.?.?:9876/',
        // 將主機標頭的原點更改為目標URL
        changeOrigin: true,
        //  將自定義的虛擬前綴替換掉 保證真實請求無誤
        pathRewrite: {
          '^/api': '/'
        }
      }
    }
  }

.vue中的請求接口的代碼如下

mounted() {
    this.getData();
  },
  methods: {
    getData: function() {
      this.$axios
        .post("/api/cleannet/foo/foo/foo", {
          pagesize: this.pagesize,
          currentpage: this.currentpage
        })
        .then(response => {
          // do next
        });
    }
}

注意
1.api是我自定義的虛擬前綴 真實的接口地址沒有這一層 需要覆蓋掉
2.api存在的意義在于解決接口地址頁面地址沖突的情況

vue + nginx 生產環境部署

最開始的時候直接將打包好的dist丟到nginxhtml目錄下
帶來的問題是頁面空白
然后采用如下解決方式 將本來為history模式的mode修改為默認的hash或者干脆去掉
再將publicPath/修改為./
這樣就帶來另一個一個問題
那就是頁面的路徑會帶上丑丑的#
如一個開發環境的登錄頁面http://localhost:8080/login帶了生產環境就會變成http://10.?.?.?:8080/dist/index.html/#/login 這樣看起來很奇怪而且增加了用戶的錄入成本 因此這次我先在本地試了nginx的部署方式

mac 安裝 nginx

首先要保證你的mac上有homebrew 沒有的話講如下代碼拷貝至終端執行

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

如果已經有了homebrew 先更新一下

brew update

我在更新的時候報錯了

xcrun: error: invalid active developer path 
(/Library/Developer/CommandLineTools), missing xcrun at: 
/Library/Developer/CommandLineTools/usr/bin/xcrun

可以看出來是x-code出了問題 原因大概是我更新了MacOSCatalina
總之 在終端執行以下代碼即可修復

xcode-select --install

更新好homebrew之后 開始安裝nginx

brew install nginx

nginx常用命令如下

// 啟動ng
nginx
// 停止ng
nginx -s stop
// 重啟ng
nginx -s reload

通常我們需要用到的兩個目錄位置如下
前端dist存放目錄
/usr/local/var/www/
nginx配置文件nginx.conf位置
/usr/local/etc/nginx/

然后我們修改nginx.conf的配置如下 此時vue的所有配置未修改

location / {
  alias  /usr/local/var/www/dist/;
  index  index.html;
}

此時訪問根路徑結果如下

image.png

而訪問我們的工程路徑login結果如下

image.png

依照官網的解決方案在nginx.conf的根目錄下添加如下配置

try_files $uri $uri/ /index.html;

變成如下配置

location / {
  alias  /usr/local/var/www/dist/;
  index  index.html;
  try_files $uri $uri/ /index.html;
}

再訪問http://localhost:9989/login就可以了
可以看到我們已經實現了去掉#

接下來要解決另一個問題 之前我們為了解決開發環境聯調的問題添加了自定義的接口前綴api 那么如何在正式環境的nginx配置里將它規避掉呢 添加配置如下

location /api {
  rewrite /api/(.*)$ /$1 break; 
  proxy_pass http://10.?.?.?:9876;
}

這樣就會將api過濾掉直接在我們的ip和端口后拼上真實的請求地址

在部署生產的時候發現更目錄被占用 那么就要如何解決將vue項目部署到非根目錄的問題

首先嘗試在nginx.conf配置多個server

server {
  listen       7789;
  server_name  localhost;
  
  location / {
    alias  /usr/otherProgram;
    index  index.html index.htm;
  }

server {
  listen       9989;
  server_name  localhost;

  location / {
    alias  /usr/myProgram;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

這個方法是可行的 但是配置多個server意味著多個端口 被告知只打通一個端口 放棄

嘗試自定義虛擬路徑 配置如下

  location @rewrites {
    rewrite ^/(newcleannet)/(.+)$ /$1/index.html last;
  }
        
  location ^~/newcleannet/ {
    alias  /usr/local/var/www/dist/;
    index  index.html index.htm;
    try_files $uri $uri/ @rewrites;
  }

訪問頁面顯示空白

image.png

這時去修改vue項目的配置

router.js

mode: 'history',
base: '/newcleannet/',
// base: process.env.BASE_URL,

vue.config.js

// 生產環境是否生成source map文件 false可以防止反編譯和加快構建速度
productionSourceMap: false,
publicPath: '/newcleannet/',
// publicPath: '/',

重新打包部署再測試 可行

vue + axios 配置

之前的項目沒有對axios進行過多配置 這次接口有諸多限制 因此簡單配置如下

axiosConfig.js

import axios from 'axios'
import qs from 'qs' //  序列化表單
import {
  Message,
  Loading
} from 'element-ui'
import router from '../router'
// 請求的超時時間
axios.defaults.timeout = 5 * 1000
// 配置cookie
// axios.defaults.withCredentials = true
// 配置請求頭
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

// 配置接口地址
// axios.defaults.baseURL = ''

let loadingInstance
// 請求攔截器 post傳參序列化
axios.interceptors.request.use(
  config => {
    loadingInstance = Loading.service({
      lock: true,
      text: '拼命加載數據中',
      spinner: 'el-icon-loading',
      background: 'rgba(0, 0, 0, 0.6)'
    })
    if (config.method === 'post') {
      config.data = qs.stringify(config.data)
    }
    return config
  },
  err => {
    loadingInstance.close()
    Message.error('請求錯誤')
    return Promise.reject(err)
  }
)
// 返回攔截器 返回狀態判斷
axios.interceptors.response.use(
  res => {
    if (res.data.STATUS === '0000') {
      loadingInstance.close()
      return res.data
    } else {
      loadingInstance.close()
      Message.error(res.data.MESSAGE)
    }
  },
  err => {
    loadingInstance.close()
    Message.error('請求失敗')
    if (err.response && err.response.data.STATUS === '401') {
      Message.error(err.response.data.MESSAGE)
      router.push('/login')
    }
    return Promise.reject(err)
  }
)

main.js

import axios from 'axios'
import './config/axiosConfig' //  axios請求配置

vue + element-ui 修改默認樣式

需要修改element-ui的默認樣式 定制主題無法完全滿足 修改的時候以前是通過!important后綴覆蓋然后不寫scoped 這樣帶來的問題是 頁面路由之后很容造成樣式互相污染 這次采用如下解決方式

Login.vue

<template>
...

<el-input 
  v-model="form.name" 
  class="custom-input" 
  @keyup.enter.native="signIn"
>
</el-input>

...
</template>

<style scoped>
.custom-input /deep/ .el-input__inner {
  width: 220px;
  background: none;
  color: rgba(255, 255, 255, 87%);
}
.custom-input /deep/ .el-input__inner:focus {
  border-color: #bb86fc;
}
.custom-input /deep/ .el-input__suffix {
  display: none;
}
</style>

主要在需要修改的樣式父層寫自定義的樣式如.custom-input 然后寫/deep/用來實現樣式穿透 最后加上scoped保證樣式的作用域只在當前文件生效

vue + echarts 實現散點路徑地圖

之前用的是通過ak在線加載百度地圖的方式 但是這次主要針對內網用戶 所以使用了離線地圖的方式 直接上代碼

Dashboard.vue

<template>
...

<HighRiskMap />

...
</template>

<script>
import HighRiskMap from "@/components/Charts3/HighRiskMap.vue";
export default {
  components: {
    HighRiskMap
  }
}
</script>

HighRiskMap.vue

<template>
  <div id="china_map"></div>
</template>

<script>
let echarts = require("echarts/lib/echarts"); // 主模塊
require("echarts/lib/chart/scatter"); // 散點圖
require("echarts/lib/chart/effectScatter"); // 散點圖放大
require("echarts/lib/chart/map"); // 地圖
require("echarts/lib/component/legend"); // 圖例
require("echarts/lib/component/tooltip"); // 提示框
require("echarts/lib/component/geo"); // 地圖geo
require("echarts/map/js/china"); // 中國地圖JS文件

export default {
  data() {
    return {
      geoCoordMap: {},
      data: [],
      lines: []
    };
  },
  mounted() {
    this.getData();
  },
  methods: {
    getData() {
      this.$axios.get("/api/?/?/?").then(response => {
        if (response) {
          this.geoCoordMap = response.geoCoord;
          this.data = response.dataHover;
          this.lines = response.lines;
          this._render_de_map();
        } else {
          this.$message.warning("沒有找到匹配的數據");
        }
      });
    },
    convertData: function(data) {
      let res = [];
      for (let i = 0; i < data.length; i++) {
        let geoCoord = this.geoCoordMap[data[i].name];
        if (geoCoord) {
          res.push({
            name: data[i].name,
            value: geoCoord.concat(data[i].value)
          });
        }
      }
      return res;
    },
    _render_de_map: function() {
      let chinaMap = echarts.init(document.getElementById("china_map"));
      chinaMap.setOption({
        backgroundColor: "transparent",
        tooltip: {
          trigger: "item",
          formatter: function(params) {
            return params.name + " : " + params.value[2];
          }
        },
        legend: {
          orient: "vertical",
          left: "left",
          top: "top",
          data: ["高危地區前五", "高危地區"],
          textStyle: {
            fontSize: this.GLOBAL.fontSize(0.14),
            color: "rgba(255,255,255,60%)"
          }
        },
        geo: {
          map: "china",
          zoom: 1.5,
          roam: true,
          label: {
            emphasis: {
              show: false
            }
          },
          itemStyle: {
            // 地圖背景色
            normal: {
              areaColor: "rgba(24,255,255,38%)",
              borderColor: "rgba(24,255,255,60%)",
              shadowBlur: 12,
              shadowColor: "#1a237e"
            },
            // 懸浮時
            emphasis: {
              areaColor: "rgba(24,255,255,87%)"
            }
          }
        },
        series: [
          {
            type: "lines",
            coordinateSystem: "geo",
            polyline: true,
            data: this.lines,
            silent: true,
            lineStyle: {
              normal: {
                color: "#18ffff",
                opacity: 0.2,
                width: 2
              }
            },
            progressiveThreshold: 500,
            progressive: 200
          },
          {
            type: "lines",
            coordinateSystem: "geo",
            polyline: true,
            data: this.lines,
            lineStyle: {
              normal: {
                color: "#80deea",
                width: 0
              }
            },
            effect: {
              constantSpeed: 30,
              show: true,
              trailLength: 0.2,
              symbolSize: 2
            },
            zlevel: 1
          },
          {
            name: "高危地區",
            type: "scatter",
            coordinateSystem: "geo",
            data: this.convertData(this.data),
            symbolSize: this.GLOBAL.fontSize(0.12),
            label: {
              normal: {
                show: false,
                fontSize: this.GLOBAL.fontSize(0.12)
              },
              emphasis: {
                show: false
              }
            },
            itemStyle: {
              normal: {
                color: "#ffab40",
                shadowBlur: 6,
                shadowColor: "#ff6f00"
              }
            }
          },
          {
            name: "高危地區前五",
            type: "effectScatter",
            coordinateSystem: "geo",
            data: this.convertData(
              this.data
                .sort(function(a, b) {
                  return b.value - a.value;
                })
                .slice(0, 5)
            ),
            symbolSize: this.GLOBAL.fontSize(0.12),
            showEffectOn: "render",
            rippleEffect: {
              brushType: "stroke"
            },
            hoverAnimation: true,
            label: {
              normal: {
                formatter: "{b}",
                position: "right",
                show: true,
                fontSize: this.GLOBAL.fontSize(0.12)
              }
            },
            itemStyle: {
              normal: {
                color: "#ff4081",
                shadowBlur: 6,
                shadowColor: "#880e4f"
              }
            },
            zlevel: 1
          }
        ]
      });
    }
  }
};
</script>

<style scoped>
#china_map {
  width: 100%;
  height: 320px;
}
</style>

請注意 對于echarts的字體無法根據屏幕大小變化導致在大屏里顯示過小的問題 上面代碼也給出了解決方法

HighRiskMap.vue

...
fontSize: this.GLOBAL.fontSize(0.12)
...

Global.vue

// 大屏字體適配
const fontSize = function(res) {
  let clientWidth =
    window.innerWidth ||
    document.documentElement.clientWidth ||
    document.body.clientWidth;
  if (!clientWidth) return;
  let fontSize = 100 * (clientWidth / 1440);
  return res * fontSize;
};

這樣就實現了離線的地圖的散點和路徑效果

vue + FileSaver + XLSX 實現前端導出報表

這個方法可以將當前頁面的表格數據導出為Excel

安裝依賴并引入依賴

yarn add file-saver xlsx

ReplyTracking.vue

<el-tooltip content="導出當前" placement="top">
  <el-button 
    class="dark-sec-btn" 
    icon="jw-icon-daochu3" 
    circle 
    @click="exportTable">
  </el-button>
</el-tooltip>

...

<script>
export default {
  methods: {
    exportTable: function() {
      this.GLOBAL.exportTable(this.$router.history.current.meta.name);
    }
  }
}
</script>

Global.vue

<script>
import FileSaver from "file-saver";
import XLSX from "xlsx";

// 前端導出當前表格數據
const exportTable = function(params) {
  // 因為element-ui的表格的fixed屬性導致多出一個table 會下載重復內容 這里刪除掉
  let table = document.querySelector("#tableForExport").cloneNode(true);
  if (table.querySelector(".el-table__fixed-right")) {
    table.removeChild(table.querySelector(".el-table__fixed-right"));
  }
  if (table.querySelector(".el-table__fixed")) {
    table.removeChild(table.querySelector(".el-table__fixed"));
  }
  let wb = XLSX.utils.table_to_book(table);
  let wbout = XLSX.write(wb, {
    bookType: "xlsx",
    bookSST: true,
    type: "array"
  });
  try {
    FileSaver.saveAs(
      new Blob([wbout], { type: "application/octet-stream" }),
      `${params}.xlsx`
    );
  } catch (e) {
    if (typeof console !== "undefined") console.log(e, wbout);
  }
  return wbout;
};
</script>

請注意 對于有el-table固定列的情況 就會在生成的Excel里有完全重復的表格數據 上面代碼也給出了解決方式

let table = document.querySelector("#tableForExport").cloneNode(true);
if (table.querySelector(".el-table__fixed-right")) {
  table.removeChild(table.querySelector(".el-table__fixed-right"));
}
if (table.querySelector(".el-table__fixed")) {
  table.removeChild(table.querySelector(".el-table__fixed"));
}

vue 拖動生成圖表思路

主要用到了這個組件

import VueDraggableResizable from "vue-draggable-resizable";

或者

import draggable from "vuedraggable";

暫時還不夠完美 后續補充

vue 大屏適配方案

主要是通過大屏頁面引入rem.js再配合postcss-px2rem組件實現 .vue不需要自己計算rem的值 依然寫px

rem.js

// rem等比適配配置文件
// 基準大小
const baseSize = 16
// 設置 rem 函數
function setRem () {
  // 當前頁面寬度相對于 1440寬的縮放比例 可根據自己需要修改
  if (document.documentElement.clientWidth >== 1440) {
    const scale = document.documentElement.clientWidth / 1440
    // 設置頁面根節點字體大小(“Math.min(scale, 2)” 指最高放大比例為2 可調整)
    document.documentElement.style.fontSize = baseSize * Math.min(scale, 3) + 'px'
  }
}

// 初始化
setRem()
// 改變窗口大小時重新設置 rem
window.onresize = function () {
  setRem()
}

vue.config.js

// 引入等比適配插件
const px2rem = require('postcss-px2rem')
// 配置基本大小
const postcss = px2rem({
  // 基準大小 baseSize,需要和rem.js中相同
  remUnit: 16
})

module.exports = {
  css: {
    loaderOptions: {
      postcss: {
        plugins: [
          postcss
        ]
      }
    }
  }
}

暫時還不夠完美 后續補充

vue 多設備適配方案

當然是引入大名鼎鼎的手淘適配組件實現 也是正常寫px 設計稿默認540還是720我忘了

main.js

import 'lib-flexible'

vue 大屏分辨率解決方案新思路

app.vue

<style lang="scss">
$bgc1: #4b5790;
$bgc2: #252f56;
html,body {
  margin: 0;
  overflow: hidden;
  height: 100%;
  width: 100%;
}
#app {
  width: 100%;
  height: 100%;
  background: linear-gradient(to bottom, $bgc1, $bgc2);
}
</style>

子組件

child.vue

<div class="wrap" :style="{ transform: 'scale(' + trans + ') translate(-50%, -50%)'}">
  ...
</div>

<script>
...
mounted () {
    this.transformX = document.body.clientWidth / 1920
    this.transformY = document.body.clientHeight / 1080
    if (document.body.clientWidth / document.body.clientHeight > 16 / 9) {
      this.trans = this.transformY
    } else {
      this.trans = this.transformX
    }
  }
...
</script>


<style scoped lang="scss">
.wrap {
  position: relative;
  width: 1920px;
  height: 1080px;
  transform-origin: 0 0;
  background-size: 100% 100%;
  font-family: "PingFang SC", "Microsoft YaHei";
  top: 50%;
  left: 50%;
}
</style>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容