uniapp專案實戰 新聞類app
typora-copy-images-to: images
typora-root-url: ./
專案實戰 慕課新聞
阿里雲服務空間名:moocnews200909
第5章 揚帆起航,勝利在向你招手 (首頁功能模組)
5-1 專案初始化.mp4
初始化雲資料庫
使用方式
- 在
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個頁面檔案
配置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
首頁結果:
3部分:
- 導航欄
- 選項卡
- 卡片列表
製作搜尋框
1. 去除預設導航欄進行自定義
"style": {
"navigationStyle": "custom",/* 自定義導航欄 */
"navigationBarTextStyle": "white",/* 導航欄前景色 */
"navigationBarTitleText": "uni-app"/* 導航欄 文字資訊 */
}
2.通過自定義元件, 實現自定義導航欄
建立元件navbar.vue
頁面內引用註冊使用
頁面訪問方式:http://localhost:8080/#/pages/tabbar/index/index
easyCom引入元件的方法
當目錄和元件名一致的時候,不需要引入,可以直接使用元件(不需要import引入)
使用這個方法為區域性引入
頁面內輸入:
<!-- 自定義導航欄 -->
<navbar></navbar>
導航欄實現
新增樣式的公共變數
/* 顏色變數 */
@mk-base-color : #f07373;
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;
width: 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;
width: 100%;
height: 30px;
border-radius: 30px;
background-color: #fff;
.navbar-search_icon {
// width: 10px;
// height: 10px;
margin-right: 10px;
}
.navbar-search_text {
width: 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進行使用)
執行結果
5-3 導航欄適配小程式
H5中無狀態列,小程式中有狀態列
微信小程式測試執行時需要將埠開啟
錯誤提示,操作方式
1.報錯處理
1. 工具的服務埠已關閉。
要使用命令列呼叫工具,請在下方輸入 y 以確認開啟,或手動開啟工具 -> 設定 -> 安全設定,將服務埠開啟。
2. 解決微信開發者工具開啟微信小程式專案頁面顯示不出來
1.運用命令列安裝依賴 npm i
2.進入微信開發者工具點選工具選擇構建npm,重新整理頁面就行.
2.顯示問題:小程式中頂部狀態列遮擋,右側膠囊將搜尋框遮擋住
處理方式:
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',width: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",/* 導航欄前景色 */
執行結果
5-4 使用字型圖示.mp4
1.使用iconfont阿里圖示庫
1.iconfont中獲取字型圖示路徑
2.將對應的css檔案下載到common/css/icons.css檔案內
3.引入到css中
@import '../../common/css/icons.css';
4.頁面內使用
<text class="iconfont icon-search"></text>
2.外掛市場使用icon
1.外掛的安裝
網址:1. 地址
在components資料夾下顯示uni-icons資料夾
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 選項卡展示
1.建立tab元件
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>
樣式中同行顯示設定
.tab-scroll
最大寬度
.tab-scroll {
max-width: 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;
width: 100%;
border-bottom: 1px #f5f5f5 solid;
background-color: #fff;
box-sizing: border-box;
.tab-scroll {
flex: 1;
overflow: hidden;
box-sizing: border-box;
max-width: 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;
width: 45px;
&::after {
content: '';
position: absolute;
top: 12px;
bottom: 12px;
left: 0;
width: 1px;
background-color: #ddd;
}
}
}
</style>
5-6 選項卡資料初始化
所有的資料表文件 專案根目錄的資料夾內
1.建立雲函式get_label
2.建立對應雲資料庫資料表
匯入資料夾內的檔案即可
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(){}
})
}
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例項上*/
2.測試方法重寫
index.vue內呼叫 重寫
測試方法get_list
this.$api.get_list().then((res)=>{
console.log(res)
})
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
得到需要匯出的格式
最終結果
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
方法呼叫
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
})
},
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>
5-9 基礎卡片檢視實現
1.將頂部tab欄固定在頂部不隨著頁面滑動
處理方式: 將滾動的內容區域放置在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>
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>
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>
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>
可左右滑動
2.製作元件list-item
在list元件內新建list-item元件,將內容區域list-scroll
提出來進行巢狀
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>
結果:
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值為當前第幾項
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
}
},
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
};
將返回的結果過濾掉其中的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==='模板型別'
修改填充對應的欄位內容
注:多圖模式中需要增加判斷是否小於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;
width: 60px;
height: 60px;
border-radius: 5px;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.listcard-content {
display: flex;
flex-direction: column;
justify-content: space-between;
padding-left: 10px;
width: 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 {
width: 100%;
padding-left: 0;
}
.listcard-image {
display: flex;
margin-top: 10px;
width: 100%;
height: 70px;
.listcard-image__item {
margin-left: 10px;
width: 100%;
border-radius: 5px;
overflow: hidden;
&:first-child {
margin-left: 0;
}
image {
width: 100%;
height: 100%;
}
}
}
.listcard-content__des {
margin-top: 10px;
}
}
&.mode-image {
flex-direction: column;
.listcard-image {
width: 100%;
height: 100px;
}
.listcard-content {
padding-left: 0;
margin-top: 10px;
.listcard-content__des {
display: flex;
align-items: center;
margin-top: 10px;
}
}
}
}
</style>
5-14 切換選項卡懶載入資料
使用介面gitlist獲取真實資料並渲染頁面
1.確定不同內容顯示在頁面上的不同標籤(做分類)
在components/list/list.vue內獲取到對應分分類
在change事件內獲取到對應的下標值,根據下標值獲取對應的分類
change(e){
/* 獲得curent屬性*/
const {current} = e.detail
console.log(this.tab[current])
/* 將current值傳給tab頂部元件實現高亮切換聯動*/
this.$emit('change',current)
}
使用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載入更多