沒朋友(MPVUE),就仿個職人小程序吧~

前言

前言不知道說什麼,可是感受你們都有前言,我也不能輸,那就隨便嘮點好了。本人是一名大三學生,是一個前端小菜雞,這是個人第一篇掘金文章。以前和兩個隊友組隊寫了一個原創小程序——猿簡歷(已上線,但還有不少須要完善的)。最近初學了vue,又涉及了一下mpvue,因而就找了個小項目來練練手,既能再找找小程序的感受,又能練練vue的寫法。起初是打算一週練手完成主要功能,因此這真的是一個很小很小的小項目,沒有什麼技術難點,只能分享一些小tips了。還但願各位看官能看的高興。css

部分項目截圖

項目準備

由於我第一次寫mpvue的時候,起初沒法下手,不肯定要怎麼構建項目,因此就把本身構建項目的步驟一併寫了上來,但願能幫助到你。html

初始化項目

初始化項目前,請確保本機已經正確裝好node環境前端

  • 下載vue腳手架vue

    npm install vue-cli -g
    複製代碼
  • 初始化項目node

    vue init mpvue/mpvue-quickstart mpvue-zhirent
    複製代碼

    progarm

    1. 是否使用vuex狀態管理模式+庫),no。緣由:vuex並非時候都須要,這只是個小型的仿寫項目,全部沒有用vuex。webpack

    2. 是否使用ESLint(適用於JavaScript和JSX 的可插入linting 實用程序),no。由於使用了ESLint將會很是麻煩。ios

  • 進入項目根目錄git

    cd mpvue-zhirent
    複製代碼
  • 根據package.json安裝項目依賴包程序員

    npm install
    複製代碼
  • 啓動初始化項目github

    npm start || npm run dev
    複製代碼

定義數據及數據請求的封裝

定義數據

當看到職人小程序時,腦子裏第一反應即是基於MVVM(Model-View-ViewModel)系統架構思想。那麼定義數據便成了項目開發最重要的事情。首先搜索了一下職人的相關網站,沒有找到能夠爬取數據的網站(也多是我沒有看到),就只能定義假數據了。而說到定義假數據,第一想法就是用EasyMock(一個可視化,而且能快速生成 模擬數據的持久化服務)定義假數據了。因而手擼假數據擼到懷疑人生,這時候終於懂了程序員那句最美的情話了——「我給你寫接口好嗎?」。

我定義數據時添加了一個"errno":0,爲的是在請求數據時判斷接口請求的狀態。active和sponsor分別是沙龍數據和主辦方數據。

mock-data

數據請求與數據請求的封裝

此次開發我用了axios來請求數據。

  1. 首先,安裝axios

    $ npm install axios
    複製代碼
  2. build > webpack.base.config.js文件中找到alias爲axios添加別名

    alias: {
      'vue': 'mpvue',
      'axios':'axios/dist/axios',
      '@': resolve('src')
     }
    複製代碼
  3. src > utils下添加一個axios.js文件,封裝axios

    import axios from 'axios'
    import qs from 'qs'
    
    axios.defaults.timeout = 30000;
    axios.defaults.baseURL ='';
    axios.defaults.headers.post[ 'Content-Type' ] = 'application/x-www-form-urlencoded;charset=UTF-8';
    axios.defaults.adapter = function (config) {
      return new Promise((resolve, reject) => {
        let data = config.method === 'get' ? config.params : qs.stringify(config.data)
        wx.request({
          url:config.url,
          method:config.method,
          data:data,
          success:(res)=>{ 
            return resolve(res)
          },
          fail:(err)=>{
            return reject(err)
          }
        })
      })
    }
    //請求攔截器
    axios.interceptors.request.use(function ( request ) {
      // console.log(request) //請求成功
      return request
    }, function ( error ) {
      // console.log(error); //請求失敗
      return Promise.reject(error);
    });
    
    // 添加響應攔截器
    axios.interceptors.response.use(function ( response ) {
      // console.log(response.data.data) //響應成功
      return response;
    }, function ( error ) {
      // console.log(error); //響應失敗
      return Promise.reject(error);
    });
    
    
    export function get (url,params) {
      return axios({
        method:'get',
        url:url,
        params:params
      })
    }
    複製代碼
  4. main.js文件中封裝數據請求

    import Vue from 'vue'
    import App from './App'
    import axios from 'axios'
    // 將get請求引入
    import {get} from './utils/axios'
    
    // 將get請求掛載上去
    Vue.prototype.$get=get;
    
    Vue.prototype.getList = function (){
      wx.showLoading({
        title:'加載中'
      }),
        this.$get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
        .then((res) => {
          if (res.data.errno === 0) {
            this.active = res.data.data.active;
            this.sponsors = res.data.data.sponsors
          }
        })
        wx.hideLoading()
    }
    
    Vue.config.productionTip = false
    App.mpType = 'app'
    
    const app = new Vue(App)
    app.$mount()
    
    複製代碼

stylus編譯

  1. 安裝stylus、stylus-loader包
    $ npm i stylus stylus-loader -D
    複製代碼
  2. build > webpack.base.config.js文件中添加找到rules,添加文件處理規則
    {
        test: /\.css$/,
        loader: 'style-loader!css-loader!stylus-loader'
      }
    複製代碼
    以下圖:
    stylus
  3. 配置完成後重啓服務,必定要重啓服務!!!!

mpvue踩坑之旅Start

好了,該安裝的也安裝了,該配置的也配置了,終於能夠開始愉快又使人激動的Coding之旅了。職人小程序包含了沙龍、主辦方、個人 三個主要頁面,以及沙龍詳情和主辦方詳情兩個頁面,個人頁面包含了我的的活動報名、主辦方收藏、活動收藏等信息。接下來我將分享一些項目中遇到的坑和一些主要功能的實現。

1、添加頁面及自定義tabBar

添加頁面

mpvue添加頁面並不像小程序那樣添加頁面方便。須要瘋狂手動添加文件,手動配置頁面信息,手動添加頁面路徑。每次添加新頁面必定要記得去app.json裏添加路徑信息!!!

1.建立頁面文件

咱們須要在src/pages目錄下手動建立頁面文件夾index,並在index文件夾下添加 index.vuemain.js兩個文件,index.vue則是咱們的html頁面(可將index改成其它,只需在main.js中引入便可),main.js不可更名,若是將main改成其餘名字(相關位置均作修改),都將會出現報錯。

  • index.vue在頁面還未寫任何頁面結構時 須要編寫以下基本結構,不然將會出現

    pages/mine/main.js 出現腳本錯誤或者未正確調用 Page() 的報錯信息

    <template>
      <div></div>
    </template>
    
    <script>
    export default {
    
    }
    </script>
    
    <style>
    
    </style>
    
    複製代碼
  • main.js的基本結構則爲

    import Vue from 'vue'
    import App from './index'  //若是index.vue更名爲home.vue,這裏則將index改成home
    
    const app = new Vue(App)
    app.$mount()
    複製代碼

2.添加頁面

每建立一個新頁面都需在src/app.json文件中的pages手動 添加頁面路徑,不然將會出現

navigateTo:fail page "pages/index/main" is not found的報錯信息

"pages": [
    "pages/index/main"
   ]
複製代碼

3.配置分頁面

能夠經過在onLoad(){}生命週期中調用wx.setNavigationBarTitle({})api動態設置分頁面的navigationBarTitle

onLoad(){
    wx.setNavigationBarTitle({
      title: '受權'
    })
  }
複製代碼

也能夠手動添加main.json文件,靜態設置分頁面的navigationBarTitle

{
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#224fa4",
    "navigationBarTitleText": "職人",
    "navigationBarTextStyle": "white"
}
複製代碼

自定義tabBar

src/app.json下的tabBar下作相應更改便可,和小程序的tabBar自定義過程無異。這裏要注意的是:通常會將tabBar的icon放在static/tabs目錄下,其餘頁面圖標則放在images目錄下,方便管理。頁面的icon能夠選擇svg格式的話就選擇svg的圖片,這樣有利於節省小程序的空間,但小程序的tabBar-icon暫時還不支持svg格式的圖片。我在webpack.base.config.js中設置了圖片路徑別名,故圖片路徑可直接寫static/tabs/car1.png,以下圖:

static

"tabBar": {
    "color": "#999",
    "backgroundColor": "#fff",
    "selectedColor": "#0c53ab",
    "borderStyle": "black",

    "list": [{
      "text": "沙龍",
      "pagePath": "pages/salon/main",
      "iconPath": "static/tabs/car1.png",
      "selectedIconPath": "static/tabs/car.png"
    }, {
      "text": "主辦方",
      "pagePath": "pages/sponsor/main",
      "iconPath": "static/tabs/home1.png",
      "selectedIconPath": "static/tabs/home.png"
    }, {
      "text": "個人",
      "pagePath": "pages/mine/main",
      "iconPath": "static/tabs/mine1.png",
      "selectedIconPath": "static/tabs/mine.png"
    }
  ],
    "position": "bottom"
  }
複製代碼

2、受權頁面

進入小程序,跳轉到受權頁面,受權後跳轉到沙龍頁面,若是用戶點擊拒絕則會停留在該頁面,用戶下次進入小程序時,仍是能跳轉到受權頁面。

受權
那麼如何實現受權頁面呢?

  • 首先,咱們須要在進入小程序時經過調用wx.getSetting()來判斷用戶是否已經受權。若是已經受權,那麼咱們就能夠經過調用wx.getUserInfo()來獲取用戶信息,若是沒有受權,則跳轉到寫好的受權頁面。那麼咱們就須要在App.vue中的onShow或onLaunch生命週期中進行判斷

    // 獲取用戶的當前設置。返回值中只會出現小程序已經向用戶請求過的權限。
    wx.getSetting({
      success(res){
        if (res.authSetting['scope.userInfo']) {
          // 若是已經受權,能夠直接調用 getUserInfo獲取用戶信息
          wx.getUserInfo({
            success: function(res) {
              console.log(res.userInfo)
            }
          })
        }else{
          // 若是未受權,則跳轉到受權頁面
          wx.reLaunch({
            url: '/pages/authorize/main',
          })
        }
      }
    })
    複製代碼
  • 而後,經過小程序<button>自帶的open-type屬性開放getUserInfo能力,進行受權。再經過bindgetuserinfo屬性返回獲取到的用戶信息,經過判斷是否獲取到用戶信息進行判斷是否受權成功,若是成功,則跳轉到沙龍首頁

    <button class="authorize" open-type="getUserInfo" @getuserinfo="getUserInfo">贊成受權</button>
    複製代碼
    getUserInfo(){
      wx.getSetting({
        success:(res) => {
          if(res.authSetting['scope.userInfo']){
            wx.showLoading({
              title:'加載中'
            })
            wx.reLaunch({
              url:'/pages/salon/main'
            })
            wx.hideLoading()
          }
        }
      })
    }
    複製代碼
  • 受權成功後,刷新頁面,就能夠看到打印出來的用戶信息啦

userInfo
就這樣,一個小程序受權頁面就完成啦,是否是很簡單?寫完以後就想起,以前組隊寫一個參賽的小程序,兩個隊友寫小程序受權界面,一個隊友搗鼓了半天說好難寫啊,因而另外一個隊友將受權頁面攬了下來,寫完以後受權頁面老是會在我意想不到的時候出如今我面前,就像瘋狂在對我說

當時由於本身要作的事情真的太多了,產品經理從0到1,UI從0到1,切圖仔從0到1,加上兩個大佬都出問題了,以爲小菜雞的本身就更不可能會了,也就專心作本身分內的事情。當本身嘗試受權界面後,就很想手動艾特我兩個隊友進來捱打。咋肥事啊小老弟?

因此碰見任何難題時,都應該本身動手去嘗試一下,不能以爲別人不會你就必定也不會,必定要本身去嘗試寫一寫!!!

3、沙龍/主辦方頁面

這兩個頁面十分簡單,很直觀的兩個數據渲染頁面。兩個頁面寫法幾乎同樣,我就挑一個代碼少一點的來講了。

數據加載

首先,咱們區別一下onLoad & onShow

  • onLoad 一個頁面只會觸發一次,頁面第一次加載時觸發,通常放一些不須要實時更新的數據
  • onShow 每次打開頁面都會執行一次,頁面加載就觸發,通常放置實時更新的數據

綜上,咱們加載這兩個頁面數據,應該在onLoad生命週期中請求數據。因爲以前咱們已經將數據請求封裝到main.js文件中了,故直接調用封裝的方法便可。

onLoad(){
    this.getList();
  }
複製代碼

數據渲染

很明顯,這兩個頁面都須要用到數據循環,須要使用v-for嵌套時。

// 主辦方頁面
 <div class="sponsor">
    <div v-for="(item,index) in sponsors" :key="index" @click="toSponsorInfo(index)" class="card-container">
      <div class="card-content">
        <div class="card-icon">
          <image class="icon" :src="item.avatar"></image>
        </div>
        <div class="card-info">
          <div class="card-title">{{item.name}}</div>
          <div class="card-desc">{{item.info}}</div>
        </div>
        <button @click.stop="collect(index)" :class="collectList[index] ? 'like' : 'unlike'">{{collectList[index] ? '已關注' : '關注'}}</button>
      </div>
      <div class="card-footer">
        <div class="card-salon-num">共舉辦{{item.salonNum}}場沙龍</div>
        <div v-if="item.salonNum>0" class="card-recently">最近沙龍:{{item.salons[0].title}}</div>
      </div>
    </div>
  </div>
複製代碼

而使用v-for嵌套時須要注意:在mpvue中外循環和內循環不能使用同一個index,須要更改一個其中一個的索引值標識,不然將出現

同一組件內嵌套的 v-for 不能連續使用相同的索引,目前爲: index,index 的報錯信息

stylus的使用

由於咱們在項目準備時已經將stylus配置好了,即可以直接在index.vue中使用stylus

<style lang="stylus" scoped>
</style>
複製代碼

這裏scoped表示該樣式是該頁面的私有樣式,不會污染全局

4、詳情頁跳轉

頁面跳轉傳參

頁面隨機點擊一個列表,都會跳轉到對應詳情頁,這涉及到了頁面傳值的問題。首先,咱們須要獲取當前點擊的列表的index值,而index值咱們在寫v-for循環時,有用到 v-for="(item,index) in sponsors 每個列表的index表明着當前數據的下標值,咱們能夠經過將該值傳遞到詳情頁,再請求數據並經過傳遞的下標值找到對應的數據,最後將拿到的數據渲染到詳情頁就好啦。具體作法以下:

  1. 首先找到循環所在的div標籤,綁定一個事件,並將當前列表的index傳遞過去
    <div v-for="(item,index) in sponsors"  :key="index"  @click="toSponsorInfo(index)" class="card-container">
    複製代碼
  2. <script>中添加methods屬性,在methods屬性中建立寫方法。
    // 接收傳遞進來的index值
    toSponsorInfo(index){
      // 拼接url,並將接收的index值傳到要跳轉的頁面
      const url = `/pages/sponsorInfo/main?index=${index}`
      wx.navigateTo({
        url
      });
    複製代碼
  3. 在詳情頁面中的onLoad生命週期中經過options接收傳遞過來的參數
    onLoad(options){
     let i = options.index;
     wx.showLoading({
      title:'加載中'
    }),
    this.$http
      .get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
      .then((res) => {
        // 拿到對應index的主辦方詳情信息
        this.sponsor = res.data.data.sponsors[i];
         wx.hideLoading()
      });
    }
    
    複製代碼

5、數據查詢

沙龍詳情的頁面中,包含了舉辦該沙龍的主辦方列表,而主辦方詳情裏面,又包含了舉辦的沙龍列表。首先,定義假數據不可能在沙龍數據裏面,將舉辦該沙龍的主辦方詳情也寫上,這樣定義數據太麻煩了,並且會重複不少數據。由於我在一個數據請求中,分了兩條數據,一條是沙龍數據,一條是主辦方數據。這兩條數據就至關於數據庫中的兩個表,但因爲沒有創建數據庫,因此where字段沒法使用,但咱們能夠經過遍歷進行數據查找。
定義數據時,我在 avtive(沙龍)數據中,給出一個 sponsors數組,數組中表明着舉辦該沙龍的主辦方列表,每一個主辦方給出一個 name字段。在 sponsors(主辦方)數據中,給出一個 salons數組,數組表明着該主辦方舉辦過的沙龍列表,每一個沙龍給出一個 title字段。

  • 沙龍詳情中的主辦方列表,這裏給出一個sponsorsNum字段,便於判斷,當主辦方數字大於0時,才須要進行數據遍歷查找。
    沙龍詳情中的主辦方列表
  • 主辦方詳情中的沙龍列表,這裏也給出一個salonNum字段,當舉辦的沙龍數量大於0時,才進行數據遍歷查找。
    主辦方詳情中的沙龍列表

以主辦方詳情爲例

  • 首先,在數據請求時,拿到全部的沙龍數據,
    this.$http
      .get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
      .then((res) => {
        // 拿到對應index的主辦方詳情
        this.sponsor = res.data.data.sponsors[i];
        // 拿到全部的沙龍數據
        let salonsInfos = res.data.data.active
    
        // 經過數組遍歷拿到該主辦方詳情中的舉辦的沙龍列表
        // 若是主辦方舉辦的沙龍數量大於0,進行數組遍歷
        if(this.sponsor.salonNum>0){
          // 遍歷該主辦方的舉辦過的沙龍列表
          this.salonList = this.sponsor.salons.map(item=>{
            let sal
            // 遍歷拿到的全部沙龍數據
            salonsInfos.forEach(salon => {
             // 當沙龍的主題與主辦方舉辦的沙龍列表中的主題相同時,拿到對應的沙龍信息,並將沙龍信息替換進沙龍列表的title字段
              if(salon.title === item.title) sal = salon
            })
            return sal
          })
        }
        
         wx.hideLoading()
      });
    },
    複製代碼
    這樣,咱們就拿到了對應title的沙龍列表的詳細數據了,再將列表渲染到頁面便可。

6、個人頁面

這個gif截出來就成了這個鬼樣子,我也不知道爲何。。。大概在暗示我要換手機了吧T-T。

從gif圖中能夠看出:個人頁面由三部分組成,頭部展現用戶信息、客服和設置,tab欄選擇選項,tab選項對應的展現頁面。

用戶頭像和暱稱

展現用戶頭像和暱稱有兩種寫法:

  • 經過調用wx.getUserInfo()來獲取用戶頭像和暱稱,可是須要用戶受權,受權後才能成功獲取用戶頭像和暱稱。

  • 使用官方的<open-data>標籤直接顯示用戶頭像和暱稱(無需受權)

    <open-data type="userAvatarUrl"></open-data>    //獲取用戶頭像直接顯示在小程序中
    <open-data type="userNickName" ></open-data>    //獲取用戶暱稱直接顯示在小程序中
    複製代碼

    修改<open-data>標籤中的樣式時需注意,這裏不能像寫img/image的樣式同樣,直接給圖片一個width="50px";height="50px";border-radius="50%"的樣式,這樣並不會生效。圖片會以下圖同樣,沒有任何改變。

    那麼怎樣才能修改 <open-data>標籤的樣式呢?
    咱們能夠添加一個display:flex屬性使圖片大小可以改變,再添加一個overflow:hidden屬性使圓角可以出現。

    <open-data class="thumb" type="userAvatarUrl"></open-data>
    複製代碼
    .thumb
        width 100%
        height 100%
        border-radius 50%
        overflow hidden
        display flex
    複製代碼

    這樣,一個好看的頭像顯示就完成啦。

客服

由於這裏只是一個小demo項目,加上客服功能還須要配置開發者信息以及添加客服帳號,實在是麻煩,就懶得弄了。可是有個icon在這裏,總感受不能點擊會很難受,因而就添加了個點擊事件,當你好奇是啥的時候彈出一個暫不支持的提示框哈哈哈哈哈。別問、問就是懶,別說、說就是不必。
若是有想要了解一下客服功能怎麼實現的盆友能夠自行查看一些小程序官方文檔:客服消息使用指南客服帳號管理

設置

點擊小齒輪就會跳轉到一個設置的頁面,頁面分爲兩項,第一項是職人小程序的%¥%¥?咱也不知道咋描述,他說是關於職人就是關於職人吧。第二項是主辦方中心。這兩項點擊又是一個跳轉頁面,兩個頁面都沒啥操做,就是一個頁面展現。關於職人的頁面我就直接截了個圖上去,畢竟咱也沒有UI小姐姐,只能偷偷懶了,不會偷懶的程序猿不是好程序猿hhhh。

選項卡頁面切換(劃重點劃重點!)

當點擊任意tab時,樣式和展現的內容都會隨之變化。
嘶,這個樣式咋動態顯示啊?經過點擊事件給dom節點添加類名嗎?有點太麻煩了吧。添加一個數據進行判斷嗎?也好麻煩啊。
嘶,這個又要咋操做才能根據我點擊的的內容展現該展現的頁面啊?點擊一個tab就給一個數據,經過數據來判斷是否展現頁面嗎?這也太麻煩了。

這個看似簡單,但又有些許難度的小tab欄應該怎麼實現呢?

  • 首先,咱們在數據源裏定義一個navList,讓tab欄由數據渲染出來,這樣咱們能夠獲取它的index值,再經過index來賦值。

    data(){
        return{
            //給一個默認的changeNav值,
            changeNav:0,
            navList: [{name:'參加'},{name:'感興趣'},{name:'關注'}]
        }
    }
    複製代碼
  • 而後,咱們能夠經過html的data-屬性來自定義數據,並將自定義數據與點擊的index值進行綁定。

    <div class="tab-item" v-for="(item,index) in navList" :key="index" :data-current="index" @click="swichNav">
    複製代碼
  • 最後,經過定義的點擊事件來獲取自定義的數據

    methods:{
        swichNav(e){
          const current = e.currentTarget.dataset.current
          this.changeNav = current
        }
    }
    複製代碼
  • 而樣式問題咱們則經過判斷改變的changeNav值是否與index值相等來動態添加一個active的類名

    :class="{active:changeNav == index}"
    複製代碼
  • 頁面的轉換則經過v-if來判斷展現哪個頁面

    <Nothing v-if="changeNav==0" :tips="salonTip"></Nothing>
    <Interest v-if="changeNav==1" :salon="interestList" @goDetail="goSalonDetail"></Interest>
    <Collect v-if="changeNav==2" :sponsors="collectList" @go="goSponDetail"></Collect>
    複製代碼

這個小程序的報名要填信息啥啥的,以及畢竟是個活動,感受隨意報名有點不太好,因此就沒有寫報名的小分頁面,就直接展現一個沒有參加的沙龍頁面了。(還不是由於懶)

7、本地緩存實現主辦方關注功能

經過動圖咱們能夠看到,每一個列表都有一個關注的按鈕,點擊關注按鈕便可成功關注,點擊列表查看主辦方詳情頁也能看見關注成功的狀態,點擊已關注會彈出一個操做菜單,點擊取消關注才能取消成功。且兩個頁面的關注狀態都是共享的,無論在哪一個頁面操做關注與否,在另外一個頁面都會共享狀態。狀態共享的話,通常會想到vuex狀態管理或者本地緩存,這裏我選擇基於本地緩存實現列表關注功能,別問,問就是沒有建vuex。
那麼,如何經過本地緩存來實現一個經過數據渲染的列表,點擊任意列表,均可以使得對應的列表進行關注與否操做呢?

  • 第一步:給按鈕添加一個點擊事件,並阻止冒泡事件。
    首先,點擊按鈕進行關注與否操做後,緊接着會跳轉到詳情頁,是由於按鈕的父級元素中還有一個點擊跳轉至詳情頁的點擊事件,若是不作任何處理,目標元素的事件會冒泡到父級元素。因此咱們在寫按鈕的點擊事件時,需阻止冒泡事件。
    <button @click.stop="collect(index)" :class="collectList[index] ? 'like' : 'unlike'">{{collectList[index] ? '已關注' : '關注'}}</button>
    複製代碼
  • 第二步:在數據源定義一個默認數組collectList:[],在頁面加載時讀取以前本地緩存的狀態查看是否有關注,若是不存在本地緩存,就把默認數組collectList添加到本地緩存。若是存在本地緩存,就將本地緩存的數據賦值給collectList
    onShow(){
        var cache = wx.getStorageSync('collectList')
        if(!cache){
          wx.setStorage({
            key:"collectList",
            data:this.collectList
          })
        }else{
          this.collectList = cache
        }
      }
    複製代碼
  • 第三步:在methods中編寫點擊事件collect(){}
    methods:{
        collect(index){
          // 防止this指向改變
          let self = this
          // 拿到緩存區的collectList數組
          var cache = wx.getStorageSync('collectList')
          // 獲取當前主辦方是否被關注
          var currentCache = cache[index]
          // 若是沒有被關注
          if(!currentCache){
            // 將當前列表的關注狀態設置爲關注
            currentCache = true
            wx.showLoading({
              title: '加載中',
            })
            //將當前列表的關注狀態賦值給本地緩存
            cache[index] = currentCache
            //從新設置緩存
            wx.setStorage({
              key:'collectList',
              data:cache
            })
            // 將緩存賦值給數據源
            self.collectList = cache
            wx.hideLoading()
          }else{
            //若是存在緩存狀態,則調用操做菜單
            wx.showActionSheet({
              itemList: ['取消關注'],
              success (res) {
                wx.showLoading({
                  title: '加載中',
                })
                currentCache = false
                cache[index] = currentCache
                wx.setStorage({
                  key:'collectList',
                  data:cache
                })
                self.collectList = cache
                wx.hideLoading()
              }
            })
          }
    }
    複製代碼
    這樣,就實現了點擊哪個主辦方的關注按鈕,哪個主辦方便發生狀態改變的功能了,那麼接下來咱們則須要在詳情頁作相似的處理。
  • 第四步:在詳情頁的數據源定義一個isCollected:'',在頁面加載時讀取緩存狀態並賦值給數據源中的isCollected
    onShow(){
        // 拿到緩存區的關注信息
        var cache = wx.getStorageSync('collectList')
        //在頁面接收參數index時將option.index賦值給數據源中的index了,因此這裏直接調用了this.index
        this.isCollected = cache[this.index]
    },
    複製代碼
  • 第五步:給詳情頁的按鈕添加點擊事件
    <button @click="collect" :class="isCollected ? 'like' : 'unlike'">{{isCollected ? '已關注' : '關注'}}</button>
    複製代碼
    methods: {
        // 接下來的操做和上面的操做沒啥區別。。。我就不寫了。。。
        collect(){
          var cache = wx.getStorageSync('collectList')
          let self = this
          var currentCache = cache[self.index]
          if(!currentCache){
            wx.showLoading({
              title:'加載中'
            })
            currentCache = true
            cache[self.index] = currentCache
            wx.setStorage({
              key:'collectList',
              data:cache
            })
            self.isCollected = cache[self.index]
            wx.hideLoading()
          }else{
            wx.showActionSheet({
              itemList: ['取消關注'],
              success(res){
                wx.showLoading({
                  title:'加載中'
                })
                currentCache = false
                cache[self.index] = currentCache
                wx.setStorage({
                  key:'collectList',
                  data:cache
                })
                self.isCollected = cache[self.index]
                wx.hideLoading()
              }
            })
          }
        },
    }
    複製代碼

這樣,就實現了基於本地緩存實現列表關注功能惹,細心的盆友可能會發現,在判斷是否存在關注狀態時,if和else都有共同的操做,可是我沒有合在一塊兒寫,而是各自都寫了一遍。是由於,合在一塊兒寫時會產生異步,當else操做還沒執行完時,就執行接下來的賦值操做,致使操做不成功。解決異步能夠用Promise,可是promise也要重複兩次代碼,能解決問題爲啥不用最簡單直白的方式呢?因此懶人本懶直接在if判斷裏重複兩次代碼了。

8、感興趣/關注列表展現

因爲以前咱們利用本地緩存實現了將感興趣/關注的功能,那麼咱們如何拿到感興趣與關注的列表詳情數據呢?一樣的,咱們仍是能夠經過數組遍從來獲取到詳細數據。就拿主辦方關注來講,咱們能夠在數據加載時先拿到緩存區的主辦方收藏列表,經過數組遍歷拿到關注了的主辦方的下角標數組,再經過遍歷將對應下角標的主辦方關注詳情拿到。將數據渲染上去便可。

onShow(){
this.$get("https://www.easy-mock.com/mock/5d17149edc925c312db9c9ea/zhirent/zhirent")
      .then((res) => {
        // 全部的主辦方
        wx.showLoading()
        this.sponsors = res.data.data.sponsors;

        // 拿到緩存中的主辦方收藏列表
        var cache = wx.getStorageSync('collectList')
        // 拿到收藏的主辦方的下角標數組
        this.targetList = []
        cache.forEach((item,i) => {
          if(item) this.targetList.push(i)
        });

        // 拿到收藏的主辦方信息列表
        this.collectList = this.targetList.map(index => {
          let spon
          this.sponsors.forEach((item,i) => {
            if(index === i) spon = item
          })
          return spon
        })
        // console.log(this.collectList)
}
複製代碼

此外,點擊個人頁面上的任意關注/感興趣列表,均可以跳轉到對應的主辦方/沙龍詳情頁面,咱們只須要在編寫頁面跳轉至詳情頁時,將跳轉的index值修改一下便可。

goSponDetail(index){
  const url = `/pages/sponsorInfo/main?index=${this.targetList[index]}`
  wx.navigateTo({
    url
  })
}
複製代碼

9、沙龍詳情頁面的一些小功能

點擊感興趣同步用戶信息

和主辦方列表關注同樣,列表實現感興趣功能我就不詳細描述了。與主辦方頁面不一樣的是,頁面加載時須要判斷一下是否感興趣,若是感興趣的話,頁面加載時,我的信息就應該出如今頭像列表上。當點擊取消感興趣時,則刪除我的信息。因爲咱們寫了受權頁面,給小程序進行了受權操做,因此能夠直接調用wx.getUserInfo()來獲取用戶信息

onShow(){
    self = this
    // 獲取用戶信息
    wx.getUserInfo({
      success(res){
        var userInfo = res.userInfo
        var nickName = userInfo.nickName
        var avatarUrl = userInfo.avatarUrl
        // 將用戶信息賦值給數據源中的user
        self.user = {
          name:nickName,
          avatar:avatarUrl
        }
        var cache = wx.getStorageSync('interestList')
        self.isInterested = cache[self.index]
        if(self.isInterested){
          self.interestInfo.unshift(self.user)
        }
      }
    })
  },
複製代碼

點擊預覽圖片

這個調用小程序官方apiwx.previewImage就好啦。

自定義操做菜單組件(父子組件通訊)

因爲這個操做菜單並非緊挨着底部的,因此咱們須要本身定義一個share的組件,並在數據源給定一個isShare:false的狀態,經過狀態來決定share組件是否展現。首先,咱們點擊分享按鈕,將數據源中的狀態改變爲true,而後再在share組件中的取消按鈕定義一個點擊事件,並經過$emit將事件傳給父組件,從而實現父子組件間通訊問題。

  • 編寫share組件,並給對應的item添加對應的點擊事件。分享給好友或羣直接應用微信官方的button組件中open-type屬性,並將open-type的值設置爲share,便可實現分享給好友功能。保存沙龍海報會自動生成對應活動的二維碼,沒有上線就沒有二維碼。因此這個功能我就沒有去寫了。這裏有一點要提的是,小程序的button若是背景色爲白色,就會出現自帶的邊框,給button添加一個plain="true"屬性就行了。
    <div class="container">
        <div class="tips" >分享該沙龍</div>
        <button plain="true" open-type="share" class="type">分享給好友或羣</button>
        <button plain="true" class="type" @click="save">保存沙龍海報</button>
        <div class="cancel" @click="cancel">取消</div>
      </div>
    複製代碼
  • 給share組件的數據源裏添加一個share:false的數據
    data() {
        return {
          share:false
        }
      }
    複製代碼
  • 點擊取消按鈕,子組件經過$emit觸發事件來向父組件傳遞數據
    cancel(){
      this.$emit('cancel',this.share)
    }
    複製代碼
  • 在父組件中引入share組件,並在數據源中定義isShare:false,經過isShare來判斷是否展現操做菜單,並經過v-on(可簡寫爲@)綁定了一個cancel事件來監聽子組件的觸發事件,
    <Share v-if="isShare" @cancel="cancelShare"></Share>
    複製代碼
    <script>
    import Share from '@/components/share/share'
    export default{
        data(){
            return:{
                isShare:false,
            }
        },
        components:{
            Share
        },
        methods:{
            //接收子組件傳遞過來的數據:this.share(false),並將值賦值給isShare
            cancelShare(msg){
              this.isShare = msg
            }
        }
    }
    </script>
    複製代碼

就這樣,一個簡單的自定義操做菜單組件就完成了。既然說到了父子組件通訊問題,那我就再說一下vue中,父組件如何向子組件傳值,以及兄弟組件間通訊問題好了。

父組件向子組件傳值

因爲沙龍詳情頁面結構實在是太多模塊了,因此我每一個展現模塊都封裝成了組件,提升代碼可讀性。以及有兩個頁面都涉及到了點擊展開詳情,因而我也封裝成了一個組件,提升代碼複用性,這裏就以點擊展開詳情的組件來講一說父組件如何向子組件傳值的問題好了。

  • 自定義一個文本伸縮組件(?&%……¥我也不知道怎麼描述這個組件,就這麼叫好了。)
    <template>
      <div>
        <div class="info-desc" :class="isEllipsis ?'ellipsis':'unellipsis'">{{info}}</div>
        <div class="text-expander" @click="ellipsis" >
            <text class="text-expander__content">{{isEllipsis?'展開所有':'所有收起'}}</text>
            <image class="text-expander__icon" :src="isEllipsis?'/static/images/down.svg':'/static/images/up.svg'"></image>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          isEllipsis:true
        }
      },
      methods: {
        ellipsis(){
          this.isEllipsis =!this.isEllipsis
        }
      },
      props:[
        'info'
      ]
    }
    </script>
    
    <style lang="stylus" scoped>
    .info-desc
      font-size 14px
      display -webkit-box
      -webkit-box-orient vertical
      text-overflow ellipsis
      overflow hidden
      line-height 26px
      text-align left
    .ellipsis
      -webkit-line-clamp 3
    .unellipsis
      -webkit-line-clamp 0
    .text-expander
      margin 0 auto
      width 80px
      height 20px
      .text-expander__content
        color #c4c8c7
        font-size 16px
      .text-expander__icon
        width 15px
        height 15px
    </style>
    複製代碼

在不一樣頁面上,文本中展現的內容不同,因此咱們須要向父組件接收數據,父組件向子組件傳遞數據能夠經過props屬性來傳遞。子組件像父組件要info數據,經過props來索取。

  • 在父組件中引用子組件,並經過v-bind(能夠簡寫爲@)來綁定數據。
    <Expander :info="salonInfo"></Expander>
    複製代碼

就這樣,子組件就能夠拿到父組件中定義的salonInfo數據了。有興趣的盆友還能夠了解一下$on(子組件接收數據),以及非父子組件的兄弟組件間通訊方式(建一個Vue事件bus對象,而後經過bus.$emit觸發事件,bus.$on監聽觸發的事件)。

結語

結語也不知道說些啥,但別人也都有,我也不能輸!那仍是隨便嘮點吧QAQ。
原本是打算找個界面簡潔功能稍多的小項目來練手,結果最後仍是百分之八十是在切圖,不得不說,切圖真的很快樂,因此個人同桌老王常常調侃我:「純粹的前端」,固然這裏指的是純粹的切圖。但一隻醬毫不服輸,此次採用的是easy-mock,下次寫小項目就嘗試jsonp請求數據,或者使用萬能的爬蟲爬取數據,或者全棧開發一個小項目。寫項目也是一件很是快樂的一件事情,當你實現一個小功能時,或者本身想到 一個解決的辦法時,真的頗有成就感(雖然這個項目真的沒有什麼技術難點T-T)。最後奉上源碼,但願能幫助到你們一小丟丟。

相關文章
相關標籤/搜索