首页开发 | 「小慕读书」官网
完成本节内容需做如下准备工作:
<template>
<div>
div>
template>
<script>
export default {
}
script>
<style lang="scss" scoped>
style>
http://www.youbaobao.xyz/mpvue-design/preview/#artboard10
src\app.json
中的"usingComponents"
新增一条"usingComponents": {
"van-icon": "vant-weapp/dist/icon/index"
}
<template>
<div class="search-bar">
<div class="search-bar-wrapper">
<van-icon
class="search"
name="search"
size="16px"
color="#858C96"
>van-icon>
<input class="search-bar-input" />
<van-icon
class="clear"
name="clear"
size="16px"
color="#858C96"
>van-icon>
div>
div>
template>
<script>
export default {
}
script>
<style lang="scss" scoped>
.search-bar {
padding: 15px 15.5px;
.search-bar-wrapper {
display: flex; /*默认横向排列*/
align-items: center; /*垂直居中*/
height: 40px;
box-sizing: border-box;
background: #F5F7F9;
border-radius: 20px;
padding: 12px 17px;
.search-bar-input {
flex: 1; /*撑满剩余空间*/
margin: 0 8px;
}
.search .clear {
display: flex; /*默认横向排列*/
align-items: center; /*垂直居中*/
height: 100%;
}
}
}
style>
<template>
<div class="home">
<SearchBar />
div>
template>
<script>
import SearchBar from '../../components/home/SearchBar'
export default {
components: {
SearchBar
},
methods: {
}
}
script>
编写过程中可以把编辑框分为两块儿竖屏:右击对应编辑框标签,选择Split Vertically
拓展:
浅谈CSS3中display属性的Flex布局 - 博客园
从box-sizing:border-box属性入手,来了解盒模型-CSDN博客
组件名称 | 属性 | 参数 | 用途 | 默认值 |
---|---|---|---|---|
SearchBar | props | focus | 搜索框是否获得焦点 | false |
disabled | 搜索框是否可交互 | false | ||
limit | 搜索框最大可输入字符数 | 50 | ||
hotSearch | 热门搜索词 | (空) | ||
data | searchWord | 搜索关键字 | (空) | |
method | onSearchBarClick | 搜索框点击事件 | - | |
onClearClick | 点击清空事件 | - | ||
onChange | 输入监听事件 | - | ||
onConfirm | 点击虚拟键盘搜索事件 | - | ||
setValue | 对输入框关键字赋值 | - | ||
getValue | 获取输入框的关键字 | - |
:focus="focus"
:
:focus
是小程序提供的组件原生参数;focus: {
type: Boolean,
default: true
},
:disabled="disabled"
:搜索框是否可交互disabled: {
type: Boolean,
default: false
},
:maxlength="limit"
:limit
搜索框最大可输入字符数与小程序原生的maxlength
对应limit: {
type: Number,
default: 10
},
:placeholder="hotSearch.length === 0 ? '搜索' : hotSearch"
:
hotSearch
热门搜索词与小程序原生的placeholder
对应hotSearch: {
type: String,
default: ''
}
v-model="searchWord"
:searchWord
是搜索关键字,这里与数据源进行双向绑定data () {
return {
searchWord: ''
}
},
onSearchBarClick () { this.$emit('onClick') },
:整个搜索框的点击事件@click="onClearClick"
:onClearClick () {
this.searchWord = '' // 输入框的值清空
this.$emit('onClear') // 调用原生`onClear`方法
},
@input="onChange"
:onChange
输入监听事件与原生input
事件对应onChange (e) { // e是回调参数
const { value } = e.mp.detail // 通过console.log(e)知道所输入值得存放位置,然后通过解构的方式把它拿出来
this.$emit('onChange', value) // 接下来的事情交给原生处理
}
confirm-type="search"
:设置键盘右下角按钮的文字@confirm="onConfirm"
:点击虚拟键盘搜索按钮时触发
confirm
而不是bindconfirm
setValue (v) {
this.searchWord = v
},
getValue (v) {
return this.searchWord
}
placeholder-style="color: #ADB4BE"
:指定 placeholder 的样式v-if="searchWord.length > 0"
使搜索框的删除按钮在没有输入内容时隐藏接下来修改src\pages\index\index.vue
@onClick="onSearchBarClick"
:为整个搜索框绑定点击事件onSearchBarClick () {
// 跳转到搜索页面
}
目前src\pages\index\index.vue所有代码如下:
<template>
<div class="home">
<SearchBar
:disabled="false"
@onClick="onSearchBarClick"
/>
div>
template>
<script>
import SearchBar from '../../components/home/SearchBar'
export default {
components: {
SearchBar
},
methods: {
onSearchBarClick () {
// 跳转到搜索页面
}
}
}
script>
<style lang="scss" scoped>
style>
目前src\components\home\SearchBar.vue所有代码如下:
<template>
<div class="search-bar">
<div class="search-bar-wrapper">
<van-icon
class="search"
name="search"
size="16px"
color="#858C96"
>van-icon>
<input
class="search-bar-input"
:focus="focus"
:disabled="disabled"
:maxlength="limit"
:placeholder="hotSearch.length === 0 ? '搜索' : hotSearch"
v-model="searchWord"
@input="onChange"
confirm-type="search"
@confirm="onConfirm"
placeholder-style="color: #ADB4BE"
/>
<van-icon
class="clear"
name="clear"
size="16px"
color="#858C96"
@click="onClearClick"
v-if="searchWord.length > 0"
>van-icon>
div>
div>
template>
<script>
export default {
props: {
focus: {
type: Boolean,
default: true
},
disabled: {
type: Boolean,
default: false
},
limit: {
type: Number,
default: 10
},
hotSearch: {
type: String,
default: ''
}
},
data () {
return {
searchWord: ''
}
},
methods: {
onSearchBarClick () {
this.$emit('onClick')
},
onClearClick () {
this.searchWord = '' // 输入框的值清空
this.$emit('onClear') // 调用原生`onClear`方法
},
onChange (e) {
const { value } = e.mp.detail // 通过console.log(e)知道所输入值得存放位置,然后通过解构的方式把它拿出来
this.$emit('onChange', value) // 接下来的事情交给原生处理
},
onConfirm (e) {
const { value } = e.mp.detail // 通过console.log(e)知道所输入值得存放位置,然后通过解构的方式把它拿出来
this.$emit('onConfirm', value) // 接下来的事情交给原生处理
},
setValue (v) {
this.searchWord = v
},
getValue (v) {
return this.searchWord
}
}
}
script>
<style lang="scss" scoped>
.search-bar {
padding: 15px 15.5px;
.search-bar-wrapper {
display: flex; /*默认横向排列*/
align-items: center; /*垂直居中*/
height: 40px;
box-sizing: border-box;
background: #F5F7F9;
border-radius: 20px;
padding: 12px 17px;
.search-bar-input {
flex: 1; /*撑满剩余空间*/
margin: 0 8px;
}
.search .clear {
display: flex; /*默认横向排列*/
align-items: center; /*垂直居中*/
height: 100%;
}
}
}
style>
组件名称 | 属性 | 参数 | 用途 | 默认值 |
---|---|---|---|---|
HomeCard | props | data | 界面需要展示的数据,userInfo为用户信息,bookList为推荐图书信息,num为书架图书数量 | null |
hasSign | 今天是否签到 | false | ||
signDay | 连续签到天数 | 0 | ||
method | gotoShelf | 跳转到书架列表 | - | |
onBookClick | 图书点击事件 | - | ||
sign | 签到事件 | - |
新建src\components\home\HomeCard.vue:
<template>
<div class="home-card">
<div class="home-card-inner">
<div class="user-info">
<div class="avatar-wrapper">
<ImageView
src="https://www.youbaobao.xyz/mpvue-res/logo.jpg"
round
/>
div>
<div class="nickname">{{'米老鼠'}}div>
<div class="shelf-text">书架共有{{3}}本好书div>
<div class="round-item">div>
<div class="shelf-text">特别精选div>
div>
<div class="book-info">
<div class="book-wrapper">
<div class="book-img-wrapper">
<ImageView
src="https://www.youbaobao.xyz/book/res/img//EarthSciences/978-981-10-3713-9_CoverFigure.jpg"
/>
div>
<div class="book-img-wrapper">
<ImageView
src="https://www.youbaobao.xyz/book/res/img//EarthSciences/978-981-10-3713-9_CoverFigure.jpg"
/>
div>
<div class="book-img-wrapper">
<ImageView
src="https://www.youbaobao.xyz/book/res/img//EarthSciences/978-981-10-3713-9_CoverFigure.jpg"
/>
div>
div>
<div class="shelf-wrapper">
<div class="shelf">书架div>
<van-icon
class="arrow"
name="arrow"
size="11px"
color="#828489"
>van-icon>
div>
div>
<div class="feedback-wrapper">div>
<div class="feedback-text" @click="onFeedBackClick">反馈div>
div>
<van-dialog id="van-dialog">van-dialog>
div>
template>
<script>
import ImageView from '../base/ImageView'
import Dialog from 'vant-weapp/dist/dialog/dialog'
export default {
components: {ImageView},
props: {
data: Object,
hasSign: {
type: Boolean,
default: false
},
signDay: {
type: Number,
default: 0
}
},
methods: {
gotoShelf() {
},
onBookClick() {
},
sign() {
},
onFeedBackClick() {
Dialog.confirm({
title: '反馈',
message: '您是否确认提交反馈信息?',
confirmButtonText: '是',
cancelButtonText: '否'
}).then(() => {
console.log('点击是之后的事件')
}).catch(() => {
console.log('点击否之后的事件')
})
}
}
}
script>
<style lang="scss" scoped>
.home-card {
background-image: linear-gradient(-90deg, #54575F 0%, #595B60 100%);
border-radius: 15px;
margin: 22px 20px 0;
.home-card-inner {
position: relative;
padding: 21.5px 17px 20px 20px;
box-sizing: border-box;
.user-info {
display: flex;
align-items: center;
.avatar-wrapper {
width: 20px;
height: 20px;
}
.nickname, .shelf-text {
font-size: 12px;
color: #FFF;
}
.nickname {
margin: 0 8.5px;
}
.shelf-text {
opacity: 0.7;
}
.round-item {
width: 4px;
height: 4px;
background: #A2A2A2;
border-radius: 50%;
margin: 0 8px;
}
}
.book-info {
display: flex;
margin-top: 16.5px;
.book-wrapper {
flex: 1;
display: flex;
justify-content: space-around;
.book-img-wrapper {
width: 72px;
height: 101px;
}
}
.shelf-wrapper {
display: flex;
align-items: center;
.shelf {
width: 11px;
font-size: 11px;
word-break: break-word; /*断字点换行*/
color: #FFF;
opacity: .8;
}
}
}
.feedback-wrapper {
position: absolute;
top: 19.5px;
right: 0;
width: 44.5px;
height: 23.5px;
background: #D8D8D8;
opacity: .3;
border-radius: 100px 0 0 100px;
}
.feedback-text {
position: absolute;
top: 19.5px;
right: 0;
width: 44.5px;
height: 23.5px;
line-height: 23.5px;
text-align: center;
color: #FFF;
font-size: 11px;
}
}
}
style>
要点:
feedback
部分的透明样式对wrapper
和text
造成同步影响,可将两部分并列而不是包含(另外设置颜色也可)margin-top
定padding
定van-dialog
组件:
template
中有节点:
script
中引入:import Dialog from 'vant-weapp/dist/dialog/dialog'
onFeedBackClick() {
Dialog.confirm({
title: '反馈',
message: '您是否确认提交反馈信息?',
confirmButtonText: '是',
cancelButtonText: '否'
}).then(() => {
console.log('点击是之后的事件')
}).catch(() => {
console.log('点击否之后的事件')
})
}
测试图片地址:
- 头像测试图片地址:https://www.youbaobao.xyz/mpvue-res/logo.jpg
- 图书测试图片地址:https://www.youbaobao.xyz/book/res/img//EarthSciences/978-981-10-3713-9_CoverFigure.jpg
.eslintrc.js的设置最好与代码格式化保持一致:
'space-before-function-paren': ["error", "never"]
=> 解决Missing space before function parentheses
提供图片懒加载、预加载等功能
组件名称 | 属性 | 参数 | 用途 | 默认值 |
---|---|---|---|---|
ImageView | props | src | 图片地址 | (空) |
mode | 图片伸缩模式 | widthFix | ||
lazyLoad | 是否启动懒加载 | true | ||
round | 是否为圆形图片 | false | ||
height | 图片高度 | auto | ||
watch | src | 监听src变化,如果src变化,则将isLoading置为true | - | |
data | isLoading | 图片是否处于加载状态 | true | |
error | 是否加载失败 | false | ||
methods | onClick | 图片点击事件 | - | |
onError | 图片加载失败事件 | - | ||
onLoad | 图片加载成功事件 | - |
新建src\components\base\ImageView.vue
<template>
<div class="image-view" @click="onClick">
<img
:class="round ? 'round image' : 'image'"
:style="{ height }"
:src="src"
:mode="mode"
:lazy-load="lazyLoad"
@load="onLoad"
@error="onError"
v-show="!isLoading && !error"
/>
<img
:class="round ? 'round image' : 'image'"
:style="{ height }"
:src="'https://www.youbaobao.xyz/book/img/loading2.ae9e5924.jpeg'"
:mode="mode"
:lazy-load="lazyLoad"
v-show="isLoading || error"
/>
</div>
</template>
<script>
export default {
props: {
src: {
type: String,
default: ''
},
mode: {
type: String,
default: 'widthFix'
},
lazyLoad: {
type: Boolean,
default: true
},
round: {
type: Boolean,
default: false
},
height: {
type: String,
default: 'auto'
}
},
watch: {
src (newValue, preValue) {}
},
data () {
return {
isLoading: true,
error: false
}
},
methods: {
onClick () {
this.$emit('onClick')
},
onLoad () {
this.isLoading = false
this.error = false
},
onError () {
this.error = true
this.isLoading = false
}
}
}
</script>
<style lang="scss" scoped>
.image-view {
width: 100%;
.image {
width: 100%;
&.round {
border-radius: 50%;
}
}
}
</style>
要点:
v-show="!isLoading && !error"
:图片在加载结束且无错情况下显示v-show="isLoading || error"
:图片在未加载结束或有错情况下显示占位图.image-view {
width: 100%;
.image {
width: 100%;
&.round {
border-radius: 50%;
}
}
}
图片标签小程序默认
而
mpvue
是
资源下载地址
- 占位符图片地址:https://www.youbaobao.xyz/book/img/loading2.ae9e5924.jpeg
- 测试大图:https://www.youbaobao.xyz/mpvue-res/big.jpg