没朋友(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)。最后奉上源码,但愿能帮助到你们一小丢丢。

相关文章
相关标签/搜索