第三章:豆瓣项目之登录、我的
创建项目
- 打开“微信开发者工具”->点击“+”新建项目,选择项目路径、项目名称(豆瓣评分)、设置AppID、选择不使用云服务、语言选择JavaScript,创建即可
- 打开VSCode
- 点击文件->打开…->找到“豆瓣评分”->打开
- VSCode写的代码怎么直接运行到“微信开发者工具”的模拟器上呢?
- “微信开发者工具”做的比较智能,一旦项目中任何文件改变,就会自动刷新模拟器
- 即写完代码一旦保存就会自动刷新模拟器
- 模拟器脱离出来:模拟器右上角有个图标“分离窗口”,点击之后模拟器就可以直接脱离出来
- 然后点击“钉”图标,让他永远显示在桌面最上层
- 本节要开发的页面如下
开发登录页面
登录整体配置
- 在项目文件目录pages下面,新建一个目录文件夹“login”
- 在login文件夹下新增4个文件
- 打开“微信开发者工具”,右击login文件夹->“新建 Page”->输入名称为login,这样就会自动生成4个login的文件,后缀分别为:
js、json、wxml、wxss
- 创建完继续回到VSCode继续开发
- 打开“微信开发者工具”,右击login文件夹->“新建 Page”->输入名称为login,这样就会自动生成4个login的文件,后缀分别为:
-
打开
app.json
文件,删除默认的index、logs路径"pages": [ "pages/login/login" ],
- 然后右击index、logs文件目录,删除这两个文件移动到废纸篓
-
保存,此时打开模拟器显示的是
pages/login/login.wxml
- 删除掉
app.wxss
文件中设置的全局样式代码 - 导入项目中使用的图片
- 在项目根目录文件夹新建一个“assets” 文件夹
- 然后在改文件夹下面建一个imgs文件夹,将图片全部放入
- 注意:上面两步都是在finder中手动建立,项目中会自动同步
登录页面搭建
login.wxml文件添加控件
- 注释掉
login.wxml
文件的初始化代码 - 背景图片实现方案
-
用view视图,然后设置背景图片
//用view做背景 <view class="logo"> </view> //css设置背景图片 .logo { /* 在微信小程序中背景图片不能使用本地图片来写 ,url()只能放远程地址*/ background-image: url(http://www.xxx/xxx.png); }
- 在小程序中,背景图片无法使用本地URL,代替方案有
- 用远程URL
- 将本地图片转成 Base64 编码:http://imgbase64.duoshitong.com/然后直接放入
url()
中
- 在小程序中,背景图片无法使用本地URL,代替方案有
-
直接用一个image视图来做背景图片
-
-
最终代码如下:
<!--pages/login/login.wxml--> <!-- <text>pages/login/login.wxml</text> --> <!-- 注意:直接输入view,然后回车就好,不要输入<view... 微信小程序中没有div,只有view --> <!-- 搞一个大容器来容纳所有的元素 --> <view class="container"> <!-- icon 图片部分 --> <!-- 方法1: 用view然后设置背景图片 --> <!-- <view class="logo"></view> --> <!-- 方法二: 直接用image做背景图片 --> <!-- 相对路径来找img地址 --> <!-- <image class="logo" src="../../assets/imgs/ic_launcher.png"></image> --> <!-- 通过绝对路径来找 --> <image class="logo" src="/assets/imgs/ic_launcher.png"></image> <!-- 登录部分 --> <!-- bindtap绑定事件 --> <view class="wechat login-btn" bindtap="wechatLogin">微信登录</view> <view class="douban login-btn" bindtap="doubanLogin">已有豆瓣帐号登录</view> <!-- 底部协议 --> <view class="agreement"> 登录表示同意 <view class="open" bind:tap="agreement">豆瓣使用协议/隐私协议</view> </view> </view>
login.wxss文件添加样式
/* pages/login/login.wxss */
.container {
/* 使用flex布局 */
display: flex;
/* 设置主轴方向 */
flex-direction: column;
/* 设置交叉轴居中 */
align-items: center;
/* 设置内边距 */
padding: 0 60rpx;
}
.logo {
/* 在微信小程序中背景图片不能使用本地图片来写 ,url()只能放远程地址*/
/* background-image: url(/assets/imgs/bg_mine_login.png); */
/* 设置图片大小 */
width: 144rpx;
height: 144rpx;
/* 设置边距 */
margin: 60rpx 0;
}
/* 登录按钮的统一样式 */
.login-btn {
height: 80rpx;
width: 100%;
/* 内容居中 */
line-height: 80rpx;
text-align: center;
/* 圆角 */
border-radius: 10rpx;
}
.wechat {
background-color: rgb(27, 172, 35);
color: white;
}
.douban {
background-color: white;
color: rgb(27, 172, 35);
border: 2rpx solid rgb(27, 172, 35);
margin-top: 30rpx;
}
/* 协议 */
.agreement {
color: gray;
font-size: 30rpx;
display: flex;
/* 绝对布局 */
position: absolute;
bottom: 50rpx;
}
.agreement .open {
color: rgb(27, 172, 35);
margin-left: 15rpx;
}
login.js文件添加事件
- 删除掉
Page({ })
中的所有默认代码 - 注意:js里面字符串用单引号,其他用双引号
// pages/login/login.js
Page({
// 点击事件
wechatLogin: function() {
console.log('wechatLogin')
},
doubanLogin: function() {
console.log('doubanLogin')
},
// 点击协议
agreement: function() {
console.log('agreement')
// 页面跳转
wx.navigateTo({
url: '/pages/agreement/agreement',
// 跳转成功
success: (result) => {
},
// 失败
fail: () => {},
// 结束
complete: () => {}
});
}
})
login.json文件添加
- 在该文件中设置导航栏的标题
{
"navigationBarTitleText": "登录"
}
关于小程序内打开网页
- 目前来说,个人类型的小程序暂不支持使用
- 先在小程序官网后台【开发-开发设置-业务域名】添加需要打开的链接,再使用 web-view 组件显示网页
- 否则不能使用web-view 组件打开网页
我的页面开发
- 新建profile文件夹,以及相应模块
-
打开app.json文件修改,将pages中的profile加载设置为第一个,然后开发“我的页面”
"pages": [ "pages/profile/profile", "pages/login/login", "pages/agreement/agreement" ],
技术知识点
数据绑定
- 目的:如何将数据动态的绑定的控件上去。
- 在当前模块的js文件中(比如profile.js)的data方法,就是绑定页面数据的方法
- 微信小程序渲染的数据必须放在data中
- 使用举例:
-
js文件数据初始化
// pages/profile/profile.js Page({ /** * 页面的初始数据 * 用来放动态绑定的数据 */ data: { name:'jack', age: 10, info:{ address: '广州', salary: 2000 }, arr:[10,20,30], nums: [11,12,13] } })
-
wxml 控件使用
<view> <!-- *{ 直接取值 }} : 这种语法叫mustache语法 --> 我的名字是:*{ name }},年龄是:*{ age }},城市: *{ info.address }},数组:*{ arr[2] }} </view>
-
for循环
- 查看官方文档“列表渲染”
-
举例:
<!-- 查看官方文档“列表渲染” --> <!-- for循环数组nums,每个元素叫num,每个下标叫idx--> <view wx:for="*{ nums }}" wx:for-item="num" wx:for-index="idx"> 位置为*{ idx }} 的元素是 *{ num }} </view> <!-- 系统默认每个元素叫item,每个下标叫:index --> <view wx:for="*{ nums }}"> 位置为*{ index }} 的元素是 *{ item }} </view> <!-- 直接定义一个数组 --> <view wx:for="*{ [100,101,102] }}"> 位置为*{ index }} 的元素是 *{ item }} </view> <!-- block使用: 直接输入block回车即可 --> <block wx:for="*{nums}}"> 位置为*{ index }} 的元素是 *{ item }} </block>
-
效果:
- view与block的区别
- view循环: 会拷贝n个view控件
- block循环:只会拷贝n个blcok内容
- view与block的区别
for循环中点击控件,如何在点击事件中拿到相应的控件?
-
wxml文件中for循环中的item控件,添加点击事件:
<view wx:for="*{ items }}" class="items" wx:key="unique"> <!-- bind:tap 添加点击事件 --> <view data-abc = "123" data-def ="234" data-index = "*{index}}" id="item-*{index}}" class="right" bind:tap="begin"> </view> </view>
- 2种方法:
- 方法1: 在控件中设置id属性,然后通过
evt.currentTarget.id
获取 - 方法2:自定义属性:
data-*
id、class
等都是系统自带的属性,我们也可以自定义属性- 上面自定义2个属性: abc、def,内容为123,234
- 方法1: 在控件中设置id属性,然后通过
- 2种方法:
-
js中添加实现点击响应:
begin:function (evt) { /* 如何拿到点击控件的索引? 1. 在控件中设置id属性,然后通过evt.currentTarget.id获取 2. 通过 data-* 设置自定义属性,自定义一个index属性:data-index = "*{index}}" */ // console.log(evt.currentTarget.id) console.log(evt.currentTarget.dataset.index) }
我的页面搭建
profile.json文件
{
"navigationBarTitleText": "书影音档案"
}
profile.wxml文件
<!--pages/profile/profile.wxml-->
<!-- <text>pages/profile/profile.wxml</text> -->
<view class="container">
<!-- 将版本与其他分割成2块 -->
<view>
<view class="login-wrapper">
<view class="login">登录</view>
<!-- mode;图片展示的额样式,是否拉伸等 -->
<image src="/assets/imgs/bg_mine_login.png" mode="scaleToFill"/>
</view>
<view class="items-wrapper">
<view class="title-wrapper">
<view class="title">我的书影音</view>
<view class="view">
登录查看
<view class="arrow"></view>
</view>
</view>
<!--
for循环
wx:key="unique" :防止警告
-->
<view wx:for="*{ items }}" class="items" wx:key="unique">
<view class="item">
<image class="icon" src="/assets/imgs/*{item.icon}}" />
<view class="info-wrapper">
<view class="info">
<view class="left">
<view class="top">*{item.title}}</view>
<view class="bottom">
<view class="count">*{item.count}}</view>
*{item.has}}
</view>
</view>
<!--
bind:tap 添加点击事件
data-* :自定义属性
id、class等都是系统自带的属性,我们也可以自定义属性
<view data-abc = "123" data-def ="234"> </view>
自定义2个属性: abc、def,内容为123,234
-->
<view data-abc = "123" data-def ="234" data-index = "*{index}}" id="item-*{index}}" class="right" bind:tap="begin">
<!-- text 可以识别换行符号\n -->
<text class="text">*{item.mark}}</text>
<view class="begin">立即开启</view>
<view class="arrow"></view>
</view>
</view>
<!--
下划线:
实现最后一行不显示下划线:
1. 通过css属性
/* 找到最后一个item,隐藏下划线 */
.items:last-child .line {
/* 隐藏,但是仍有改下划线,只是透明的 */
/* visibility: hidden; */
/* 直接没有该下划线 */
display: none;
}
2. 通过if条件句
wx:if="*{ }}" 条件控制 ,最后一个item不显示下划线
-->
<view wx:if="*{ index < items.length-1 }}" class="line"></view>
</view>
</view>
</view>
</view>
</view>
<view class="verson">版本 1.0.39</view>
</view>
profile.js文件
// pages/profile/profile.js
Page({
/**
* 页面的初始数据
* 用来放动态绑定的数据
*/
data: {
items: [
{
icon: 'ic_cat_movie.png',
title: '观影分析',
count: 0,
has: '看过',
mark: '录10部影片\n开启观影分析'
},
{
icon: 'ic_cat_book.png',
title: '读书分析',
count: 0,
has: '读过',
mark: '标记10本书\n开启读书分析'
},
{
icon: 'ic_cat_music.png',
title: '音乐分析',
count: 0,
has: '听过',
mark: '记录10张唱片\n开启音乐分析'
},
]
},
begin:function (evt) {
/*
如何拿到点击控件的索引?
1. 在控件中设置id属性,然后通过evt.currentTarget.id获取
2. 通过 data-* 设置自定义属性,自定义一个index属性:data-index = "*{index}}"
*/
// console.log(evt.currentTarget.id)
// console.log(evt.currentTarget.dataset.index)
const idx = evt.currentTarget.dataset.index;
if(idx == 0){
console.log('观影分析')
}else if (idx == 1){
console.log('读书分析')
}else if (idx == 2){
console.log('音乐分析分析')
}
}
})
profile.wxss文件
/* pages/profile/profile.wxss */
/* 为了是page大小正好充满屏幕,否则默认包裹子控件大小 */
page {
height: 100%;
}
/*
将版本号与其他分成2块 ,然后设置space-between
*/
.container {
/* 给页面设置一个统一的字体大小 */
font-size: 28rpx;
display: flex;
flex-direction: column;
/* 这一步必须将父控件page设置尺寸为手机屏幕尺寸才有效 */
justify-content: space-between;
height: 100%;
}
/*
版本适配:
1. 如果使用 absolute,那么版本就是悬浮在页面底部,当数据过多时,就不行了
2. 所以版本必须是跟随底部的,因此使用上面的方法
*/
.verson {
/* position: absolute;
bottom: 30rpx; */
color: gray;
font-size: 24rpx;
width: 100%;
text-align: center;
margin-bottom: 30rpx;
}
.login-wrapper {
display: flex;
justify-content: center;
align-items: center;
height: 500rpx;
/* 父相: 注意!!!! */
position: relative;
}
.login-wrapper .login {
color: #1e4826;
background-color: white;
border-radius: 50rpx;
font-size: 50rpx;
height: 100rpx;
line-height: 100rpx;
width: 500rpx;
text-align: center;
/* 设置阴影 */
box-shadow: 2rpx 2rpx 2rpx 0 rgba(0, 0, 0, 1);
}
/* 注意:1. 让图片脱离布局 2. 让图片位于“登录”下一层 */
.login-wrapper image {
/* 脱离布局,并设置称为背景 */
/* 子绝 */
position: absolute;
left: 0;
top: 0;
/* 设置层次:z轴最底层*/
z-index: -1;
width: 100%;
/* 注意:一定要在父控件设置相对定位:position: relative;,否则默认的相对控件为整个屏幕 */
height: 100%;
}
.items-wrapper {
margin-top: 30rpx;
}
.title-wrapper {
padding-left: 30rpx;
padding-right: 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
}
.title-wrapper .title {
font-weight: bold;
font-size: 36rpx;
}
.title-wrapper .view {
color: gray;
display: flex;
align-items: center;
}
/* 模拟一个箭头 */
.items-wrapper .arrow {
width: 20rpx;
height: 20rpx;
border: 4rpx solid gray;
border-left: none;
border-top: none;
transform: rotate(-45deg);
margin-left: 10rpx;
}
.item {
display: flex;
align-items: center;
padding-left: 30rpx;
padding-right: 20rpx;
margin-top: 20rpx;
/* 设置item里面所有文字的默认大小 */
font-size: 28rpx;
}
.item .icon {
width: 108rpx;
height: 108rpx;
margin-right: 30rpx;
}
/* 找到最后一个item,隐藏下划线 */
/* .items:last-child .line { */
/* 隐藏,但是仍有改下划线,只是透明的 */
/* visibility: hidden; */
/* 直接没有该下划线 */
/* display: none; */
/* } */
/* 让当前控件填充父元素 */
.info-wrapper {
/* 方法1 */
/* width: 100%; */
/* 方法2: */
/* flex布局中的最后一个布局属性:flex */
/* flex: 1; */
/* 等价于:flex-grow: 1 */
flex-grow: 1;
padding-right: 20rpx;
}
.info-wrapper .info {
display: flex;
justify-content: space-between;
align-items: center;
}
.info .left .bottom {
display: flex;
color: #818181;
align-items: center;
}
.info .left .top {
margin-bottom: 10rpx;
font-size: 32rpx;
}
.info .count {
font-size: 60rpx;
font-weight: bold;
margin-right: 10rpx;
}
.info .right {
display: flex;
align-items: center;
}
.info .right .text {
color: gray;
margin-right: 10rpx;
}
.info .right .begin {
color: #4a4a4a;
background-color: #f5f5f5;
padding: 10rpx 15rpx;
}
.item .line {
width: 100%;
height: 2rpx;
background-color: rgba(0, 0, 0, 0.2);
margin-top: 15rpx;
}