uni-app 快速入门 从零开始实现新闻资讯类跨端应用(更新中)
文章目录
一、微信小程序基础
1.初始化文件目录结构
1、utils文件夹主要存放工具类
pages目录存放所有页面代码
app.js是文件入口
app.jasn是项目配置
app.wxss是全局样式表
2、index目录
index.js当前页面的业务逻辑
index.json当前页面的配置文件
index.wxml当前页面模板
index.wxss当前页面的样式
2.数据绑定,条件判断、列表渲染
index.js
//必须使用Page构造器来构造页面
Page({
/**
* 页面的初始数据
*/
data: {
name:'hzy',
is_ok:false,
list:['1','2','3','4']
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
//改变数据的方法this.setData(obj)
//this是指当前对象的实例,指向Page
setTimeout(()=>{this.setData({name:'wwj',is_ok:true})},2000)
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
index.wxml
<!--pages/index/index.wxml-->
<text wx:if="{{is_ok}}">{{name}}</text>
<!--text 是行级元素,相当于span-->
<!--wx:if 控制标签是否渲染-->
<view wx:for="{{list}}">{{item}}</view>
<!--view 是块级元素,相当于div-->
<!--wx:for 控制标签循环渲染-->
<!--item 是指当前变量默认值
index 是指当前下标默认值-->
<!--
<view wx:for="{{list}}" wx:for-item="list">{{list}}</view>
等价于
<view wx:for="{{list}}">{{item}}</view>
-->
二、uni-app基础
1.uni-app核心知识点概况
1.1 uni-app规范
①页面文件遵循Vue单文件组件(SFC)规范
②组件标签接近小程序规范
③接口能力(JS API)靠近小程序规范
④数据绑定及事件处理同Vue.js规范
⑤为兼容多端运行,建议使用flex布局进行开发
1.2 uni-app特色
①条件编译
②App端的Nvue开发法
③HTML5+
1.2 uni-app特色
2.uni-app环境搭建
①HBuilder直接创建
②vue-cli命令行创建
1、npm install -g @vue/cli 安装vue-cli
2、 vue -V 查看vue-cli版本,是否安装成功
3、 vue create -p dcloudio/uni-preset-vue 名称 安装uni-app项目
3.uni-app模板语法和数据绑定
<template>
<view v-bind:class="className" v-on:click="open">
{{title}}
</view>
<!-- v-bind:表示class的值为动态 -->
<!-- 与小程序不同的是这里的v-bind:class,直接赋值,而不用{{}} -->
<!-- :是v-bind:简写 -->
<!-- @是v-on:简写 -->
</template>
<script>
export default {
//初始化数据
data() {
return {
title: '两秒之前',
className:'bul'
}
},
onLoad() {
},
//自定义的事件方法写到methods里面
methods: {
//两秒之后修改title和className
open(){
setTimeout(()=>{
this.title="两秒之后"
this.className="gre"
//this指向view实例,赋值方便,不用setstate和setData
},2000)
}
}
}
</script>
<style>
.bul {
color: #007AFF;
}
.gre{
color: #4CD964;
}
</style>
4.uni-app条件判断
<template>
<view>
<view>这是一直显示的内容</view>
<view>
<view v-if="show==='vue'">{{show}}</view>
<view v-else-if="show==='react'">{{show}}</view>
<view v-else>uni-app</view>
</view>
<!-- v-if: 指令的表达式返回true才会渲染 -->
<button @click="isShow()">点击</button>
</view>
</template>
<script>
export default {
data() {
return {
show: 'vue'
}
},
onLoad() {},
methods: {
isShow() {
this.show = this.show === 'vue' ? 'react' : 'vue'
}
}
}
</script>
<style>
</style>
5.uni-app列表渲染
<template>
<view>
<!-- 列表渲染,通过数组来渲染列表 -->
<view v-for="(item,index) in arr">{{index+1+' '+item}}</view>
<!-- item:被迭代的数组元素的别名 -->
<!-- index:被迭代的数组元素的下标 -->
<view v-for="(value,key) in obj">{{key+' : '+value}}</view>
<!-- 同时也可以遍历对象 可以获取value和key, 顺序不要弄错-->
</view>
</template>
<script>
export default {
data() {
return {
arr:['uni-app','vue','react'],
obj:{
name:'hzy',
age:18,
type:'man',
}
}
},
onLoad() {},
methods: {
}
}
</script>
<style>
</style>
6.uni-app基础组件使用
<template>
<!-- uni-app基础组件使用,view也是基础组件,相当于html的div标签 -->
<view>
<view>view标签:</view>
<view hover-class="redd">vue</view>
<!-- hover-class指定点击下去的样式类 -->
<view>react</view>
<view>text标签:</view>
<text>vue</text>
<text>react</text>
<!-- text标签相当于html的span,行内元素-->
<scroll-view scroll-y="true" class="height" @scroll="scroll">
<view v-for="item in 20">{{item}}</view>
</scroll-view>
<!-- 可滚动视图区域,必须设置高度 -->
<!-- @scroll触发滚动时候的响应事件 -->
<button type="default" @click="input">点击</button>
<!-- <input type="text" v-bind:value="value"> -->
<input type="text" v-model="value">
<view>{{value}}</view>
<!-- value前面得加上v-bind:表示他是个动态绑定的值,或者使用v-model,指的是双向绑定 -->
</view>
</template>
<script>
export default {
data() {
return {
value: 'vue'
}
},
onLoad() {},
methods: {
scroll(e) {
console.log(e)
},
input() {
this.value = this.value === 'vue' ? 'react' : 'vue'
}
}
}
</script>
<style>
.redd {
border: 1px red solid;
}
.height {
height: 50px;
border: 1px red solid;
}
</style>
7.uni-app自定义组件
index.vue 主页面
<template>
<view>
<hzyBtn color="blue" @change="change"></hzyBtn>
<!-- 让组件去接收一个名为change的事件 -->
</view>
</template>
<script>
//在script标签下引用自定义组件,@表示的事根目录
import but from '@/components/hzyBtn/hzyBtn.vue'
export default {
data() {
return {
}
},
onLoad() {},
methods: {
change(e) {
console.log('页面的事件被触发,组件返回了:' + e)
}
}
}
</script>
<style>
</style>
hzyBtn.vue 自定义按钮组件
<template>
<view class="btn-box" :style="{color:color}" @click="butClick">
点击
</view>
</template>
<script>
export default {
props: {
color: {
type: String,
default: '#000'
},
// color接受一个类型为String的对象,默认值为'#000'
},
// props接收属性
data() {
return {};
},
methods: {
butClick() {
console.log('组件内事件触发')
this.$emit('change', this.color)
//触发页面传来的‘change’事件
}
}
}
</script>
<style>
.btn-box {
width: 200px;
height: 100px;
text-align: center;
line-height: 100px;
border: 1px red solid;
}
</style>
页面
点击之后控制台打印
8.uni-app基础api使用
<template>
<view></view>
</template>
<script>
export default {
data() {
return {}
},
onLoad() {
uni.getSystemInfo({
success(res) {
console.log('成功的回调', res)
},
fail(err) {
console.log('失败的回调', err)
},
complete(res) {
console.log('不管成功失败都会返回')
}
})
//uni.getSystemInfo(obj)获取系统信息的api,异步的
},
// onLoad()只加载一次,监听页面加载,其参数为上个页面传递的数据,参数类型为Object
methods: {}
}
</script>
<style>
</style>
9.uni-app条件编译
<template>
<view>
<!-- 条件编译写在对应的注释里 -->
<!-- 表示在h5和app端才进行这段代码的编译 -->
<!-- #ifdef H5 || APP-PLUS-->
<view>h5和app平台才显示</view>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view>微信小程序不显示,其他都显示</view>
<!-- #endif -->
</view>
</template>
<script>
export default {
data() {
return {}
},
onLoad() {
},
methods: {
// #ifdef
// #endif
}
}
</script>
<style>
/* #ifdef */
/* #endif */
</style>
10.uni-app页面布局
<template>
<view class="content color">uni-app</view>
</template>
<script>
export default {
data() {
return {}
},
onLoad() {
},
methods: {}
}
</script>
<style>
@import 'index.css';
/* 引入外部css方式 */
/* page{}等价于body{} */
page {
background-color: #007AFF;
}
/* 尺寸单位*/
.content {
/* px rpx rem vh vx*/
font-size: 50px;
}
</style>
10.uni-app生命周期
①应用生命周期(定义在app.vue页面中)
<script>
// 生命周期分为三种:
// 应用生命周期:定义在app.vue页面中
// 页面生命周期
// 组件生命周期
export default {
//应用初始化完成触发一次,全局只触发一次
//可以做一些登录的事情,或者拿一些全局变量
onLaunch: function() {
console.log('App Launch')
},
//应用启动的时候,或者从后台进入前台会触发
onShow: function() {
console.log('App Show')
},
//应用从前台进入后台触发
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>
②页面生命周期
<template>
<view>
<view>生命周期</view>
<hzyText></hzyText>
</view>
</template>
<script>
import hzyText from '@/components/hzyText/hzyText.vue'
export default {
data() {
return {}
},
//监听页面加载(页面加载之后执行,再次加载时候不执行,浏览器有缓存)
onLoad() {
console.log('page onLoad')
},
//监听页面初次渲染完成
onReady() {
//如果渲染速度快,会在页面进入动画完成前触发
console.log('page onReady')
},
//监听页面显示,页面每次出现在屏幕上都会触发
onShow(){
console.log('page onShow')
},
//监听页面隐藏
onHide(){
console.log('page onHide')
},
//监听页面卸载
onUnload(){
console.log('page onUnload')
},
methods: {}
}
</script>
<style>
</style>
③组件生命周期
<template>
<view>
hzyText组件
</view>
</template>
<script>
export default {
data() {
return {
};
},
//在实例初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用
beforeCreate() {
console.log('beforeCreate')
},
//实例创建完成之后立即调用,挂载阶段还没开始
created() {
console.log('created')
},
//挂载到实例上去之后调用
mounted() {
console.log('mounted')
},
//Vue实例销毁后调用
destroyed() {
console.log('destroyed')
}
}
</script>
<style>
</style>
三、项目基础配置
1.uni-app项目配置
①微信小程序,微信开发者工具要在设置的安全中打开服务端口,Hx才能启用
②安卓真机,手机连接电脑后,点击关于手机,点击五次版本号,进入开发者模式,开启usb调试,打开传输文件
③H5
2.uni-app目录结构了解
目录结构
1、componens - 自定义组件目录
2、pages - 页面存放目录 (里面页面的目录和文件需同名 例如:logon中的logon.vue)
3、static - 静态文件资源目录(图片和字体可以放在该目录)
4、unpackage - 编译后的文件存放目录
5、utils - 自定义存放工具类的目录
6、common - 自定义存放公用东西的目录
7、App.vue - 页面入口(可以做用户登录、项目初始话、全局样式更改等)
8、main.js - 应用入口
9、manifest.json - 项目配置
10、pages.json - 页面配置
11、readme.md - 项目基本情况
12、uni.scss - uni-app内置的常用样式变量
3.配置项目底部选项卡
pages.json
{
//"pages":注册页面文件,页面窗口表现形式
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app",
//app平台下特有窗口表现样式
"app-plus": {
},
//微信小程序平台下特有窗口表现样式
"mp-weixin": {
},
//h5平台下特有窗口表现样式
"h5": {
}
}
}
,{
"path" : "pages/about/about",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/my/my",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
],
//"globalStyle"所有页面的默认配置,当"pages"下配置了和"globalStyle"一样的属性,就会将 "globalStyle"中的属性覆盖
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
//"tabBar"指得是tab栏的表现
"tabBar":{
"color":"#666",
"selectedColor":"#ff5a5f",//选中时候的颜色
"borderStyle":"black",
//tabBar关联页面
"list":[
{
"pagePath":"pages/index/index",
"text":"首页",
"iconPath":"static/home.png",//本地图片,大小40kb,尺寸81*81px
"selectedIconPath":"static/home-active.png"//选中后图片
},
{
"pagePath":"pages/about/about",
"text":"关于",
"iconPath":"static/follow.png",//本地图片,大小40kb,尺寸81*81px
"selectedIconPath":"static/follow-active.png"//选中后图片
},
{
"pagePath":"pages/my/my",
"text":"我的",
"iconPath":"static/my.png",//本地图片,大小40kb,尺寸81*81px
"selectedIconPath":"static/my-active.png"//选中后图片
}
]
}
}
总结:页面有个可以写onTabItemTap(e) { },这个生命周期函数是点击tebbar时候触发
4.在uni-app中使用sass
1、安装scss插件
<template>
<view class="content box">
<image class="logo" src="/static/logo.png"></image>
<view class="text-area">
<text class="title">{{title}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
//tabbar点击触发的钩子
onTabItemTap(e) {
console.log(e)
},
methods: {
}
}
</script>
<style lang="scss">
// scss可以写样式可以一层套一层
$width : 200rpx; //变量定义用$
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
//&表示父级 box和content是同级的样式
&.box{
border: 1px red solid;
}
.logo {
height: $width;
width: $width;
margin-top: $width;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
.title {
font-size: 36rpx;
color: #8f8f94;
}
}
}
</style>
注意:style标签中得写lang="scss"来引用scss
四、uniCloud (云开发平台)的基础用法
1.认识 uniCloud开发
①uniCloud是基于serverless模式和js编程的云开发平台
②用javascript开发前后台真题业务,开发成本大幅下降,只需专注业务
③非H5,免域名使用服务器
④uniCloud开发流程
⑤uniCloud构成
云函数:
云数据库
云储存和CDN3
2.HBuilderX 中配置 uniCloud 环境
第一步:确保uni-app应用标识处于获取状态
第二步:右键cloudfunctions创建云服务空间,回来后再次右键选择关联云服务空间
!!! 注意:创建云函数后要点击右键部署到云开发平台
3. 使用 uniCloud web 控制台
方式:右键cloudfunctions就可以打开uniCloud web控制台
注意:①在云数据库中,所有字段和值都要用双引号。②localhost在uni-app中已经自动解决了跨域问题,如果要通过本机的ip访问云服务器就得在云控制台中进行跨域配置
③项目可以直接使用云存储的图片,视频的链接,也可以直接上传文件到云存储
4. 开始使用云函数
云函数 get_list/index.js
'use strict';
exports.main = async (event, context) => {
//event为客户端上传的参数
//context 包含了调用信息和运行状态,每次调用的上下文
console.log('event : ', event)
//返回数据给客户端
return {
"content":"成功",
"event":event
}
};
页面index.vue
uniCloud.callFunction({
//调用云函数
name: "get_list",
//传的参数
data: {
"name":"hzy"
},
//成功回调
success(res) {
console.log('云函数调用成功',res.result);
},
//失败回调
fail(res) {
console.log('云函数调用失败',res);
}
})
点击调用之后
5.云数据库的添加和删除
'use strict';
const db = uniCloud.database(); //1、获取数据库的引用
// 获取 `user` 集合的引用
exports.main = async (event, context) => {
const collection = db.collection('user'); //2、获取集合的引用
//给集合添加一条记录,就是添加一个json对象
//添加await 之后数据才能同步
let res = await collection.add(
[
{
"name": "angular",
"age": 60
},
{
"name": "gofa",
"age": 100
}
]
)
//删除云数据库中的记录,要传入ID,先用doc()找到,再用remove()删除
let res = await collection.doc('5fd8e308fb0f850001897146').remove()
return {}
};
6.数据库的更新和查找
①更新数据
'use strict';
const db = uniCloud.database(); //1、获取数据库的引用
// 获取 `user` 集合的引用
exports.main = async (event, context) => {
const collection = db.collection('user'); //2、获取集合的引用
//更新数据库记录,先用doc()找到,再用update()更新,也可以用set()来更新
//可以增加字段,减少字段,修改字段
//update() 只能更新存在的记录
//set 如果记录存在就更新,如果不存在就添加
const res = await collection.doc('5fd8e308fb0f850001897146').update(
name:"likeReact"
)
return {}
};
②查找数据
'use strict';
const db = uniCloud.database(); //1、获取数据库的引用
// 获取 `user` 集合的引用
exports.main = async (event, context) => {
const collection = db.collection('user'); //2、获取集合的引用
//查询,先通过doc()找到这条记录,再用get()把记录拿到
//doc()只能应用id,不能应用其他字段
const docRes = await collection.doc("5fd8e308fb0f850001897146").get()
//where(),可以通过字段名称来找到记录,注意,需要传对象
const whereRes = await collection.where({
name:"vue"
}).get()
console.log(JSON.stringify(whereRes))
return {}
};
7.使用云储存上传文件
//选择图片的api
uni.chooseImage({
count: 1, //只允许上传一张图片
success(res) {
const tempFilePath = res.tempFilePaths[0] //获取第一张图片的地址
//上传到云储存中
console.log(tempFilePath)
uniCloud.uploadFile({
filePath: tempFilePath,
cloudPath:"123456.jpg",//名称必须和上传的图片名称一致
success(res) {
console.log('success', res)
//返回的filePath和fileID都可以直接拿来使用
},
fail() {
console.log('fail', res)
}
})
},
fail(res) {
console.log(res)
}
})
五、首页功能模块
1.项目初始化
1、创建db_init_json,初始化云数据库
2、配置tabbar
创建tabbar目录,将首页,关注,我的,三个页面放到tabbar文件目录下
在pages.json文件中配置tabbar
"tabBar": {
"color": "#666",
"selectedColor": "#f07373",
"backgroundColor": "#fff",
"list": [{
"pagePath": "pages/tabbar/index/index",
"iconPath": "static/home.png",
"selectedIconPath": "static/home.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": "我的"
}]
}
2. 自定义导航栏
navbar.vue
<template>
<view class="navbar">
<view class="navbar-fixed">
<view class="navbar-serach">
<view class="navbar-serach_icon"></view>
<view class="navbar-serach_text">请输入...</view>
</view>
</view>
<view style="height: 45px;"></view>
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss">
.navbar{
.navbar-fixed{
position: fixed;//固定在顶部
top:0px;
left: 0px;
z-index: 99;
display: flex;
justify-content: center;//水平居中
align-items: center;
width: 100%;
height: 45px;//常见导航栏都是45px
background-color: $mk-base-color;
padding: 0 15px;
box-sizing: border-box;//只在盒内显示
.navbar-serach{
display: flex;//flex布局,横排
align-items: center;//垂直居中
height: 30px;
border-radius: 30px;//和高度一样就能实现圆角
width: 100%;
padding: 0 10px;
background-color: #fff;
.navbar-serach_icon{
width: 10px;
height: 10px;
border: 1px red solid;
margin-right: 10px;
}
.navbar-serach_text{
font-size: 12px;
color: #999;
}
}
}
}
</style>
注意:①
一些常用的样式变量可以写到uni.scss中,方便以后直接通过名字引用
②uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用,而且还是局部引用,非常点赞
3.导航栏适配小程序
在小程序中是有一个状态栏的,所以做完H5之后还要添加一个状态栏,在navbar中做一下适配,在不同设配上小程序状态栏的高度不一样,导航栏的高度就等于胶囊的buttom减去状态栏高度在加top减去状态栏高度
<template>
<view class="navbar">
<view class="navbar-fixed">
<!-- 状态栏 -->
<view :style="{height:statusBarHeight+'px'}"></view>
<!-- 导航栏内容 -->
<view class="navbar-conten" :style="{height:navBarHeight+'px',width:navBarWidth+'px'}">
<view class="navbar-serach">
<view class="navbar-serach_icon">
<uni-icons type="search" size="16px" color="#999"></uni-icons>
<!-- <text class="iconfont iconicon-test"></text> -->
</view>
<view class="navbar-serach_text">请输入...</view>
</view>
</view>
</view>
<view :style="{height:navBarHeight+statusBarHeight+'px'}"></view>
</view>
</template>
<script>
export default {
data() {
return {
statusBarHeight: 20, //状态栏高度
navBarHeight: 45, //导航栏高度
navBarWidth: 375, //内容宽度
};
},
//组件加载的时候执行
created() {
//同步获取手机系统信息
const info = uni.getSystemInfoSync()
//设置状态栏高度(H5的状态栏高度是0)
this.statusBarHeight = info.statusBarHeight
this.navBarWidth = info.windowWidth
// #ifndef H5 || APP-PLUS || MP-ALIPAY
//H5 app mp-alipay 不支持uni.getMenuButtonBoundingClientRect()这个api
//获取胶囊的位置信息
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
//(胶囊底部高度-状态栏高度)+(胶囊顶部高度-状态栏高度)=导航栏高度
this.navBarHeight = (menuButtonInfo.bottom - info.statusBarHeight) + (menuButtonInfo.top - info.statusBarHeight) + 4
//内容宽度
this.navBarWidth = menuButtonInfo.left
// #endif
}
}
</script>
<style lang="scss">
@import "../../common/icons/css/icons.css";
.navbar {
.navbar-fixed {
position: fixed; //固定在顶部
top: 0px;
left: 0px;
z-index: 99;
width: 100%;
background-color: $mk-base-color;
.navbar-conten {
display: flex;
justify-content: center; //水平居中
align-items: center;
padding: 0 15px;
box-sizing: border-box; //只在盒内显示
.navbar-serach {
display: flex; //flex布局,横排
align-items: center; //垂直居中
height: 30px;
border-radius: 30px; //和高度一样就能实现圆角
width: 100%;
padding: 0 10px;
background-color: #fff;
.navbar-serach_icon {
margin-right: 10px;
}
.navbar-serach_text {
font-size: 12px;
color: #999;
}
}
}
}
}
</style>
微信状态栏的前景色是黑色的,如果要改成白色,则需要在pages.json中配置
4 .使用字体图标
①复制阿里图标库的icon代码
直接在text中通过类名引用
<text class="iconfont iconicon-test"></text>
②在插件市场安装icons图标插件
直接通过组件去引用
<uni-icons type="search" size="16px" color="#999"></uni-icons>
5.选项卡展示
<template>
<view class="tab">
<scroll-view class="tab-scroll" scroll-x>
<view class="tab-scroll_box">
<view v-for="item in list" :key="index" class="tab-scroll_item">
{{item.name}}
</view>
</view>
</scroll-view>
<view class="tab-icons">
<uni-icons type="gear" size="26" color="#666"></uni-icons>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: [{
name: 'uni-app'
}, {
name: 'uni-app'
}, {
name: 'uni-app'
}, {
name: 'uni-app'
}, {
name: 'uni-app'
}, {
name: 'uni-app'
}, {
name: 'uni-app'
}]
};
}
}
</script>
<style lang="scss">
.tab {
display: flex;
width: 100%;
border-bottom: 1px #f5f5f5 solid;
background-color: #fff;
box-sizing: border-box; //盒内元素
.tab-scroll_box {
display: flex;
flex-wrap: nowrap; //不换行
align-items: center; //垂直居中
box-sizing: border-box; //盒内元素
height: 45px;
.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{
// &::after指的是该元素后面的内容
content: '';
position: absolute;
top:12px;
bottom:12px;
left: 0;
width: 1px;
background-color: #ddd;
}
}
}
</style>
6.选项卡数据初始化
云函数get_label
'use strict';
const db = uniCloud.database()//创建数据库的引用
exports.main = async (event, context) => {
let label = await db.collection('label').get()//获取label表数据
// await 意思是等一下,等着db.collection('label').get()这个函数执行完毕,得到值后再赋值给label
//返回数据给客户端
return {
code:200,
msg:'数据请求成功',
data:label.data
}
};
首页index.vue
<template>
<view class="content">
<navbar></navbar>
<tab :list="tabList"></tab>
<!-- <view v-for="item in 100">{{item}}</view> -->
</view>
</template>
<script>
//uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用
//而且还是局部引用
// import navbar from "@/components/navbar/navbar.vue"
export default {
data() {
return {
tabList: []
}
},
onLoad() {
this.getLabel()
},
methods: {
getLabel() {
uniCloud.callFunction({
name: 'get_label',
}).then((res) => {
this.tabList = res.result.data
})
}
}
}
</script>
<style lang="scss">
</style>
tab.vue
<template>
<view class="tab">
<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>
<view class="tab-icons">
<uni-icons type="gear" size="26" color="#666"></uni-icons>
</view>
</view>
</template>
<script>
export default {
props:{
//接受一个list属性,类型是数组,默认值是空数组
list: {
type: Array,
default:[]
}
},
data() {
return {
};
}
}
</script>
<style lang="scss">
.tab {
display: flex;
width: 100%;
border-bottom: 1px #f5f5f5 solid;
background-color: #fff;
box-sizing: border-box; //盒内元素
.tab-scroll_box {
display: flex;
flex-wrap: nowrap; //不换行
align-items: center; //垂直居中
box-sizing: border-box; //盒内元素
height: 45px;
.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 {
// &::after指的是该元素后面的内容
content: '';
position: absolute;
top: 12px;
bottom: 12px;
left: 0;
width: 1px;
background-color: #ddd;
}
}
}
</style>
7.封装数据请求
api/index.js
//批量导出文件(自动导出)
const requireApi = require.context(
//参数:读取文件的路径,是否遍历文件的子目录,匹配文件的正则
//api目录的相对路径
'.',
//是否查询子目录
false,
//查询文件的一个后缀,只匹配.js后缀
/.js$/
)
let module = {}
requireApi.keys().forEach((key, index) => {
//requireApi.keys() 匹配好的文件列表 [./index.js,./list.js,...]
//forEach相当于map
if (key === './index.js') return
Object.assign(module, requireApi(key)) //对象合并
})
export default module
api/list.js
/* 存放所有接口请求的api */
import $http from '../http.js'
export const get_label = (data) => {
return $http({
url:'get_label',
data
})
}
http.js
/* 封装网络请求 */
export default function $http(options){
const {url,data} = options //url接口名称,data传给接口的数据
return new Promise((reslove, reject) => {
uniCloud.callFunction({
name: url,
data
}).then((res)=>{
if(res.result.code===200){
reslove(res.result)
}else{
reject(res.result)
}
}).catch((err)=>{reject(err)})
})
}
main.js
import Vue from 'vue'
import App from './App'
import api from './common/api'
Vue.config.productionTip = false
Vue.prototype.$api = api //绑定到vue实例中,之后就可以直接通过this.$api调用(方便)
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
tabbar/index.vue
<template>
<view class="content">
<navbar></navbar>
<tab :list="tabList"></tab>
<!-- <view v-for="item in 100">{{item}}</view> -->
</view>
</template>
<script>
//uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用
//而且还是局部引用
// import navbar from "@/components/navbar/navbar.vue"
export default {
data() {
return {
tabList: []
}
},
onLoad() {
this.getLabel()//获取选项卡数据
},
methods: {
getLabel() {
this.$api.get_label(
{
name:'get_label'
}
).then((res)=>{
const {data} = res
this.tabList = data
console.log(res)
})
}
}
}
</script>
<style lang="scss">
</style>
8.选项卡切换
tab.vue
<template>
<view class="tab">
<scroll-view class="tab-scroll" scroll-x>
<view class="tab-scroll_box">
<view v-for="(item,index) in list" :class="{active:activeIndex===index}" :key="index" class="tab-scroll_item" @click="clickTab(item,index)">
{{item.name}}
</view>
</view>
</scroll-view>
<view class="tab-icons">
<uni-icons type="gear" size="26" color="#666"></uni-icons>
</view>
</view>
</template>
<script>
export default {
props: {
//接受一个list属性,类型是数组,默认值是空数组
list: {
type: Array,
default: []
}
},
data() {
return {
activeIndex:0
};
},
methods: {
//点击tab
clickTab(item, index) {
this.activeIndex = index
this.$emit('tab',{
data:item,
index
})//调用父组件的方法,将当前tab的内容和下标传给父组件
}
}
}
</script>
<style lang="scss">
.tab {
display: flex;
width: 100%;
border-bottom: 1px #f5f5f5 solid;
background-color: #fff;
box-sizing: border-box; //盒内元素
.tab-scroll_box {
display: flex;
flex-wrap: nowrap; //不换行
align-items: center; //垂直居中
box-sizing: border-box; //盒内元素
height: 45px;
.tab-scroll_item {
flex-shrink: 0; //不挤压
padding: 0 10px;
color: #333;
font-size: 14px;
&.active{
color: $mk-base-color;
}
}
}
.tab-icons {
position: relative;
display: flex;
justify-content: center; //水平居中
align-items: center; //垂直居中
width: 45px;
&::after {
// &::after指的是该元素后面的内容
content: '';
position: absolute;
top: 12px;
bottom: 12px;
left: 0;
width: 1px;
background-color: #ddd;
}
}
}
</style>
:class="{active:activeIndex===index}" 动态类的定义,当activeIndex等于index,该标签就会多一个active的类
tabbar/index.vue
<template>
<view class="content">
<navbar></navbar>
<tab :list="tabList" @tab='tab'></tab>
<!-- <view v-for="item in 100">{{item}}</view> -->
</view>
</template>
<script>
//uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用
//而且还是局部引用
// import navbar from "@/components/navbar/navbar.vue"
export default {
data() {
return {
tabList: []
}
},
onLoad() {
this.getLabel()//获取选项卡数据
},
methods: {
//获取选项卡数据方法
getLabel() {
this.$api.get_label(
{
name:'get_label'
}
).then((res)=>{
const {data} = res
this.tabList = data
console.log(res)
})
},
//tab切换时候触发的方法
tab({data,index}){
console.log(data,index)
}
}
}
</script>
<style lang="scss">
</style>
9.基础卡片实现
components/list-scroll.vue
<template>
<view class="scroll">
<scroll-view scroll-y class="list-scroll">
<view>
<!-- slot 插槽 -->
<slot></slot>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss">
.scroll {
flex: 1;
overflow: hidden; //溢出隐藏
box-sizing: border-box; // 盒内元素
.list-scroll {
height: 100%;
display: flex;
flex-direction: column; //垂直排列
}
}
</style>
list-card.vue
<template>
<view>
<!-- 基础卡片 -->
<view class="listcard">
<view class="listcard-image">
<image src="../../static/logo.png" mode="aspectFill"></image>
</view>
<view class="listcard-content">
<view class="listcard-content__title">
<text>uni-app开发框架uni-app开发框架uni-app开发框架uni-app开发框架uni-app开发框架uni-app开发框架</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">120浏览</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss">
.listcard {
display: flex;
padding: 10px;
margin: 10px;
border-radius: 5px;
box-shadow: 0 0 5px 1px rgba($color:#000000, $alpha:0.1);
//5px模糊效果,1px扩散
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{
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;
}
}
}
}
</style>
tabbar/index.vue
<template>
<view class="home">
<navbar></navbar>
<tab :list="tabList" @tab='tab'></tab>
<list-scroll>
<list-card v-for="item in 5"></list-card>
</list-scroll>
</view>
</template>
<script>
//uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用
//而且还是局部引用
// import navbar from "@/components/navbar/navbar.vue"
export default {
data() {
return {
tabList: []
}
},
onLoad() {
this.getLabel()//获取选项卡数据
},
methods: {
//获取选项卡数据方法
getLabel() {
this.$api.get_label(
{
name:'get_label'
}
).then((res)=>{
const {data} = res
this.tabList = data
console.log(res)
})
},
//tab切换时候触发的方法
tab({data,index}){
console.log(data,index)
}
}
}
</script>
<style lang="scss">
page{
height: 100%;
display: flex;
}
.home{
display: flex;
flex-direction: column;//垂直排列
flex:1;//等价于flex-grow: 1;flex-shrink: 1;flex-basis: 0%;
overflow: hidden;//溢出隐藏
// border: 1px red solid;
}
</style>
10.更多卡片视图实现
components/list-card.vue
<template>
<view>
<!-- 基础卡片 -->
<view v-if="mode==='base'" class="listcard">
<view class="listcard-image">
<image src="../../static/logo.png" mode="aspectFill"></image>
</view>
<view class="listcard-content">
<view class="listcard-content__title">
<text>uni-app开发框架uni-app开发框架uni-app开发框架uni-app开发框架uni-app开发框架uni-app开发框架</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">120浏览</view>
</view>
</view>
</view>
<!-- 多图模式卡片 -->
<view v-if="mode==='column'" class="listcard mode-column">
<view class="listcard-content">
<view class="listcard-content__title">
<text>uni-app开发框架</text>
</view>
<view class="listcard-image">
<view v-for="item in 3" :key="item" 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">120浏览</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>uni-app开发框架</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">120浏览</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props:{
//通过接受mode来判断,显示什么模式的卡片,mode是String类型的字符串,默认值是base
//base基础卡片,column多图卡片,image,大图卡片
mode:{
type:String,
default:'base'
}
},
data() {
return {
};
}
}
</script>
<style lang="scss">
.listcard {
display: flex;
padding: 10px;
margin: 10px;
border-radius: 5px;
box-shadow: 0 0 5px 1px rgba($color:#000000, $alpha:0.1);
//5px模糊效果,1px扩散
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 {
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: 0px;
}
.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: 0px;
}
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>
tabbar/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>
<script>
//uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用
//而且还是局部引用
// import navbar from "@/components/navbar/navbar.vue"
export default {
data() {
return {
tabList: []
}
},
onLoad() {
this.getLabel()//获取选项卡数据
},
methods: {
//获取选项卡数据方法
getLabel() {
this.$api.get_label(
{
name:'get_label'
}
).then((res)=>{
const {data} = res
this.tabList = data
console.log(res)
})
},
//tab切换时候触发的方法
tab({data,index}){
console.log(data,index)
}
}
}
</script>
<style lang="scss">
page{
height: 100%;
display: flex;
}
.home{
display: flex;
flex-direction: column;//垂直排列
flex:1;//等价于flex-grow: 1;flex-shrink: 1;flex-basis: 0%;
overflow: hidden;//溢出隐藏
// border: 1px red solid;
}
</style>
11.实现内容切换
tabbar/index.vue
<template>
<view class="home">
<navbar></navbar>
<tab :list="tabList" @tab='tab'></tab>
<view class="home-list">
<list :list="tabList"></list>
</view>
</view>
</template>
<script>
//uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用
//而且还是局部引用
// import navbar from "@/components/navbar/navbar.vue"
export default {
data() {
return {
tabList: []
}
},
onLoad() {
this.getLabel() //获取选项卡数据
},
methods: {
//获取选项卡数据方法
getLabel() {
this.$api.get_label({
name: 'get_label'
}).then((res) => {
const {
data
} = res
this.tabList = data
console.log(res)
})
},
//tab切换时候触发的方法
tab({
data,
index
}) {
console.log(data, index)
}
}
}
</script>
<style lang="scss">
page {
height: 100%;
display: flex;
}
.home {
display: flex;
flex-direction: column; //垂直排列
flex: 1; //等价于flex-grow: 1;flex-shrink: 1;flex-basis: 0%;
overflow: hidden; //溢出隐藏
.home-list{
flex: 1;//撑开
box-sizing: border-box;//盒内显示
}
}
</style>
components/list.vue
<template>
<swiper class="home-swiper">
<swiper-item v-for="(item,index) in list" :key="index" class="swiper-item">
<listItem></listItem>
</swiper-item>
</swiper>
</template>
<script>
import listItem from './listItem.vue' //引入list-item组件
export default {
//引入组件必须要注册,习惯
components: {
listItem
},
props: {
//list是父组件传来的选项卡的数据
list: {
type: Array,
default: []
}
},
data() {
return {
};
},
}
</script>
<style lang="scss">
.home-swiper {
height: 100%;
.swiper-item {
height: 100%;
overflow: hidden;
.list-scroll {
height: 100%;
}
}
}
</style>
components/listItem.vue
<template>
<list-scroll class="list-scroll">
<list-card mode="base"></list-card>
<list-card mode="base"></list-card>
<list-card mode="base"></list-card>
<list-card mode="base"></list-card>
<list-card mode="base"></list-card>
<list-card mode="base"></list-card>
</list-scroll>
</template>
<script>
</script>
<style lang="scss">
.list-scroll{
height: 100%;
}
</style>
12.选项卡与内容联动
components/list.vue
<template>
<swiper class="home-swiper" :current="activeIndex" @change="change">
<swiper-item v-for="(item,index) in list" :key="index" class="swiper-item">
<listItem></listItem>
</swiper-item>
</swiper>
</template>
<script>
import listItem from './listItem.vue' //引入list-item组件
export default {
//引入组件必须要注册,习惯
components: {
listItem
},
props: {
//list是父组件传来的选项卡的数据
list: {
type: Array,
default: []
},
//activeIndex是父组件传来的当前tab得位置
activeIndex:{
type: Number,
default: 0
}
},
data() {
return {
};
},
methods: {
change(e) {
const {current} = e.detail //e.detail.current是当前swiper的第几项
this.$emit('change', current) //调用父组件传来的change方法,把current传给父组件
}
}
}
</script>
<style lang="scss">
.home-swiper {
height: 100%;
.swiper-item {
height: 100%;
overflow: hidden;
.list-scroll {
height: 100%;
}
}
}
</style>
components/tab.vue
<template>
<view class="tab">
<scroll-view class="tab-scroll" scroll-x>
<view class="tab-scroll_box">
<view v-for="(item,index) in list" :class="{active:activeIndex===index}" :key="index" class="tab-scroll_item"
@click="clickTab(item,index)">
{{item.name}}
</view>
</view>
</scroll-view>
<view class="tab-icons">
<uni-icons type="gear" size="26" color="#666"></uni-icons>
</view>
</view>
</template>
<script>
export default {
props: {
//接收父组件传来的一个list,类型是数组,默认值是空数组
list: {
type: Array,
default: []
},
//接收父组件传来的一个tabIndex,类型是number,默认值是0
tabIndex:{
type:Number,
default: 0,
}
},
//watch事件,监听data和props里面值的变化
watch:{
//监听tabIndex值的变化,一旦改变,立即触发
//方法名字与值得名字一样,接受两个参数,改变后和改变前的值
tabIndex(newVal,oldVal){
this.activeIndex=newVal
console.log('newVal==>',newVal,'oldVal==>',oldVal)
}
},
data() {
return {
activeIndex: 0
};
},
methods: {
//点击tab
clickTab(item, index) {
this.activeIndex = index
this.$emit('tab', {
data: item,
index
}) //调用父组件的方法,将当前tab的内容和下标传给父组件
}
}
}
</script>
<style lang="scss">
.tab {
display: flex;
width: 100%;
border-bottom: 1px #f5f5f5 solid;
background-color: #fff;
box-sizing: border-box; //盒内元素
.tab-scroll_box {
display: flex;
flex-wrap: nowrap; //不换行
align-items: center; //垂直居中
box-sizing: border-box; //盒内元素
height: 45px;
.tab-scroll_item {
flex-shrink: 0; //不挤压
padding: 0 10px;
color: #333;
font-size: 14px;
&.active {
color: $mk-base-color;
}
}
}
.tab-icons {
position: relative;
display: flex;
justify-content: center; //水平居中
align-items: center; //垂直居中
width: 45px;
&::after {
// &::after指的是该元素后面的内容
content: '';
position: absolute;
top: 12px;
bottom: 12px;
left: 0;
width: 1px;
background-color: #ddd;
}
}
}
</style>
tabbar/index.vue
<template>
<view class="home">
<navbar></navbar>
<tab :list="tabList" :tabIndex="tabIndex" @tab='tab'></tab>
<view class="home-list">
<list :list="tabList" @change="change" :activeIndex="activeIndex"></list>
</view>
</view>
</template>
<script>
//uni-app有个easyCom模式,只要组件的存放位置是"@/components/组件名/组件名.vue",就可以无需引用,直接使用
//而且还是局部引用
// import navbar from "@/components/navbar/navbar.vue"
export default {
data() {
return {
tabList: [],//tab列表
tabIndex:0,//当前swiper的位置
activeIndex:0,//当前tab的位置
}
},
onLoad() {
this.getLabel() //获取选项卡数据
},
methods: {
//获取选项卡数据方法
getLabel() {
this.$api.get_label({
name: 'get_label'
}).then((res) => {
const {
data
} = res
this.tabList = data
console.log(res)
})
},
//tab切换时候触发的方法
tab({data,index}) {
this.activeIndex=index //将切换后得位置赋值给activeIndex,让swiper去接收
console.log(data, index)
},
//切换swiper触发的方法,获得当前swiper是第几项,再传给tab,实现tab和swiper联动
change(current){
this.tabIndex = current
console.log('当前swiper的位置',current)
}
}
}
</script>
<style lang="scss">
page {
height: 100%;
display: flex;
}
.home {
display: flex;
flex-direction: column; //垂直排列
flex: 1; //等价于flex-grow: 1;flex-shrink: 1;flex-basis: 0%;
overflow: hidden; //溢出隐藏
.home-list{
flex: 1;//撑开
box-sizing: border-box;//盒内显示
}
}
</style>
13.内容卡片数据初始化
cloudfunctions-aliyun/get_list.js
'use strict';
const db = uniCloud.database()//创建数据库的引用
exports.main = async (event, context) => {
let list = await db.collection('article')
.field({content:false})//field()指定返回字段,不要content;false表示不返回,true表示只返回
.get()//获取article表数据
//返回数据给客户端
return {
code:200,
msg:'数据请求成功',
data:list.data
}
};
common/api/list.js
/* 存放所有接口请求的api */
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
})
}
components/list/list.vue
<template>
<swiper class="home-swiper" :current="activeIndex" @change="change">
<swiper-item v-for="(item,index) in list" :key="index" class="swiper-item">
<listItem :articleList="articleList"></listItem>
</swiper-item>
</swiper>
</template>
<script>
import listItem from './listItem.vue' //引入list-item组件
export default {
//引入组件必须要注册,习惯
components: {
listItem
},
props: {
//list是父组件传来的选项卡的数据
list: {
type: Array,
default: []
},
//activeIndex是父组件传来的当前tab得位置
activeIndex:{
type: Number,
default: 0
}
},
data() {
return {
articleList:[]//文章列表,需要传给子组件
};
},
created() {
this.getList()//调用get_list接口
},
methods: {
change(e) {
const {current} = e.detail //e.detail.current是当前swiper的第几项
this.$emit('change', current) //调用父组件传来的change方法,把current传给父组件
},
//调用get_list接口的方法
getList(){
this.$api.get_list().then(res=>{
const {data} = res
this.articleList=data
console.log(res)
})
}
}
}
</script>
<style lang="scss">
.home-swiper {
height: 100%;
.swiper-item {
height: 100%;
overflow: hidden;
.list-scroll {
height: 100%;
}
}
}
</style>
components/list/listItem.vue
<template>
<list-scroll class="list-scroll">
<list-card mode="base" v-for="item in articleList" :key="item._id" :item="item"></list-card>
</list-scroll>
</template>
<script>
export default {
props: {
//articleList是父组件传来的文章列表
articleList:{
type:Array,
default:[]
}
}
}
</script>
<style lang="scss">
.list-scroll {
height: 100%;
}
</style>
components/list-card.vue
<template>
<view>
<!-- 基础卡片 -->
<view v-if="item.mode==='base'" class="listcard">
<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 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来判断,显示什么模式的卡片,mode是String类型的字符串,默认值是base
//base基础卡片,column多图卡片,image,大图卡片
// mode:{
// type:String,
// default:'base'
// },
//item是父组件传来的文章列表里面的信息
item:{
type:Object,
default:{},
}
},
data() {
return {
};
}
}
</script>
<style lang="scss">
.listcard {
display: flex;
padding: 10px;
margin: 10px;
border-radius: 5px;
box-shadow: 0 0 5px 1px rgba($color:#000000, $alpha:0.1);
//5px模糊效果,1px扩散
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 {
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: 0px;
}
.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: 0px;
}
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>