uniapp项目实战 新闻类app


typora-copy-images-to: images
typora-root-url: ./


项目实战 慕课新闻

阿里云服务空间名:moocnews200909

第5章 扬帆起航,胜利在向你招手 (首页功能模块)

5-1 项目初始化.mp4

初始化云数据库

使用db_init.json初始化项目数据库

使用方式

  • cloudfucntions目录右键即可创建db_init.json
  • 编写好json内容,在db_init.json上右键初始化数据库。

参考样式

{
    "collection_test": { // 集合(表名)
        "data": [ // 数据
           {
                "_id": "da51bd8c5e37ac14099ea43a2505a1a5",
               "name": "tom"
           }
        ],
        "index": [{ // 索引
            "IndexName": "index_a", // 索引名称
            "MgoKeySchema": { // 索引规则
                "MgoIndexKeys": [{
                    "Name": "index", // 索引字段
                    "Direction": "1" // 索引方向,1:ASC-升序,-1:DESC-降序,2dsphere:地理位置
                }],
                "MgoIsUnique": false // 索引是否唯一
            }
        }]
    }
}

制作tabbar

创建3个页面文件

image-20200923134500522

配置pages.json(底部的页签内容编辑器会自动提示作用)

{
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/tabbar/index/index",
			"style": {
				"navigationStyle": "custom",
				"navigationBarTextStyle": "white",
				"navigationBarTitleText": "uni-app"
			}
		}, {
			"path": "pages/tabbar/follow/follow",
			"style": {}
		}, {
			"path": "pages/tabbar/my/my",
			"style": {}
		}, {
			"path": "pages/home-search/home-search",
			"style": {
				"navigationStyle": "custom"
			}
		}, {
			"path": "pages/home-label/home-label",
			"style": {
				"navigationBarTitleText": "标签管理"
			}
		}, {
			"path": "pages/home-detail/home-detail",
			"style": {}
		}
	    ,{
            "path" : "pages/detail-comments/detail-comments",
            "style" : {}
        }
    ],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "uni-app",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
	"tabBar": {
		"color": "#666",
		"selectedColor": "#f07373",
		"backgroundColor": "#fff",
		"list": [{
			"pagePath": "pages/tabbar/index/index",
			"iconPath": "static/home.png",
			"selectedIconPath": "static/home-active.png",
			"text": "首页"
		}, {
			"pagePath": "pages/tabbar/follow/follow",
			"iconPath": "static/follow.png",
			"selectedIconPath": "static/follow-active.png",
			"text": "关注"
		}, {
			"pagePath": "pages/tabbar/my/my",
			"iconPath": "static/my.png",
			"selectedIconPath": "static/my-active.png",
			"text": "我的"
		}]
	}
}

5-2 自定义导航栏.mp4

首页结果:

image-20200909191242765

3部分:

  • 导航栏
  • 选项卡
  • 卡片列表

制作搜索框

1. 去除默认导航栏进行自定义

"style": {
    "navigationStyle": "custom",/* 自定义导航栏 */
    "navigationBarTextStyle": "white",/* 导航栏前景色 */
    "navigationBarTitleText": "uni-app"/* 导航栏 文字信息 */
}

image-20200909193626483

2.通过自定义组件, 实现自定义导航栏

​ 创建组件navbar.vue

​ 页面内引用注册使用

​ 页面访问方式:http://localhost:8080/#/pages/tabbar/index/index

easyCom引入组件的方法

当目录和组件名一致的时候,不需要引入,可以直接使用组件(不需要import引入)

​ 使用这个方法为局部引入

​ 页面内输入:

	<!-- 自定义导航栏 -->
	<navbar></navbar>

导航栏实现

添加样式的公共变量

/* 颜色变量 */
@mk-base-color : #f07373;

image-20200913172907442

navbar.vue

<template>
    <view class="navbar">
            <view class="navbar-fixed">
                <view class="navbar-content">
                    <view class="navbar-search">
                        <!-- 非搜索页显示 -->
                        <view class="navbar-search_icon">
                            <uni-icons type="search" size="16" color="#999"></uni-icons>
                        </view>
                        <view class="navbar-serach">
                            <!-- 搜索页显示  -->
                            <input class="navbar-search_text" type="text" v-model="val" placeholder="请输入您要搜索的内容"  />
                        </view>
                    </view>
                </view>
            </view>
    </view>
</template>

<script>
    export default {
        name: 'navbar',
        data() {
            return {
                val: ''
            };
        }
    }
</script>

<style lang="less">
    @import '../../common/css/icons.css';
    @import './../../uni.less';

    .navbar {
        .navbar-fixed {
            position: fixed;
            top: 0;
            left: 0;
            z-index: 99;
             100%;
            background-color: @mk-base-color;
            
            .navbar-content {
                display: flex;
                justify-content: center;
                align-items: center;
                padding: 0 15px;
                height: 45px;
                box-sizing: border-box;
                .navbar-search {
                    display: flex;
                    align-items: center;
                    padding: 0 10px;
                     100%;
                    height: 30px;
                    border-radius: 30px;
                    background-color: #fff;

                    .navbar-search_icon {
                        //  10px;
                        // height: 10px;
                        margin-right: 10px;
                    }

                    .navbar-search_text {
                         100%;
                        font-size: 14px;
                        color: #999;
                    }
                }

                &.search {
                    padding-left: 0;

                    .navbar-content__search-icons {
                        margin-left: 10px;
                        margin-right: 10px;
                    }

                    .navbar-search {
                        border-radius: 5px;
                    }
                }
            }

        }

    }
</style>

uni.scss 作为公共的样式色值在组件内可以直接使用(我改成less进行使用)

运行结果

image-20200913181533266

5-3 导航栏适配小程序

H5中无状态栏,小程序中有状态栏

微信小程序测试运行时需要将端口打开

错误提示,操作方式

1.报错处理

1. 工具的服务端口已关闭。

要使用命令行调用工具,请在下方输入 y 以确认开启,或手动打开工具 -> 设置 -> 安全设置,将服务端口开启。

image-20200913170158060

2. 解决微信开发者工具打开微信小程序项目页面显示不出来

1.运用命令行安装依赖 npm i

image-20200913182236974

2.进入微信开发者工具点击工具选择构建npm,刷新页面就行.

2.显示问题:小程序中顶部状态栏遮挡,右侧胶囊将搜索框遮挡住

image-20200913182731464

处理方式:

1.设定状态栏高度,导航栏高度,窗口宽度

        data() {
            return {
                statusBarHeight: 20,/* 状态栏高度 */
                navBarHeight: 45,/* 导航栏高度 */
                windowWidth: 375,/* 窗口宽度 */
                /* 设定状态栏默认高度 */
                val: ''/* 导航栏搜索框的值 */
            };
        },

2.分别根据设备调用api,计算对应的高度和位置

使用uniapp条件编译

#ifdef :      if defined  仅在某个平台编译
#ifndef :     if not defined  在除里该平台的其他编译
#endif :      end if 结束条件编译
%PLATFORM%     需要编译的平台,上面的MP就是各个小程序的意思
        created() {
            // 获取手机系统信息
            const info = uni.getSystemInfoSync()
            // 设置状态栏高度(H5顶部无状态栏小程序有状态栏需要撑起高度)
            this.statusBarHeight = info.statusBarHeight
            this.windowWidth = info.windowWidth
            // 除了h5 app mp-alipay的情况下执行
            // #ifndef H5 || APP-PLUS || MP-ALIPAY
            // 获取胶囊的位置
            const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
            console.log(menuButtonInfo);
            // (胶囊底部高度 - 状态栏的高度) + (胶囊顶部高度 - 状态栏内的高度) = 导航栏的高度
            this.navBarHeight = (menuButtonInfo.bottom - info.statusBarHeight) + (menuButtonInfo.top - info.statusBarHeight)
            this.windowWidth = menuButtonInfo.left
            // #endif
        }

3.赋值给对应的dom元素

<template>
    <view class="navbar">
        <view class="navbar-fixed">
            <!-- 状态栏小程序撑起高度 -->
            <view :style="{height:statusBarHeight+'px'}"></view>
            <view class="navbar-content" :style="{height:navBarHeight+'px',windowWidth+'px'}">
                <view class="navbar-search">
                    <view class="navbar-search_icon">
                        <uni-icons type="search" size="16" color="#999"></uni-icons>
                    </view>
                    <view class="navbar-serach">
                        <input class="navbar-search_text" type="text" v-model="val" placeholder="请输入您要搜索的内容" />
                    </view>
                </view>
            </view>
        </view>
    </view>
</template>

修改状态栏颜色从黑色改成白色:前景色 根目录:pages.json

"navigationBarTextStyle": "white",/* 导航栏前景色 */

运行结果

image-20200913183839600

5-4 使用字体图标.mp4

1.使用iconfont阿里图标库

1.iconfont中获取字体图标路径

2.将对应的css文件下载到common/css/icons.css文件内

image-20200909222200236

3.引入到css中

    @import '../../common/css/icons.css';

4.页面内使用

<text class="iconfont icon-search"></text>

2.插件市场使用icon

1.插件的安装

网址:1. 地址

image-20200909222836999

image-20200909222848598

image-20200909223043247

在components文件夹下显示uni-icons文件夹

image-20200913184924140

2.插件的使用

<view class="navbar-search_icon">
    <uni-icons type="search" size="16" color="#999"></uni-icons>
</view>

网址:https://ext.dcloud.net.cn/plugin?id=28

使用方式

script 中引用组件(现有项目无需格外引入只需要安装插件即可)

import uniIcons from "@/components/uni-icons/uni-icons.vue"
export default {
    components: {uniIcons}
}

template 中使用组件

<uni-icons type="contact" size="30"></uni-icons>
属性说明
属性名 类型 默认值 说明
size Number 24 图标大小
type String - 图标图案,参考示例
color String - 图标颜色
事件说明
事件名 说明 返回值
@click 点击 Icon 触发事件 -

5-5 选项卡展示

image-20200909224328319

1.创建tab组件

image-20200913184958065

2.页面横向滚动组件书写

<scroll-view class="tab-scroll" scroll-x="true" >
    <view class="tab-scroll__box">
        <view v-for="item in 10" class="tab-scroll__item">
            {{item}}内容
        </view>
    </view>
</scroll-view>

3.给顶部导航栏组件添加占位符撑起高度

注意:需要添加占位符高度 状态栏高度+导航栏高度(否则下面tab会塌陷)

navbar.vue文件

        <!-- 需要添加占位符高度  状态栏高度+导航栏高度(否则下面tab会塌陷)-->
        <view :style="{height: statusBarHeight+navBarHeight+'px'}"></view>

image-20200913185909641

image-20200913190020233

image-20200909225331410

样式中同行显示设定.tab-scroll最大宽度

        .tab-scroll {
            max- calc(100vw - 45px);
		}

tab.vue完整代码

<template>
    <view class="tab">
        <scroll-view class="tab-scroll" scroll-x>
            <view class="tab-scroll__box">
                <view v-for="item in 10" class="tab-scroll__item">
                    {{item}}内容
                </view>
            </view>
        </scroll-view>
        <view class="tab-icons" @click="open">
            <uni-icons type="gear" size="26" color="#666"></uni-icons>
        </view>
    </view>
</template>

<script>
    export default {
        data() {
            return {

            };
        }
    }
</script>

<style lang="less">
    @import '../../common/css/icons.css';
    @import './../../uni.less';

    .tab {
        display: flex;
         100%;
        border-bottom: 1px #f5f5f5 solid;
        background-color: #fff;
        box-sizing: border-box;

        .tab-scroll {
            flex: 1;
            overflow: hidden;
            box-sizing: border-box;
            max- calc(100vw - 45px);
            
            .tab-scroll__box {
                display: flex;
                align-items: center;
                flex-wrap: nowrap;
                height: 45px;
                box-sizing: border-box;

                .tab-scroll__item {
                    flex-shrink: 0;
                    padding: 0 10px;
                    color: #333;
                    font-size: 14px;

                    &.active {
                        color: @mk-base-color;
                    }
                }
            }

            .tab-scroll__box {
                display: flex;
                align-items: center;
                flex-wrap: nowrap;
                height: 45px;
                box-sizing: border-box;

                .tab-scroll__item {
                    flex-shrink: 0;
                    padding: 0 10px;
                    color: #333;
                    font-size: 14px;
                }
            }
        }

        .tab-icons {
            position: relative;
            display: flex;
            justify-content: center;
            align-items: center;
             45px;

            &::after {
                content: '';
                position: absolute;
                top: 12px;
                bottom: 12px;
                left: 0;
                 1px;
                background-color: #ddd;
            }
        }
    }
</style>

5-6 选项卡数据初始化

所有的数据表文件 项目根目录的文件夹内

image-20200913192141325

1.创建云函数get_label

2.创建对应云数据库数据表

image-20200909232043376

导入文件夹内的文件即可

3.云函数 get_label书写返回分类数据

'use strict';
// 获取数据库的引用
const db = uniCloud.database()
exports.main = async (event, context) => {
	// 获取 label 表的数据
	let label = await db.collection('label').get()

	//返回数据给客户端
	return {
		code: 200,
		msg: '数据请求成功',
		data: label.data
	}
};

4.页面内方法调用接口

网址:客户端调用云函数

getLabel() {
    // 客户端调用云函数方法
    const data = uniCloud.callFunction({
        name:"get_label",
        success(res){
            console.log(res)
        },
        fail(err){console.log(err)},
        complete(){}
    })
}

image-20200909233841106

5.组件传值

index.vue内

<tab :list="tabList"></tab>
	export default {
		data() {
			return {
				tablist:[]
			}
		},
		methods: {
			
		},
        onLoad() {
            console.log('dasasdasd')
             this.getLabel();
             console.log(this.tablist)
        },
        methods: {
            getLabel() {
                const _this = this;
                // 客户端调用云函数方法
                const data = uniCloud.callFunction({
                    name:"get_label",
                    success(res){
                        _this.tablist = res.result.data;
                    },
                    fail(err){console.log(err)},
                    complete(){}
                })
            }
        }
	}

补充:客户端调用云函数方法写法2

            getLabel() {
                const _this = this;
                // 客户端调用云函数方法
                const data = uniCloud.callFunction({
                    name:"get_label",
                }).then((res)=>{
                    console.log(res)
                    _this.tablist = res.result.data;
                }).catch((err)=>{
                    console.log(err)
                })
            }

组件内写法

        <scroll-view class="tab-scroll" scroll-x>
            <view class="tab-scroll__box">
                <view v-for="(item, index) in list" :key="index" class="tab-scroll__item" >
                    {{item.name}}
                </view>
            </view>
        </scroll-view>
props: {
    list: {
        type: Array
    }
},

5-7 封装数据请求

目的: 只关注成功的返回,失败的返回统一处理

在common - api - index.js

封装成的结果

this.$api.get_label().then((res) => {
})

步骤

|-common
|	|-api	api接口调用
|	|-css	公共样式
|-http.js	封装网络请求

1.promise方法封装uniCloud.callFunction云函数请求方法

common目录下的http.js

const get_label = (data)=> {	
    return new Promise((reslove, reject) => {
		uniCloud.callFunction({
			name: url,
			data: dataObj
		}).then((res) => {
			if (res.result.code === 200) {
				// .then
				reslove(res.result)
			} else {
				// catch
				reject(res.result)
			}
		}).catch((err) => {
			reject(err)
		})
	})
}
export default {
    get_label
}

在api目录下创建list.js(测试方法可行性)

/* 所有的云函数接口列表写在这里 */
export const get_list = (data)=>{
    return new Promise((reslove, reject) => {
        reslove({'data':'请求成功'})
	})
}

绑定到根目录的main.js上

import api from './common/api'
Vue.prototype.$api = api /*绑定到vue实例上*/

image-20200915113049447

2.测试方法重写

index.vue内调用 重写

测试方法get_list

                this.$api.get_list().then((res)=>{
                    console.log(res)
                })

image-20200915102629432

this.$api.get_label({
    name:"get_label"
}).then((res) => {
    const {
        data
    } = res
    console.log('标签 ',data);
    this.tabList = data
    // 	console.log(this.tabList);
})

3.创建http.js 作为方法调用(创建一个http的接口方法)

目的:为了将return 内的 Promise 提出作为公共部分

export default function $http(options){
    const {url,data} = options;
    return new Promise((reslove, reject) => {
		uniCloud.callFunction({
			name: url,/* 云函数名称 */
			data: data/* 传递的数据 */
		}).then((res) => {

			if (res.result.code === 200) {
				// .then
				reslove(res.result)
			} else {
				// catch
				reject(res.result)
			}

		}).catch((err) => {
			reject(err)
		})
	})
}

4.修改原先的list.js

方法: 引入http.js 将原先的返回值方法进行改造

import $http from './../http.js'
export const get_label = (data)=> {	
    return $http({
        url:'get_label',
        data
    })
}

/* 所有的云函数接口列表写在这里 */
export const get_list = (data)=>{
    return new Promise((reslove, reject) => {
        reslove({'data':'请求成功'})
	})
}

5.统一封装云函数请求,在list内统一调用

之后如果有多个云函数只需要重复写多个方法函数即可

import $http from './../http.js'
export const get_label = (data)=> {	
    return $http({
        url:'get_label',
        data
    })
}


6.由于有多个云函数,需要批量的导出和引用,需要改写index.js文件

原先只能导出一个云函数(现在需要无论名称是什么都可以全部导入导出)

//原先的===============
// import {get_label,get_list} from './list.js'

// export default{
//     get_label,
//     get_list
// }
//修改后的================
// 批量导出文件
const requireApi = require.context(
	// api 目录的相对路径
	'.',
	// 是否查询子目录
	false,
	// 查询文件的一个后缀
	/.js$/
)

let module = {}
requireApi.keys().forEach((key,index)=>{
    //因为index为输出的目录,所以需要排除掉
	if(key === './index.js') return
	console.log(key);
    //对象合并
	Object.assign(module,requireApi(key))
})
console.log(module)
export default module

得到需要导出的格式

image-20200915111347650

最终结果

7.在api内创建list.js进行调用

http.js

export default function $http(options) {
	const {
		url,
		data
	} = options
	const dataObj = {
		user_id: '5e76254858d922004d6c9cdc',
		...data
	}
	return new Promise((reslove, reject) => {
		uniCloud.callFunction({
			name: url,
			data: dataObj
		}).then((res) => {

			if (res.result.code === 200) {
				// .then
				reslove(res.result)
			} else {
				// catch
				reject(res.result)
			}

		}).catch((err) => {
			reject(err)
		})
	})
}

list.js

import $http from '../http.js'

export const get_label = (data) => {
	return $http({
		url: 'get_label',
		data
	})
}

export const get_list = (data) => {
	return $http({
		url: 'get_list',
		data
	})
}

export const update_like = (data) => {
	return $http({
		url: 'update_like',
		data
	})
}
export const get_search = (data) => {
	return $http({
		url: 'get_search',
		data
	})
}

export const update_label = (data) => {
	return $http({
		url: 'update_label',
		data
	})
}

export const get_detail = (data) => {
	return $http({
		url: "get_detail",
		data
	})
}

export const update_comment = (data) => {
	return $http({
		url: "update_comment",
		data
	})
}
export const get_comments = (data) => {
	return $http({
		url: 'get_comments',
		data
	})
}

export const update_author = (data) =>{
	return $http({
		url: 'update_author',
		data
	})
}

export const update_thumbsup =  (data) =>{
	return $http({
		url: 'update_thumbsup',
		data
	}) 
}

index.js

// 批量导出文件
const requireApi = require.context(
	// api 目录的相对路径
	'.',
	// 是否查询子目录
	false,
	// 查询文件的一个后缀
	/.js$/
)

let module = {}
requireApi.keys().forEach((key,index)=>{
	if(key === './index.js') return
	console.log(key);
	Object.assign(module,requireApi(key))
})

export default module

image-20200911135131780

方法调用

				this.$api.get_label({
                        name:"get_label"
                    }).then((res) => {
					const {
						data
					} = res
					console.log('标签 ',data);
					data.unshift({
						name:'全部'
					})
					this.tabList = data
					// 	console.log(this.tabList);
				})

5-8 选项卡切换高亮

​ 步骤:

1.绑定点击事件注册

tab.vue

<scroll-view class="tab-scroll" scroll-x>
    <view class="tab-scroll__box">
        <view v-for="(item, index) in list" :key="index" class="tab-scroll__item" 
              @click="clickTab(item, index)">{{item.name}}</view>
    </view>
</scroll-view>

事件绑定:

clickTab(item, index) {
    console.log(item,index);
    this.activeIndex = index
    this.$emit('tab', {
        data: item,
        index: index
    })
},

image-20200911135631985

2.获得点击对象的name值和索引值

通过动态绑定类,进行申明

<view class="tab-scroll__box">
<view v-for="(item, index) in list" :key="index" class="tab-scroll__item" :class="{active:activeIndex === index}"
@click="clickTab(item, index)">{{item.name}}</view>
</view>
.tab-scroll__item {
    flex-shrink: 0;
    padding: 0 10px;
    color: #333;
    font-size: 14px;

    &.active {
        color: $mk-base-color;
    }
}

3.将事件传递给父级页面

			clickTab(item, index) {
				console.log(item,index);
				this.activeIndex = index
				this.$emit('tab', {
					data: item,/* 点击的内容 */
					index: index/* 点击的索引 */
				})
			},

4.在首页的tab组件内接受事件

@tab="tab"

<tab :list="tabList" :tabIndex="tabIndex"  @tab="tab"></tab>

注册事件:

		tab({data,index}){
			console.log(data,index);
			this.activeIndex = index
		},

list-item组件

<template>
	<list-scroll class="list-scroll" @loadmore="loadmore">
		<list-card mode="base" :item="item" v-for="item in list" :key="item._id"></list-card>
		<uni-load-more v-if="list.length === 0 || list.length > 7" iconType="snow" :status="load.loading"></uni-load-more>
	</list-scroll>
</template>

<script>
	export default {
		props: {
			list: {
				type: Array,
				default () {
					return []
				}
			},
			load: {
				type: Object,
				default () {
					return {
						loading: "loading"
					}
				}
			}
		},
		methods: {
			loadmore() {
				this.$emit('loadmore')
			}
		}
	}
</script>

<style>
	.list-scroll {
		height: 100%;
	}
</style>

image-20200915131640760

5-9 基础卡片视图实现

1.将顶部tab栏固定在顶部不随着页面滑动

image-20200916145248814

处理方式: 将滚动的内容区域放置在scroll-view标签内

	<view class="home">
		<navbar></navbar>
        <tab :list="tabList" @tab="tab"></tab>
        <view class="scroll">
            <scroll-view scroll-y="true" class="list-scroll" >
                <view>
                    <view v-for="item in 100">
                        {{item}}的内容
                    </view>
                </view>
            </scroll-view>
        </view>

	</view>

通过flex竖向布局实现剩余内容高度撑满屏幕

	page {
		height: 100%;
		display: flex;
	}
	.home  {
		display: flex;
		flex-direction: column;
		flex: 1;
		overflow: hidden;
            
        .scroll{
            flex: 1;
            overflow: hidden;
            box-sizing: border-box;
            .list-scroll{
                height: 100%;
                display: flex;
                flex-direction: column;
            }
        }
		.home-list {
			flex:1;
			box-sizing: border-box;
		}
	}

2.将内容放置在list-scroll组件内

​ 创建list-scroll组件

​ 作用:作为滚动区域的组件

<template>
	<view class="scroll">
		<scroll-view scroll-y="true" class="list-scroll" >
		    <view>
                <slot></slot>
		    </view>
		</scroll-view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				
			}
		},
		methods: {
			
		}
	}
</script>

<style  lang="less">
	.scroll {
		flex: 1;
		overflow: hidden;
		box-sizing: border-box;
		.list-scroll {
			height: 100%;
			display: flex;
			flex-direction: column;
		}
	}
</style>

3.组件list-card循环列表创建

<template>
	<view class="home">
		<navbar></navbar>
        <tab :list="tabList" @tab="tab"></tab>
        <list-scroll>
            <list-card v-for="(item,i) in 10"></list-card>
        </list-scroll>
	</view>
</template>

4.构建卡片组件list-card基础卡片

基础组件构建

<template>
	<view>
        <!-- 基础卡片-->
		<view class="listcard">
            <view class="listcard-image">
                <!-- aspectFill代表完全填充图片内容-->
                <image src="../../static/logo.png" mode="aspectFill"></image>
            </view>
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>文章标题</text>
                </view>
                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">文章描述内容</view>
                    </view>
                    <view class="listcard-content__des-browse">浏览量浏览</view>
                </view>
            </view>
        </view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
			};
		}
	}
</script>

image-20200916154005722

5-10 更多卡片视图实现

1.list-card添加多图模式和大图模式

<template>
	<view>
        <!-- 基础卡片-->
		<view class="listcard">
            <view class="listcard-image">
                <!-- aspectFill代表完全填充图片内容-->
                <image src="../../static/logo.png" mode="aspectFill"></image>
            </view>
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题文章标题</text>
                </view>
                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">文章描述内容</view>
                    </view>
                    <view class="listcard-content__des-browse">浏览量浏览</view>
                </view>
            </view>
        </view>
        		<!-- 多图模式 -->
        		<view  class="listcard mode-column">
        			<view class="listcard-content">
        				<view class="listcard-content__title">
        					<text>文章标题</text>
        				</view>
        				<view class="listcard-image">
                            <view v-for="item in 3" :key="index" class="listcard-image__item">
                                <image src="../../static/logo.png" mode="aspectFill"></image>
                            </view>
        					
        				</view>
        				<view class="listcard-content__des">
        					<view class="listcard-content__des-label">
        						<view class="listcard-content__des-label-item">文章描述内容</view>
        					</view>
        					<view class="listcard-content__des-browse">浏览</view>
        				</view>
        			</view>
        		</view>
        		<!-- 大图模式 -->
        		<view  class="listcard mode-image">
        			<view class="listcard-image">
        				<image src="../../static/logo.png" mode="aspectFill"></image>
        			</view>
        			<view class="listcard-content">
        				<view class="listcard-content__title">
        					<text>文章标题</text>
        				</view>
        
        				<view class="listcard-content__des">
        					<view class="listcard-content__des-label">
        						<view class="listcard-content__des-label-item">文章描述内容</view>
        					</view>
        					<view class="listcard-content__des-browse">浏览</view>
        				</view>
        			</view>
        		</view>
	</view>
</template>

image-20200916155917801

2.list-card添加props渲染不同视图

添加父传子props接受的参数,

基础卡片:v-if="item.mode === 'base'"

多图模式:v-if="item.mode === 'column'"

大图模式:v-if="item.mode === 'image'"

默认为mode=base

<template>
    <view>
        <!-- 基础卡片-->
        <view v-if="mode==='base'" class="listcard">
            <view class="listcard-image">
                <!-- aspectFill代表完全填充图片内容-->
                <image src="../../static/logo.png" mode="aspectFill"></image>
            </view>
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>文章标题文章</text>
                </view>
                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">文章描述内容</view>
                    </view>
                    <view class="listcard-content__des-browse">浏览量浏览</view>
                </view>
            </view>
        </view>
        <!-- 多图模式 -->
        <view v-if="mode==='column'" class="listcard mode-column">
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>文章标题</text>
                </view>
                <view class="listcard-image">
                    <view v-for="item in 3" :key="index" class="listcard-image__item">
                        <image src="../../static/logo.png" mode="aspectFill"></image>
                    </view>

                </view>
                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">文章描述内容</view>
                    </view>
                    <view class="listcard-content__des-browse">浏览</view>
                </view>
            </view>
        </view>
        <!-- 大图模式 -->
        <view v-if="mode==='image'" class="listcard mode-image">
            <view class="listcard-image">
                <image src="../../static/logo.png" mode="aspectFill"></image>
            </view>
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>文章标题</text>
                </view>

                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">文章描述内容</view>
                    </view>
                    <view class="listcard-content__des-browse">浏览</view>
                </view>
            </view>
        </view>
    </view>
</template>

<script>
    export default {
        props: {
            mode: {
                type: String,
                default: 'base'
            }
        },
        data() {
            return {};
        }
    }
</script>

index.vue调用对应组件内容

<template>
	<view class="home">
		<navbar></navbar>
        <tab :list="tabList" @tab="tab"></tab>
        <list-scroll>
            <list-card mode="base"></list-card>
            <list-card mode="column"></list-card>
            <list-card mode="image"></list-card>
        </list-scroll>
	</view>
</template>

image-20200916161452780

5-11 实现内容切换

1.list组件制作

作用:用来放置swiper左右滑动列表

list.vue

<template>
		<swiper class="home-swiper">
            <swiper-item class="swiper-item">
                <list-scroll class='list-scroll'>
                    <list-card mode="base"></list-card>
                    <list-card mode="column"></list-card>
                    <list-card mode="image"></list-card>
                </list-scroll>
            </swiper-item>
            <swiper-item class="swiper-item">
                <list-scroll class='list-scroll'>
                    <list-card mode="base"></list-card>
                    <list-card mode="column"></list-card>
                    <list-card mode="image"></list-card>
                </list-scroll>
            </swiper-item>
        </swiper>
</template>

index.vue将原先内容注释

	<view class="home">
		<navbar></navbar>
        <tab :list="tabList" @tab="tab"></tab>
<!--       <list-scroll>
            <list-card mode="base"></list-card>
            <list-card mode="column"></list-card>
            <list-card mode="image"></list-card>
        </list-scroll> -->
       <view class="home-list">
            <list></list>
        </view>
	</view>

image-20200917155543838

可左右滑动

2.制作组件list-item

在list组件内新建list-item组件,将内容区域list-scroll提出来进行嵌套

image-20200917160743033

list-item.vue

<template>
    <list-scroll class='list-scroll'>
        <list-card mode="base"></list-card>
        <list-card mode="column"></list-card>
        <list-card mode="image"></list-card>
    </list-scroll>
</template>
<script>
</script>
<style lang="less">
    .list-scroll{
        height:100%
    }
</style>

由于list-item不符合对应规范需要单独引入

list.vue

<template>
		<swiper class="home-swiper">
            <swiper-item class="swiper-item">
                <list-item></list-item>
            </swiper-item>
            <swiper-item class="swiper-item">
                <list-item></list-item>
            </swiper-item>
            <swiper-item class="swiper-item">
                <list-item></list-item>
            </swiper-item>
        </swiper>
</template>
<script>
    /* 因为不符合规范需要手动注册引入*/
    import listitem from './list-item.vue'
	export default {
        components:{'list-item':listitem}
	}
</script>

结果:

image-20200917160907498

3.在list传入tab实现高亮

先从index.vue内传入tablist然后循环列表tab

index.vue

   <view class="home-list">
        <list :tab = "tabList"></list>
    </view>

list.vue

<template>
		<swiper class="home-swiper">
            <swiper-item v-for="(item,index) in tab" :key="index" class="swiper-item">
                <list-item></list-item>
            </swiper-item>
        </swiper>
</template>
<script>
    /* 因为不符合规范需要手动注册引入*/
    import listitem from './list-item.vue'
	export default {
        components:{'list-item':listitem},
        props:{
            tab:Array,
            default(){
                return [];
            }
        }
	}
</script>

结果:实现列表的个数与选项卡一一对应

5-12 选项卡与内容联动

1.swiper监听左右滚动 change事件监听滑动

<template>
		<swiper class="home-swiper" @change="change">
            <swiper-item v-for="(item,index) in tab" :key="index" class="swiper-item">
                <list-item></list-item>
            </swiper-item>
        </swiper>
</template>
<script>
    /* 因为不符合规范需要手动注册引入*/
    import listitem from './list-item.vue'
	export default {
        components:{'list-item':listitem},
        props:{
            tab:Array,
            default(){
                return [];
            }
        },
        methods:{
            change(e){
                console.log(e)
            }
        }
	}
</script>

方法会返回的current值为当前第几项

image-20200917161821604

activeIndex可以控制当前返回第几项

方法:将current值传给activeIndex 实现高亮显示切换联动

list.vue

            change(e){
                /* 获得curent属性*/
                const {current} = e.detail
                console.log(e)
                /* 将current值传给tab顶部组件实现高亮切换联动*/
                this.$emit('change',current)
            }

index.vue

2.添加change事件进行监听变化,传值给tab组件实现高亮切换

<list :tab = "tabList" @change="change"></list>
            /* 接收子组件的事件*/
            change(current){
                /* 当前current的值*/
                   console.log(current)
                   /* 再将值传给table顶部的tab组件*/
                   this.tabIndex =current;
            },

tab组件引用处接受传递的参数

<tab :list="tabList" @tab="tab" :tabIndex="tabIndex"></tab>

3.通过watch监听props中传值的变化实现切换效果

tab.vue组件

原先tab组件通过activeIndex 控制高亮显示

通过props接受tabIndex的值

        props: {
            list: {
                type: Array
            },
            /* 切换的高亮显示项*/
            tabIndex:{
                  type:Number,
                  default:0
            }
        },

watch监听props变化

        watch:{
            tabIndex(newVal,oldVal){
                this.activeIndex = newVal
            }
        },

image-20200917163843181

4.当点击tab的时候底部的列表也切换

给tab点击后this.$emit传值给index,再从index传值给list

tab之前就写了tab$emit方法tab

tab.vue

            clickTab(item,index) {
                this.activeIndex = index;
                this.$emit('tab',{
                    data:item,/* 传递的数据 */
                    index:index/* 传递的索引值 */
                })
            }

index.vue

在index.vue中先申明变量data activeIndex:0

tab内接收的参数传给activeIndex

            /* 接收组件传递值*/
            tab({data,index}){
              console.log(data)   
              console.log(index)   
              this.activeIndex =index;
            }

props组件传值activeIndex

 <list :tab = "tabList" :activeIndex="activeIndex" @change="change"></list>

swiper的current属性为跳转到第几项

list.vue

proprs接收参数

            /* 切换的高亮显示项*/
            activeIndex:{
                  type:Number,
                  default:0
            }

参数赋值给swiper组件:current="activeIndex"

	<swiper class="home-swiper" :current="activeIndex"  @change="change">
        <swiper-item v-for="(item,index) in tab" :key="index" class="swiper-item">
            <list-item></list-item>
        </swiper-item>
    </swiper>

5-13 内容卡片数据初始化

创建云函数git_list获取列表信息

git_list

'use strict';
// 获取数据库的引用
const db = uniCloud.database()
exports.main = async (event, context) => {
    const list = await db.collection('aritcle').get()
    // 返回数据给客户端
    return list
};

image-20200921162641760

将返回的结果过滤掉其中的content,使用方法(field)

最后优化结果

'use strict';
// 获取数据库的引用
const db = uniCloud.database()
exports.main = async (event, context) => {
    const list = await db.collection('article')
    .field({
        /* true表示只返回这个字段,false表示不返回*/
        content:false
    })
    .get()
    // 返回数据给客户端
    return{
        code:200,
        msg:"数据请求成功",
        data:list.data
    }
};

在接口的common/api/list 增加返回

/* 所有的云函数接口列表写在这里 */
export const get_list = (data)=>{

    return $http({
        url:'get_list',
        data
    })
}

在components/list/list.vue调用对应云函数获取数据

            /* 获得对应的信息,需要将分类参数传递给云函数*/
            getList(name){
                this.$api.getList().then((res)=>{
                    console.log(res);
                    const {data} = res;
                    this.list = data;
                })
            }

调用list

组件内不能用onload只能用created

        created(){
            /* 初始化调用*/
            this.getList()
        },

完整的list.vue

<template>
		<swiper class="home-swiper" :current="activeIndex"  @change="change">
            <swiper-item v-for="(item,index) in tab" :key="index" class="swiper-item">
                <list-item :list="list"></list-item>
            </swiper-item>
        </swiper>
</template>
<script>
    /* 因为不符合规范需要手动注册引入*/
    import listitem from './list-item.vue'
	export default {
        components:{'list-item':listitem},
        data:function(){
            return {
                list:[]
            }
        },
        props:{
            tab:Array,
                
            default(){
                return [];
            },
            /* 切换的高亮显示项*/
            activeIndex:{
                  type:Number,
                  default:0
            }
        },
        /* onload在页面上,created组件*/
            
        created(){
            /* 初始化调用*/
            this.getList()
        },
        methods:{
            change(e){
                /* 获得curent属性*/
                const {current} = e.detail
                /* 获得对应的分类名称*/
                // console.log(this.tab[current].name)
                /* 每次变更将获取到的值传递给getList实现数据变更*/
                // this.getList(this.tab[current].name)
                /* 将current值传给tab顶部组件实现高亮切换联动*/
                this.$emit('change',current)

            },
            /* 获得对应的信息,需要将分类参数传递给云函数*/
            getList(){
                this.$api.get_list().then((res)=>{
                    console.log(res);
                    const {data} = res;
                    this.list = data;
                })
            }
        }
	}
</script>

<style lang="less">
.home-swiper{
    height: 100%;
    .swiper-item{
        height:100%;
        overflow:hidden;

    }
}
</style>

在list下的list-item.vue内,

先修改list-item.vue 实现内容循环遍历,并传值:item=item给list-card

<template>
    <list-scroll class='list-scroll'>
        <list-card mode="base" :item=item v-for="item in list" :key="item._id">
        </list-card>
    </list-scroll>
</template>

<script>
    export default{
        props:{
            list:{
                type:Array,
                default(){
                    return []
                }
            }
        }
    }
</script>

<style lang="less">
    
    .list-scroll{
        height:100%
    }
</style>

修改对应的list-card内容显示不同的内容信息接受item的内容

在props内接受对象item,默认返回空对象

        props: {
            item:{
                type:Object,
                default(){
                    return {}
                }
            }
        },

原先的mode可以清楚,在item内包含mode

修改上方模板的判断为item.mode==='模板类型'

image-20200921170633793

修改填充对应的字段内容

注:多图模式中需要增加判断是否小于3才渲染v-if="index < 3"

list-card.vue完整代码

<template>
    <view>
        <!-- 基础卡片-->
        <view v-if="item.mode==='base'" class="listcard">
            <view class="listcard-image">
                <!-- aspectFill代表完全填充图片内容-->
                <image :src="item.cover[0]" mode="aspectFill"></image>
            </view>
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>{{item.title}}</text>
                </view>
                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">{{item.classify}}</view>
                    </view>
                    <view class="listcard-content__des-browse">{{item.browse_count}}浏览</view>
                </view>
            </view>
        </view>
        <!-- 多图模式 -->
        <view v-if="item.mode==='column'" class="listcard mode-column">
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>{{item.title}}</text>
                </view>
                <view class="listcard-image">
                    <view v-if="index < 3" v-for="(item,index) in item.cover" :key="index" class="listcard-image__item">
                        <image :src="item" mode="aspectFill"></image>
                    </view>

                </view>
                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">{{item.classify}}</view>
                    </view>
                    <view class="listcard-content__des-browse">{{item.browse_count}}浏览</view>
                </view>
            </view>
        </view>
        <!-- 大图模式 -->
        <view v-if="item.mode==='image'" class="listcard mode-image">
            <view class="listcard-image">
                <image :src="item.cover[0]" mode="aspectFill"></image>
            </view>
            <view class="listcard-content">
                <view class="listcard-content__title">
                    <text>{{item.title}}</text>
                </view>

                <view class="listcard-content__des">
                    <view class="listcard-content__des-label">
                        <view class="listcard-content__des-label-item">{{item.classify}}</view>
                    </view>
                    <view class="listcard-content__des-browse">{{item.browse_count}}浏览</view>
                </view>
            </view>
        </view>
    </view>
</template>

<script>
    export default {
        props: {
            mode: {
                type: String,
                default: 'base'
            },
            item:{
                type:Object,
                default(){
                    return {}
                }
            }
        },
        data() {
            return {};
        }
    }
</script>

<style lang="less">
    @import './../../uni.less';

    .listcard {
        display: flex;
        padding: 10px;
        margin: 10px;
        border-radius: 5px;
        box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.1);
        box-sizing: border-box;

        .listcard-image {
            flex-shrink: 0;
             60px;
            height: 60px;
            border-radius: 5px;
            overflow: hidden;

            image {
                 100%;
                height: 100%;
            }
        }

        .listcard-content {
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            padding-left: 10px;
             100%;

            .listcard-content__title {
                position: relative;
                padding-right: 30px;
                font-size: 14px;
                color: #333;
                font-weight: 400;
                line-height: 1.2;

                text {
                    overflow: hidden;
                    text-overflow: ellipsis;
                    display: -webkit-box;
                    -webkit-line-clamp: 2;
                    -webkit-box-orient: vertical;
                }

            }

            .listcard-content__des {
                display: flex;
                justify-content: space-between;
                font-size: 12px;

                .listcard-content__des-label {
                    display: flex;

                    .listcard-content__des-label-item {
                        padding: 0 5px;
                        margin-right: 5px;
                        border-radius: 15px;
                        color: @mk-base-color;
                        border: 1px @mk-base-color solid;
                    }
                }

                .listcard-content__des-browse {
                    color: #999;
                    line-height: 1.5;
                }
            }
        }

        &.mode-column {
            .listcard-content {
                 100%;
                padding-left: 0;
            }

            .listcard-image {
                display: flex;
                margin-top: 10px;
                 100%;
                height: 70px;

                .listcard-image__item {
                    margin-left: 10px;
                     100%;
                    border-radius: 5px;
                    overflow: hidden;

                    &:first-child {
                        margin-left: 0;
                    }

                    image {
                         100%;
                        height: 100%;
                    }
                }
            }

            .listcard-content__des {
                margin-top: 10px;
            }
        }

        &.mode-image {
            flex-direction: column;

            .listcard-image {
                 100%;
                height: 100px;
            }

            .listcard-content {
                padding-left: 0;
                margin-top: 10px;

                .listcard-content__des {
                    display: flex;
                    align-items: center;
                    margin-top: 10px;
                }
            }
        }
    }
</style>

image-20200921171928087

5-14 切换选项卡懒加载数据

使用接口gitlist获取真实数据并渲染页面

1.确定不同内容显示在页面上的不同标签(做分类)

image-20200921155924341

在components/list/list.vue内获取到对应分分类

在change事件内获取到对应的下标值,根据下标值获取对应的分类

            change(e){
                /* 获得curent属性*/
                const {current} = e.detail
                console.log(this.tab[current])
                /* 将current值传给tab顶部组件实现高亮切换联动*/
                this.$emit('change',current)
            }

image-20200921172130264

使用this.tab[current].name可以获得到对应的name值,

2.将对应的名称name值传递给云函数获得对应的分类信息

​ list内创建方法getList并传递参数name

            /* 获得对应的信息,需要将分类参数传递给云函数*/
            getList(name){
                this.$api.getList({name}).then((res)=>{
                    console.log(res);
                    const {data} = res;
                    this.list = data;
                })
            }

在页面初始化加载的时候调用created中调用,和在onchange事件切换的时候调用

        created(){
            /* 初始化调用*/
            this.getList('后端开发')
        },
            change(e){
                /* 获得curent属性*/
                const {current} = e.detail
                /* 获得对应的分类名称*/
                console.log(this.tab[current].name)
                /* 每次变更将获取到的值传递给getList实现数据变更*/
                this.getList(this.tab[current].name)
                /* 将current值传给tab顶部组件实现高亮切换联动*/
                this.$emit('change',current)

            },

云函数获取event获取数据并进行筛选聚合操作

使用聚合操作进行筛选数据.

云函数 get_list/index.js

'use strict';
// 获取数据库的引用
const db = uniCloud.database()
exports.main = async (event, context) => {
    /* 接收分类去筛选数据*/
    const {name} = event;
    let matchObj = {}
	if (name !== '全部') {
		matchObj = {
			classify: name/* 筛选字段将符合条件的文档传递给下一个流水线 */
		}
	}
    // 聚合 : 更精细化的去处理数据 求和 、分组、指定那些字段
    const list = await db.collection('article')
    .aggregate()/* 获得聚合操作的集合 */
    .match(matchObj)/* 筛选字段将符合条件的文档传递给下一个流水线 */
    .project({
        /* false表示不返回*/
        content:false
    })
    .end()/* 发起实际聚合操作 */
    // 返回数据给客户端
    return{
        code:200,
        msg:"数据请求成功",
        data:list.data
    }
};

由于渲染新的内容会闪烁显示,增加缓存功能

在getList内添加缓存的下标current

添加data值listCatchData = {};

        data:function(){
            return {
                list:[],
                /* 处理切换的时候内容闪烁的状态(设定缓存状态)*/
                listCatchData = {};
            }
        },
            /* 获得对应的信息,需要将分类参数传递给云函数*/
            getList(current){
                this.$api.get_list({
                    name:this.tab[current].name
                }).then((res)=>{
                    console.log(res);
                    const {data} = res;
                    this.list = data;
                    /* 赋值current进行缓存*/
                    this.listCatchData[current] = data
                })
            }

初始化的调用填入为

需要注意当内容没有渲染的时候getList在created内执行会报错

            /* 初始化调用*/
            this.getList(this.activeIndex);

修改为将created中的方法在watch中进行调用

        /* 当tab发生变化的时候才进行渲染*/
        watch:{
            tab(newVal) {
                if (newVal.length === 0) return;
                  this.listCatchData = {};
                  this.getList(this.activeIndex);
            },
        },
        /* onload在页面上,created组件*/
            
        created(){
            /* 初始化调用*/
            /* tab是没有赋值*/
            
        },

修改模板渲染的部分

<list-item :list="listCatchData[index]"></list-item>

数据请求成功,页面没显示,需要将渲染更新重新处理赋值

使用this.$set来重新渲染页面

当数据发生变化的时候使用

                  // 懒加载 当数据变更的时候才会渲染数据
                  this.$set(this.listCatchData, current, data);

完整的getList方法

            /* 获得对应的信息,需要将分类参数传递给云函数*/
            getList(current){
                this.$api.get_list({
                    name:this.tab[current].name
                }).then((res)=>{
                    console.log(res);
                    const {data} = res;
                    console.log('请求数据',data);
                    // this.list = data;
                    /* 赋值current进行缓存*/
                    // this.listCatchData[current] = data
                  // 懒加载 当数据变更的时候才会渲染数据
                  this.$set(this.listCatchData, current, data);
                })
            }

list.vue完整代码

<template>
		<swiper class="home-swiper" :current="activeIndex"  @change="change">
            <swiper-item v-for="(item,index) in tab" :key="index" class="swiper-item">
                <list-item :list="listCatchData[index]"></list-item>
            </swiper-item>
        </swiper>
</template>
<script>
    /* 因为不符合规范需要手动注册引入*/
    import listitem from './list-item.vue'
	export default {
        components:{'list-item':listitem},
        data:function(){
            return {
                list:[],
                // 处理切换的时候内容闪烁的状态(设定缓存状态)
              // js 的限制 listCatchData[index] = data 
              listCatchData: {},
            }
        },
        props:{
            tab:Array,
                
            default(){
                return [];
            },
            /* 切换的高亮显示项*/
            activeIndex:{
                  type:Number,
                  default:0
            }
        },
        /* 当tab发生变化的时候才进行渲染*/
        watch:{
            tab(newVal) {
                if (newVal.length === 0) return;
                  this.listCatchData = {};
                  this.getList(this.activeIndex);
            },
        },
        /* onload在页面上,created组件*/
            
        created(){
            /* 初始化调用*/
            /* tab是没有赋值*/
            
        },
        methods:{
            change(e){
                /* 获得curent属性*/
                const {current} = e.detail
                /* 获得对应的分类名称*/
                console.log(this.tab[current].name)
                /* 每次变更将获取到的值传递给getList实现数据变更*/
                this.getList(this.tab[current].name)
                /* 将current值传给tab顶部组件实现高亮切换联动*/
                this.$emit('change',current)

            },
            /* 获得对应的信息,需要将分类参数传递给云函数*/
            getList(current){
                this.$api.get_list({
                    name:this.tab[current].name
                }).then((res)=>{
                    console.log(res);
                    const {data} = res;
                    console.log('请求数据',data);
                    // this.list = data;
                    /* 赋值current进行缓存*/
                    // this.listCatchData[current] = data
                  // 懒加载 当数据变更的时候才会渲染数据
                  this.$set(this.listCatchData, current, data);
                })
            }
        }
	}
</script>

<style lang="less">
.home-swiper{
    height: 100%;
    .swiper-item{
        height:100%;
        overflow:hidden;

    }
}
</style>

5-15 -1 上拉加载更多(上)

https://ext.dcloud.net.cn/plugin?id=29

使用组件 LoadMore加载更多

image-20200911151509228

image-20200911151540882

5-16 -2 上拉加载更多(下)

5-17 -1 收藏按钮实现(上 )

5-18 -2 收藏按钮实现(下)

第6章 做事是否快捷,不在一时奋发,而在能否持久(搜索页功能模块)

6-1 搜索页导航栏修改 (2).mp4

6-2 使用vuex 管理历史记录 (2).mp4

6-3 -1 搜索逻辑实现(上 ) (2).mp4

6-4 -2 搜索逻辑实现(下) (2).mp4

6-5 搜索历史数据持久化 (2).mp4

第7章 锲而不舍,金石可镂(标签页功能模块)

7-1 标签管理页布局样式.mp4

7-2 标签页数据处理.mp4

7-3 编辑标签页.mp4

7-4 保存标签页数据.mp4

7-5 使用自定义事件同步数据.mp4

第8章 坚持就是胜利,坚持才会有所成就(详情页功能模块)

8-1 详情页页面展示.mp4

8-10 评论内容实现 (5).mp4

8-11 评论内容实现(6).mp4

8-12 关注作者(上).mp4

8-13 关注作者(下).mp4

8-14 文章的收藏与点赞(上).mp4

8-15 文章的收藏和点赞(下).mp4

8-16 评论列表(上).mp4

8-17 评论列表(下).mp4

8-2 内容预加载.mp4

8-3 详情页面数据初始化.mp4

8-4 富文本渲染.mp4

8-5 发布窗口展示.mp4

8-6 评论内容实现(1).mp4

8-7 评论内容实现(2).mp4

8-8 评论内容实现(3).mp4

8-9 评论内容实现(4).mp4

第9章 关注页功能模块

9-1 关注页导航栏实现).mp4

9-2 收藏文章内容实现.mp4

9-3 收藏与首页内容关联.mp4

9-4 关注作者页面实现.mp4

9-5 同步关注作者页面数据.mp4

第10章 个人信息页功能模块

10-1 个人中心页面实现.mp4

10-2 个人中心数据处理).mp4

10-3 我的文章实现).mp4

10-4 问题反馈页面实现).mp4

10-5 反馈图片选择.mp4

10-6 上传图片.mp4

第11章 积少成多,走向完善(项目优化与平台兼容)

11-1 微信小程序优化与兼容.mp4

11-2 支付宝小程序优化与兼容.mp4

11-3 其他平台优化与兼容.mp4

第12章 最后的冲刺,成功就在眼前(项目发行与打包)

12-1 h5端发行打包.mp4

12-2 小程序端发行上传.mp4

12-3 App 端发行打包.mp4

原文地址:https://www.cnblogs.com/NB-JDzhou/p/13717899.html