iMap | 一款基於 Electron 和 Vue 的跨平臺旅行地圖生成器

Trevor發表於2019-02-16

專案地址:https://huangxizhou.com/project/iMap

技術棧

專案目錄

.
├── LICENSE
├── README.md
├── build··········································electron-packager 打包出來的各平臺應用
│   └── icons······································各平臺應用圖示
│       ├── icon.icns
│       ├── icon.ico
│       └── icon.png
├── dist···········································應用構建後的程式碼目錄
│   ├── electron
├── package.json···································應用層級的 package.json
├── src············································electron 入口檔案資料夾
│   ├── index.ejs
│   ├── main·······································electron 主程式檔案資料夾
│   │   ├── index.dev.js
│   │   └── index.js
│   └── renderer···································Vue 相關的目錄
│       ├── App.vue································單頁面的主結構
│       ├── assets·································靜態資原始檔夾
│       │   ├── img································專案配圖
│       │   │   ├── china.png
│       │   │   ├── excel_example.png
│       │   │   └── world.png
│       │   └── my-theme···························iVew 自定義主題相關目錄
│       ├── components·····························Vue 相關元件目錄
│       │   ├── Layout·····························佈局元件
│       │   │   └── Header.vue·····················導航欄
│       │   ├── Page·······························頁面元件
│       │   │   ├── Auth.vue·······················使用者許可權獲取元件
│       │   │   ├── Chart.vue······················製作地圖元件
│       │   │   ├── ForgetPassword.vue·············忘記密碼元件
│       │   │   ├── Help.vue·······················幫助文件元件
│       │   │   ├── Home.vue·······················主頁元件
│       │   │   ├── Login.vue······················登入頁元件
│       │   │   ├── Map.vue························地圖型別元件
│       │   │   ├── MyProject.vue··················我的專案元件
│       │   │   ├── Register.vue···················註冊元件
│       │   │   └── Update.vue·····················登入元件
│       │   └── Ui·································功能元件
│       │       ├── AddPointModal.vue··············新增地點元件
│       │       ├── DelPointModal.vue··············刪除地點元件
│       │       └── EditPointModal.vue·············修改地點元件
│       ├── data···································Echarts 相關資料存放資料夾
│       │   ├── china.json·························中國地圖 json 資料
│       │   ├── map.js·····························匯入座標資料
│       │   └── world.json·························世界地圖 json 資料
│       ├── filter·································Vue 過濾器目錄
│       │   └── index.js
│       ├── main.js································Vue 入口檔案
│       ├── router·································Vue 路由檔案
│       │   └── index.js
│       ├── server·································ajax相關操作檔案
│       │   ├── ajax.js····························二次封裝ajax
│       │   └── url.js·····························介面別名
│       ├── store··································Vuex 資料目錄
│       │   ├── actions.js·························涉及多個 mutations 的 action 集合 
│       │   ├── index.js···························Vuex 入口檔案
│       │   ├── modules····························模組目錄
│       │   │   ├── excel.js·······················Excel 資料相關模組
│       │   │   └── user_info.js···················使用者資料相關模組
│       │   └── mutations_types.js·················mutation-types 宣告  
│       ├── tool···································工具資料夾
│       │   └── index.js
│       └── version································應用版本資料夾
│           └── version.js
└── yarn.lock

介面與資料問題

本專案使用的是官方推薦的axios

Axios 是一個基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。

本來之前想使用 prototype 來寫侵入式程式碼,但維護起來還是很麻煩,最終還是擼了個開箱即用的 axios 二次封裝的js檔案出來:

// POST 請求
post({...obj}) {
  return new Promise((resolve, reject) => {
    axios.post(obj.url, obj.data, {
        headers: {
          "Authorization": user && user.accessToken
        }
      }).then((data) => {
      if(data.data.code === 0) {
        // ...
      } else if (data.data.code === 1)  {
        // ...        
      } else {
        // ...  
      }
    }).catch((data) => {
      reject(data)
    })
  })     
}

一般來說,在接收到後端返回的資料時,可以使用resolve(data.data)來直接處理資料,當然也可以按需引入一些 UI 元件與過濾器,直接在 ajax 封裝檔案中就可以過濾資料並進行全域性提示,提升使用者體驗。

對於 GET 請求,由於專案有使用到百度地圖開放 API 來獲取城市經緯度,涉及到 jsonp 跨域問題,尷尬的是 axios 官方說明不會支援 jsonp ,所以只能引入 jsonp 這個 npm 包,並且將請求此介面從 GET 請求中單獨提取出來。

  getLocation({...obj}) {
    return new Promise((resolve, reject) => {
      jsonp(obj.url + `?` + qs.stringify(obj.data) , null,  (err, data) => {
        if (err) {
          Message.error(`請求錯誤`)
        } else {
          if(data.status === 0) {
            resolve(data)
          }
        }
      })
    })
  }

GET 請求 ajax 程式碼封裝與 POST 基本一致,只是將 obj.data 換成 { paramas: obj.data } 即可。

關於 Vuex 的使用

Vuex是個能把元件的共享狀態抽取出來,當做一個全域性單例模式進行管理。這樣不管你在何處改變狀態,都會通知使用該狀態的元件做出相應修改。

在本專案中,需要提取到全域性管理的資料有 userInfoexcelData,前者儲存在全域性中,可以減少請求使用者資訊介面的數量,節約資源,後者由於 Excel 檔案上傳到後端後只做解析,不儲存,所以全域性管理 excelData 很重要。

在這裡就單獨說一說新增城市座標的操作好了:
首先是 mutations_types 宣告檔案

// mutations_types.js
export const ADD_EXCEL_DATA = `ADD_EXCEL_DATA`
...

宣告 statemutations
在 Vuex 中,更改 Vuex 的 store 中的狀態的唯一方法就是 mutations

// modules/excel.js
import * as types from `../mutations_types`

// state
const state = {
  excelData: {}
}

// mutations
const mutations = {
  ...
  // 新增 excelData
  [types.ADD_EXCEL_DATA] (state, data) {
    state.excelData.data.push(data)
  },
  ...
}

// 匯出 state, mutations
export default {
  state,
  mutations
}

然而,Mutations 必須是同步函式,所以我們還需要 actions

// actions.js
import * as types from `./mutations_types`

...
// 新增 excelData
export const addExcelData = ({ commit }, data) => {
    commit(types.ADD_EXCEL_DATA, data)
}
...

最後,再在入口檔案注入 modules 和 actions即可

...
export default new Vuex.Store({
  actions,
  modules: {
    excel
  }
})

這樣一來,關於 excelData 的 資料管理的任督二脈已經打通,現在就看如何來使用了

在元件中可以使用 mapStatemapActions 輔助函式來簡化程式碼
mapState函式用在計算屬性中

// AddPointModal.vue
  computed: {
    ...mapState({
      excelData: state => state.excel.excelData,
      countAlias: `excelData`
    })
  }

mapActions 函式在本專案中用於 methods 中

  methods: {
    ...mapActions({
      addExcelData: `addExcelData`
    })
    ...
}

在函式中呼叫

this.addExcelData(Arrary)

這樣就成功將一個新的陣列元素新增到 excelData 中了。

如何獲取 Excel 資料

本專案採用 Leancloud 來作為後端支援,使用的是Node.js(express),對於獲取使用者的 Excel 資料並不想儲存資料的話,本專案的思路是將 Node 作為一個檔案中轉的存在,上傳使用者 Excel 檔案至伺服器後,在 /public 目錄下儲存Excel檔案,再在下次使用者上傳時,清空 /public 目錄。

使用者 Excel 處理的庫本專案選擇的是 node-xlsx

特別注意的是,在前端檔案中,上傳使用者 Excel 檔案是需要使用 formData 來上傳的

let formData = new FormData()
formData.append(`file`, this.file)

後端使用 formidable 這個庫來接收 Excel 檔案並使用 form.parse 來解析。

關於使用 Echarts

ECharts,一個純 Javascript 的圖表庫,可以流暢的執行在 PC 和移動裝置上,相容當前絕大部分瀏覽器(IE8/9/10/11,Chrome,Firefox,Safari等),底層依賴輕量級的 Canvas 類庫 ZRender,提供直觀,生動,可互動,可高度個性化定製的資料視覺化圖表。

對於在 Vue 專案中使用 Echarts , 本專案採用的是 vue-echarts
chart.vue 檔案中,我們利用一個三元運算子來動態切換地圖型別

this.selectMapType === `china` ?  ECharts.registerMap(`china`, chinaMap) : ECharts.registerMap(`world`, worldMap)

對於地圖資料的匯入,本專案的方法是的是匯入一個物件,返回地圖 option 資料:

// map.js
...
export default {
    getMapData({...obj}) {
    ...
        return {
           // 地圖配置資料
        }
    }
}

再在前端檔案中使用這個函式即可: this.option = map.getMapData(this.excelData)

Vue 中父子元件雙向繫結

在本專案中,我將新增、編輯、刪除地點的彈窗全部提取到公用 Ui 元件資料夾中,然而這就需要利用 v-model 來實現元件 props 雙向繫結

// template
<Modal :value="value" v-model="showModal"></Modal>


// script
export default {
  props: {
      value: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return: {
        showModal: false
    }
  },
  watch:{
    value(val) {
      this.showModal = val
    },
    showModal(val) {
      this.$emit(`input`, val)
    }
  },
  mounted() {
    if (this.value) {
      this.showModal = true;
    }
  }
}

這樣就能使用value來儲存 v-model 的值,從而進行雙向繫結。

在MacOS 上打包 Win32 軟體包

在 electron-vue 文件中有這麼一句話:

If you are wanting to build for Windows with a custom icon using a non-Windows platform, you must have wine installed.

那麼我們就來安裝wine。首先確保你的 Mac 已經安裝 homebrew,執行 brew install wine 來安裝wine。

接下來,會出現一個錯誤提示,提示我們需要安裝 Xquartz,按照錯誤提示給的下載網址下載即可。

再次執行 brew install wine

安裝成功後在專案目錄下執行 npm run build:win32 就可以打包成Win32 安裝包了

求一份工作~

2019年畢業生求帶走

我的簡歷

相關文章