超英电影预热项目

底部导航栏tabBar配置

  • tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。
"tabBar": {
    "color": "#bfbfbf",  //tab 上的文字默认颜色
    "selectedColor": "#515151",  //tab 上的文字选中时的颜色
    "borderStyle": "black",  //tabbar 上边框的颜色,仅支持 black/white
    "backgroundColor": "#ffffff",  //tab 的背景色
    "list": [{
            "pagePath": "pages/index/index",  //页面路径,必须在 pages 中先定义
            "text": "首页", //tab 上按钮文字,在 5+APP 和 H5 平台为非必填。例如中间可放一个没有文字的+号图标
            "iconPath": "static/tabBarIco/index.png", //图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片,不支持字体图标
            "selectedIconPath": "static/tabBarIco/index_sel.png" //选中时的图片路径
        },
        {
            "pagePath": "pages/search/search",
            "text": "搜索",
            "iconPath": "static/tabBarIco/search.png",
            "selectedIconPath": "static/tabBarIco/search_sel.png"
        },
        {
            "pagePath": "pages/me/me",
            "text": "我的",
            "iconPath": "static/tabBarIco/me.png",
            "selectedIconPath": "static/tabBarIco/me_sel.png"
        }
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

移动端关闭导航栏

  • 每个页面均支持通过配置 titleNView 为 false 来禁用原生导航栏
  • 如果 h5 节点没有配置,默认会使用 app-plus 下的配置。
  • 配置了 h5 节点,则会覆盖 app-plus 下的配置。

轮播图组件swiper

  • HBuilderX快捷键,输入uswper
属性名 类型 默认值 说明 平台支持度
indicator-dots Boolean false 是否显示面板指示点
indicator-color Color rgba(0, 0, 0, .3) 指示点颜色
indicator-active-color Color #000000 当前选中的指示点颜色
autoplay Boolean false 是否自动切换
current Number 0 当前所在滑块的 index
current-item-id String 当前所在滑块的 item-id ,不能与 current 被同时指定 微信小程序、5+App、H5、百度小程序、头条小程序
interval Number 5000 自动切换时间间隔
duration Number 500 滑动动画时长
circular Boolean false 是否采用衔接滑动
vertical Boolean false 滑动方向是否为纵向
previous-margin String 0px 前边距,可用于露出前一项的一小部分,接受 px 和 rpx 值 头条小程序不支持
next-margin String 0px 后边距,可用于露出后一项的一小部分,接受 px 和 rpx 值 头条小程序不支持
display-multiple-items Number 1 同时显示的滑块数量 微信小程序、5+App、H5、百度小程序、头条小程序
skip-hidden-item-layout Boolean false 是否跳过未显示的滑块布局,设为 true 可优化复杂情况下的滑动性能,但会丢失隐藏状态滑块的布局信息 微信小程序、5+App
@change EventHandle current 改变时会触发 change 事件,event.detail = {current: current, source: source}
@animationfinish EventHandle 动画结束时会触发 animationfinish 事件,event.detail 同上 头条小程序不支持
<swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000" class="carousel">
	<swiper-item v-for="carousel in carouselList" :key="carousel.id"><image :src="carousel.image" class="carousel"></image></swiper-item>
</swiper>
1
2
3

uni.request发起网络请求

参数名 类型 必填 默认值 说明 平台差异说明
url String 开发者服务器接口地址
data Object/String/ArrayBuffer 请求的参数
header Object 设置请求的 header,header 中不能设置 Referer。
method String GET 有效值详见下方说明
dataType String json 如果设为 json,会尝试对返回的数据做一次 JSON.parse
responseType String text 设置响应的数据类型。合法值:text、arraybuffer 支付宝小程序不支持
success Function 收到开发者服务成功返回的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)

示例

uni.request({
    url: 'https://www.example.com/request', //仅为示例,并非真实接口地址。
    data: {
        text: 'uni.request'
    },
    header: {
        'custom-header': 'hello' //自定义请求头信息
    },
    success: (res) => {
        console.log(res.data);
        this.text = 'request success';
    }
});
1
2
3
4
5
6
7
8
9
10
11
12
13

提取请求服务器地址

  1. 单独import导入js公共文件
//common/common.js

const serverUrl = "https://www.imovietrailer.com/superhero"; //生产环境
// const serverUrl = "https://www.imovietrailer-dev.com/superhero";		// 开发环境
export default {
	serverUrl
}

//pages/index/index.vue

 import common from "../../common/common.js"
onLoad() {
    // 获取common.js中的服务器地址
    var serverUrl = common.serverUrl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. 将变量挂载到vue原型prototype上
//main.js

Vue.prototype.serverUrl = "https://www.imovietrailer.com/superhero";			// 生产环境
// Vue.prototype.serverUrl = "https://www.imovietrailer-dev.com/superhero";		// 开发环境

////pages/index/index.vue

onLoad() {
    // 通过挂载到main.js中获取服务器的地址,作为全局变量
	var serverUrl = this.serverUrl;
}
1
2
3
4
5
6
7
8
9
10
11

小程序真机调试

小程序真机调试需要配置AppID,请求服务器需要在小程序平台开发中配置服务器域名
  1. 配置appid
    • 进入小程序公众平台 -> 左侧开发选项 -> 开发设置 -> 开发者ID
  2. 配置服务器域名
    • 进入小程序公众平台 -> 左侧开发选项 -> 开发设置 -> 服务器域名
  3. 配置开发者工具
    • 进入微信开发者工具 -> 菜单项最右侧详情 -> 修改appid

滚动视图组件(scroll-view)

属性名 类型 默认值 说明 平台支持
scroll-x Boolean false 允许横向滚动
scroll-y Boolean false 允许纵向滚动
upper-threshold Number 50 距顶部/左边多远时(单位px),触发 scrolltoupper 事件
lower-threshold Number 50 距底部/右边多远时(单位px),触发 scrolltolower 事件
scroll-top Number 设置竖向滚动条位置
scroll-left Number 设置横向滚动条位置
scroll-into-view String 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
scroll-with-animation Boolean false 在设置滚动条位置时使用动画过渡
enable-back-to-top Boolean false iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只支持竖向 微信小程序
@scrolltoupper EventHandle 滚动到顶部/左边,会触发 scrolltoupper 事件
@scrolltolower EventHandle 滚动到底部/右边,会触发 scrolltolower 事件
@scroll EventHandle 滚动时触发,event.detail = {scrollLeft, scrollTop, scrollHeight, scrollWidth, deltaX, deltaY}
  • 使用竖向滚动时,需要给 <scroll-view>一个固定高度,通过 css 设置 height。
  • HBuilderX快捷键,输入uScrollView
<scroll-view scroll-x="true" class="page-block hot">
	<view class="single-poster" v-for="superhero in hotSuperheroList" :key="superhero.id">
		<view class="poster-warpper">
			<image :src="superhero.cover" class="poster"></image>
			<view class="movie-name">{{superhero.name}}</view>
			<view class="movie-score-warpper">
				<image src="../../static/icos/star-yellow.png" class="start-ico"></image>
			</view>
		</view>
	</view>
</scroll-view>
1
2
3
4
5
6
7
8
9
10
11

开发组件

开发标题组件

  • 用于显示标题和标题图标
<!-- titleBar.vue -->
<template name="titleBar">
	<view class="page-block super-hot">
		<view class="hot-title-warpper">
			<image :src="icon" class="hot-ico"></image>
			<view class="hot-title">{{title}}</view>
		</view>
	</view>
</template>

<script>
export default {
	name: 'titleBar',
	props: {
		title: '',
		icon: ''
	}
};
</script>

<!-- 调用 -->

<titleBar :title="meuntitle[2].title" :icon="meuntitle[2].icon" />
<!-- 组件中的静态资源路径要与使用组件位置为准 -->
meuntitle: [
  {
  	title: '热门超英',
  	icon: '../../static/icos/hot.png'
  },
  {
  	title: '热门预告',
  	icon: '../../static/icos/interest.png'
  },
  {
  	title: '猜你喜欢',
  	icon: '../../static/icos/guess-u-like.png'
  }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

开发星星组件

  • 根据评论分数,动态计算星星数量
<!-- trailerStars.vue -->
<template name="trailerStars">
	<!-- 定义组件名称 -->
	<view>
		<view class="movie-score-warpper">
			<image
				v-for="(yellow, index) in yelloScore" :key="index"
				src="../../static/icos/star-yellow.png" class="start-ico"></image>
			<image
				v-for="(gray, itemindex) in grayScore" :key="itemindex"
				src="../../static/icos/star-gray.png" class="start-ico"></image>
			<view class="movie-score" v-if="showNum == 1">
				{{innerScore}}
			</view>
		</view>
	</view>
</template>

<script>
export default {
	name: 'trailerStars', //定义组件名称
	//定义组件内部使用的属性
	props: {
		//自定义一个变量,用于接受父组件(首页或者其他页面)传入的参数值
		innerScore: 0, // 定义外部传入的分数
		showNum: 0 // 是否需要显示具体的分数  1:显示  0:不显示
	},
	data() {
		return {
			yelloScore: 0, //黄色星星
			grayScore: 5 //灰色星星
		};
	},
	created() {
		var tempScore = 0; 
		//判断传入的值是否为空
		if(this.innerScore != null && this.innerScore != undefined && this.innerScore != ''){
			tempScore = this.innerScore;
		}
		//计算星星的数量,并绑定到data数据中
		var yelloScore = parseInt(tempScore / 2);  //黄星
		var grayScore = 5 - yelloScore;  //灰星
		this.yelloScore = yelloScore;
		this.grayScore = grayScore;
	}
};
</script>

<!-- 调用组件 -->
<trailerStars :innerScore="guess.score" :showNum="0" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

媒体组件video

  • 常用属性
属性名 类型 默认值 说明 平台差异说明
src String 要播放视频的资源地址
autoplay Boolean false 是否自动播放
loop Boolean false 是否循环播放 头条小程序不支持
muted Boolean false 是否静音播放 头条小程序不支持
duration Number 指定视频时长,单位为秒(s)。 头条小程序不支持
controls Boolean true 是否显示默认播放控件(播放/暂停按钮、播放进度、时间) 头条小程序不支持
page-gesture Boolean false 在非全屏模式下,是否开启亮度与音量调节手势 微信小程序、H5
direction Number 设置全屏时视频的方向,不指定则根据宽高比自动判断。有效值为 0(正常竖向), 90(屏幕逆时针90度), -90(屏幕顺时针90度) 头条小程序不支持
show-progress Boolean true 若不设置,宽度大于240时才会显示 头条小程序不支持
show-fullscreen-btn Boolean true 是否显示全屏按钮 头条小程序不支持
show-play-btn Boolean true 是否显示视频底部控制栏的播放按钮 头条小程序不支持
show-center-play-btn Boolean true 是否显示视频中间的播放按钮 头条小程序不支持
enable-progress-gesture Boolean true 是否开启控制进度的手势 头条小程序不支持
objectFit String contain 当视频大小与 video 容器大小不一致时,视频的表现形式。contain:包含,fill:填充,cover:覆盖 微信小程序、H5
poster String 视频封面的图片网络资源地址,如果 controls 属性值为 false 则设置 poster 无效 头条小程序不支持
@play EventHandle 当开始/继续播放时触发play事件 头条小程序不支持
@pause EventHandle 当暂停播放时触发 pause 事件 头条小程序不支持
@ended EventHandle 当播放到末尾时触发 ended 事件 头条小程序不支持
@timeupdate EventHandle 播放进度变化时触发,event.detail = {currentTime, duration} 。触发频率 250ms 一次 头条小程序不支持
@fullscreenchange EventHandle 当视频进入和退出全屏时触发,event.detail = {fullScreen, direction},direction取为 vertical 或 horizontal 头条小程序不支持
@waiting EventHandle 视频出现缓冲时触发 头条小程序不支持
@error EventHandle 视频播放出错时触发 头条小程序不支持
  • <video> 默认宽度 300px、高度 225px,可通过 css 设置宽高。
<!-- 视频播放数据是一个数组,当点击播放一个视频时,其他视频应该停止播放,对此需要做优化 -->
<!-- 当页面隐藏时,视频播放应该停止,对此需要做优化 -->
<video
:id="trailer.id"
:data-playingIndex="trailer.id"
@play="meIsPlaying"
v-for="trailer in hotTrailerList" 
:key="trailer.id" 
:src="trailer.trailer" 
:poster="trailer.poster" 
class="hot-movie-single"
controls></video>
<script>
export default {
    data() {
        return {

        }
    },
    onHide() {
        // 如果此时有播放,禁止视频播放
		if (this.videoContext) {
			this.videoContext.pause();
		}
	},
    methods: {
        meIsPlaying(e) {
			//播放一个视频的时候,需要暂停其他正在播放的视频
			var me = this;
			var trailerId = "";
			if(e) {
                trailerId = e.currentTarget.dataset.playingindex;
                //创建并返回 video 上下文 videoContext 对象
				me.videoContext = uni.createVideoContext(trailerId);
			}
			var hotTrailerList = me.hotTrailerList;
			for (var i = 0; i < hotTrailerList.length ; i ++) {
				var tempId = hotTrailerList[i].id;
				//如果视屏id不等于当前播放的id,暂停他们的视频
				if (tempId != trailerId) {
						uni.createVideoContext(tempId).pause();
					}
			}
		}
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

界面-动画

创建一个动画实例 animation。调用实例的方法来描述动画。最后通过动画实例的export方法导出动画数据传递给组件的animation属性。

h5不支持,需要进行条件编译

点赞动画

<!-- 点赞数据是遍历出来的5条数据,当点击时,所有点赞都会执行动画,所以要创建画画数组,绑定对应的索引值 -->
<view class="movie-oper" :data-gIndex="gIndex" @click="praiseMe">
    <image src="../../static/icos/praise.png" class="praise-ico"></image>
    <view class="praise-me">点赞</view>
    <view :animation="animationDataArr[gIndex]" class="praise-me animation-opacity">+1</view>
</view>
<script>
export default {
    data() {
        return {
            animationData: {},
			animationDataArr: [{}, {}, {}, {}, {}]
        }
    },
    onUnload() {
		//页面卸载的时候,清除动画数据
		this.animationData = {};
		this.animationDataArr = [{}, {}, {}, {}, {}];
	},
    onLoad() {
        var me = this;
        // #ifdef APP-PLUS || MP-WEIXIN
		// 在页面创建的时候,创建一个临时动画对象
		this.animation = uni.createAnimation();
		// #endif
    },
    methods: {
       	//实现点赞动画效果
		praiseMe(e) {
			// console.log(e)
			// #ifdef APP-PLUS || MP-WEIXIN
			var gIndex = e.currentTarget.dataset.gindex;

			//构建动画数据,并且通过step来表示这组动画的完成
			this.animation
				.translateY(-60)
				.opacity(1)
				.step({
					duration: 400
				});
			//导出动画数据到view组件,实现组件的动画效果
			// this.animationData = this.animation.export();
			this.animationData = this.animation;
			this.animationDataArr[gIndex] = this.animationData.export();

			//还原动画
			setTimeout(() => {
				this.animation
					.translateY(0)
					.opacity(0)
					.step({
						duration: 0
					});
				// this.animationData = this.animation.export();
				this.animationData = this.animation;
				this.animationDataArr[gIndex] = this.animationData.export();
			}, 500);

			//#endif
		}
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

下拉刷新

  • 通过配置下拉刷新选项,实现首页下拉刷新请求更新数据
  • 在 js 中定义 onPullDownRefresh 处理函数(和onLoad等生命周期函数同级),监听该页面用户下拉刷新事件。
    • 需要在 pages.json 里,找到的当前页面的pages节点,并在 style 选项中开启 enablePullDownRefresh
    • 当处理完数据刷新后,uni.stopPullDownRefresh 可以停止当前页面的下拉刷新。
{
	"path": "pages/index/index",
	"style": {
		"app-plus": {
			"titleNView": true //禁用原生导航栏
		},
		"enablePullDownRefresh": true //开启单页面下拉刷新功能
	}
}
1
2
3
4
5
6
7
8
9
<script>
export default {
    data() {
        return {
            guessULikeList: []
        }
    },
    onLoad() {
        //页面加载时请求数据
        this.refresh();
    },
    onPullDownRefresh() {
        //监听下拉刷新
		this.refresh();
	},
    methods: {
       refresh() {
           //加载动画
			uni.showLoading({
				title: '加载中...',
				mask: true
            });
            //导航条加载动画
			uni.showNavigationBarLoading();

			var serverUrl = this.serverUrl;
			// 猜你喜欢
			uni.request({
				url: serverUrl + '/index/guessULike',
				method: 'POST',
				header: {
					'content-type': 'application/x-www-form-urlencoded'
				},
				data: {
					qq: '466481615'
				},
				success: res => {
					// console.log(res.data);
					// 获取真实数据之前,务必判断状态是否为200
					if (res.data.status == 200) {
						var guessULikeList = res.data.data;
						this.guessULikeList = guessULikeList;
					}
				},
				complete: () => {
                    //接口调用结束的回调函数(调用成功、失败都会执行)
					uni.hideLoading();
					uni.hideNavigationBarLoading();
					uni.stopPullDownRefresh();
				}
			});
		}
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

上拉加载

  • onReachBottom页面上拉触底事件的处理函数
  • 一般用于分页数据
data() {
  return {
  	trailerList: [],
  	keywords: '', // 搜索的关键字
  	page: 1, // 当前第几页
  	pageSize: 9,
  	totalPages: 1 // 总页数
  };
},
onReachBottom() {
  var page = this.page + 1;
  var totalPages = this.totalPages;
  // 如果当前需要分页的分页数和总页数相等,就不分页
  if (page > totalPages) {
  	return;
  }
  this.pagedTrailerList(this.keywords, page, 15);
},
methods: {
    pagedTrailerList(keywords, page, pageSize) {
			//搜索内容列表
			var serverUrl = this.serverUrl;
			// 猜你喜欢
			uni.request({
				url: serverUrl + '/search/list',
				method: 'POST',
				header: {
					'content-type': 'application/x-www-form-urlencoded'
				},
				data: {
					keywords: keywords,
					qq: '466481615',
					page: page,
					pageSize: pageSize
				},
				success: res => {
					// console.log(res.data);
					// 获取真实数据之前,务必判断状态是否为200
					if (res.data.status == 200) {
						var tempList = res.data.data.rows;
						//拼接获取到的数据
						this.trailerList = this.trailerList.concat(tempList);
						this.totalPages = res.data.data.total; //获取总页数
						this.page = page; //覆盖当前页面里的page
						// console.log(this.trailerList)
					}
				},
				complete: () => {
					uni.hideNavigationBarLoading();
					uni.hideLoading();
				}
			});
		},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

页面跳转

属性名 类型 默认值 说明 平台支持度
url String 应用内的跳转链接,值为相对路径或绝对路径,如:"../first/first","/pages/first/first",注意不能加 .vue 后缀
open-type String navigate 跳转方式
delta Number 当 open-type 为 'navigateBack' 时有效,表示回退的层数
animation-type String pop-in/out 当 open-type 为 navigateTo、navigateBack 时有效,窗口的显示/关闭动画效果,详见:窗口动画 5+App
animation-duration Number 300 当 open-type 为 navigateTo、navigateBack 时有效,窗口显示/关闭动画的持续时间。 5+App
hover-class String navigator-hover 指定点击时的样式类,当hover-class="none"时,没有点击态效果
hover-stop-propagation Boolean false 指定是否阻止本节点的祖先节点出现点击态 微信小程序
hover-start-time Number 50 按住后多久出现点击态,单位毫秒
hover-stay-time Number 600 手指松开后点击态保留时间,单位毫秒

open-type 有效值

说明 平台支持度
navigate 对应 uni.navigateTo 的功能
redirect 对应 uni.redirectTo 的功能
switchTab 对应 uni.switchTab 的功能
reLaunch 对应 uni.reLaunch 的功能 头条小程序不支持
navigateBack 对应 uni.navigateBack 的功能

navigator-hover 默认为 {background-color: rgba(0, 0, 0, 0.1); opacity: 0.7;}, <navigator> 的子节点背景色应为透明色。

<template>
    <view>
        <view class="page-body">
            <view class="btn-area">
                <navigator url="navigate/navigate?title=navigate" hover-class="navigator-hover">
                    <button type="default">跳转到新页面</button>
                </navigator>
                <navigator url="redirect/redirect?title=redirect" redirect hover-class="other-navigator-hover">
                    <button type="default">在当前页打开</button>
                </navigator>
            </view>
        </view>
    </view>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • uni.navigateTo
    • 保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面。
参数 类型 必填 默认值 说明 平台支持度
url String 需要跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2',path为下一个页面的路径,下一个页面的onLoad函数可得到传递的参数 :-
animationType String pop-in 窗口显示的动画效果,详见:窗口动画 5+App
animationDuration Number 300 窗口动画持续时间,单位为 ms 5+App
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
uni.navigateTo({
    url: 'test?id=1&name=uniapp'
});

// test.vue
export default {
    onLoad: function (option) { //option为object类型,会序列化上个页面传递的参数
        console.log(option.id); //打印出上个页面传递的参数。
        console.log(option.name); //打印出上个页面传递的参数。
    }
}
1
2
3
4
5
6
7
8
9
10
11
  • uni.switchTab
    • 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
参数 类型 必填 说明
url String 需要跳转的 tabBar 页面的路径(需在 pages.json 的 tabBar 字段定义的页面),路径后不能带参数
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
  • uni.navigateBack
    • 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。
参数 类型 必填 默认值 说明 平台支持度
delta Number 1 返回的页面数,如果 delta 大于现有页面数,则返回到首页。
animationType String pop-out 窗口关闭的动画效果,详见:窗口动画 5+App
animationDuration Number 300 窗口关闭动画的持续时间,单位为 ms 5+App
// 注意:调用 navigateTo 跳转时,调用该方法的页面会被加入堆栈,而 redirectTo 方法则不会。见下方示例代码

// 此处是A页面
uni.navigateTo({
    url: 'B?id=1'
});

// 此处是B页面
uni.navigateTo({
    url: 'C?id=1'
});

// 在C页面内 navigateBack,将返回A页面
uni.navigateBack({
    delta: 2
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • navigateTo, redirectTo 只能打开非 tabBar 页面。
  • switchTab 只能打开 tabBar 页面。
  • reLaunch 可以打开任意页面。
  • 页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
  • 不能在 App.vue 里面进行页面跳转。

修改导航栏属性

  1. pages.json配置导航栏
属性 类型 默认值 描述 平台差异说明
navigationBarBackgroundColor HexColor #000000 导航栏背景颜色,如"#000000"
navigationBarTextStyle String white 导航栏标题颜色,仅支持 black/white
navigationBarTitleText String 导航栏标题文字内容
navigationStyle String default 导航栏样式,仅支持 default/custom。 微信小程序7.0以上支持单页面设置custom以取消导航栏、百度小程序
disableScroll Boolean false 设置为 true 则页面整体不能上下滚动(bounce效果),只在页面配置中有效,在globalStyle中设置无效 微信小程序(iOS)、百度小程序(iOS)
backgroundColor HexColor #ffffff 窗口的背景色 微信小程序、百度小程序、头条小程序
backgroundTextStyle String dark 下拉 loading 的样式,仅支持 dark/light
enablePullDownRefresh Boolean false 是否开启下拉刷新,详见页面生命周期。
onReachBottomDistance Number 50 页面上拉触底事件触发时距页面底部距离,单位只支持px,详见页面生命周期
backgroundColorTop HexColor #ffffff 顶部窗口的背景色。 仅 iOS 平台
backgroundColorBottom HexColor #ffffff 底部窗口的背景色。 仅 iOS 平台
app-plus Object 设置编译到 App 平台的特定样式,配置项参考下方 app-plus App
h5 Object 设置编译到 H5 平台的特定样式,配置项参考下方 H5 H5
usingComponents Object 引用小程序组件,参考 小程序组件 5+App、微信小程序、支付宝小程序、百度小程序
  1. 通过api修改导航栏属性
  • uni.setNavigationBarTitle动态设置当前页面的标题。
参数 类型 必填 说明
title String 页面标题
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
uni.setNavigationBarTitle({
    title: '新的标题'
});
1
2
3
  • uni.setNavigationBarColor设置页面导航条颜色
参数 类型 必填 说明 平台差异说明
frontColor String 前景颜色值,包括按钮、标题、状态栏的颜色,仅支持 #ffffff 和 #000000 5+App、H5、微信小程序、百度小程序
backgroundColor String 背景颜色值,有效值为十六进制颜色
animation Object 动画效果,{duration,timingFunc} 微信小程序、百度小程序
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
// 通过api修改导航栏的属性
uni.setNavigationBarColor({
    frontColor: '#ffffff',
    backgroundColor: '#000000'
});
1
2
3
4
5

自定义导航栏按钮

自定义按钮

属性 类型 默认值 描述
type String none 按钮样式,可取值见:buttons 样式
color String 默认与标题文字颜色一致 按钮上文字颜色
background String 默认值为灰色半透明 按钮的背景颜色,仅在标题栏type=transparent时生效
badgeText String 按钮上显示的角标文本,最多显示3个字符,超过则显示为...
colorPressed String 默认值为 color 属性值自动调整透明度为 0.3 按下状态按钮文字颜色
float String right 按钮在标题栏上的显示位置,可取值"left"、"right"
fontWeight String normal 按钮上文字的粗细。可取值"normal"-标准字体、"bold"-加粗字体。
fontSize String 按钮上文字大小
fontSrc String 按钮上文字使用的字体文件路径。不支持网络地址,请统一使用本地地址。
select String false 是否显示选择指示图标(向下箭头)
text String 按钮上显示的文字。使用字体图标时 unicode 字符表示必须 '\u' 开头,如 "\ue123"(注意不能写成"\e123")。
width String 44px 按钮的宽度,可取值: "*px" - 逻辑像素值,如"10px"表示10逻辑像素值,不支持upx。按钮的内容居中显示; "auto" - 自定计算宽度,根据内容自动调整按钮宽度

按钮样式

  • 使用 type 值设置按钮的样式时,会忽略 fontSrc 和 text 属性。
说明
forward 前进按钮
back 后退按钮
share 分享按钮
favorite 收藏按钮
home 主页按钮
menu 菜单按钮
close 关闭按钮
none 无样式,需通过 text 属性设置按钮上显示的内容、通过 fontSrc 属性设置使用的字体库。
//pages.json
{
    "pages": [
        {
			"path": "pages/movie/movie",
			"style": {
				"navigationBarTitleText": "电影详情",
				"app-plus": {
					"titleNView": {
						"type": "transparent",
						"buttons": [{
								"type": "share"
							},
							{
								"type": "home"
							},
							{
								"type": "forward"
							}
						]
					}
				}
			}
		},
        ...
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

图片预览功能uni.previewImage

参数名 类型 必填 说明 平台差异说明
current String 当前显示图片的链接,不填则默认为 urls 的第一张
indicator String 图片指示器样式,可取值: "default" - 底部圆点指示器; "number" - 顶部数字指示器; "none" - 不显示指示器。 5+APP
loop Boolean 是否可循环预览,默认值为"false" 5+APP
urls Array(String) 需要预览的图片链接列表
longPressActions Object 长按图片显示操作菜单,如不填默认为保存相册 5+APP、HBuilderX1.9.5+
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
<view class="scroll-block">
	<view class="plots-title">剧照</view>
	<scroll-view scroll-x class="scroll-list">
		<image v-for="(img, imgIndex) in plotPicsArray" :key="imgIndex" :src="img" class="plot-image" mode="aspectFill" :data-imgIndex="imgIndex" @click="lookMe"></image>
	</scroll-view>
</view>
<script>
methods: {
  lookMe(e) {
  	// console.log(e);
  	var me = this;
  	var imgIndex = e.currentTarget.dataset.imgindex;
  	uni.previewImage({
  		current: me.plotPicsArray[imgIndex],
  		urls: me.plotPicsArray
  	});
  },
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

多数组图片预览

<scroll-view scroll-x class="scroll-list">
<!-- 多数组图片预览,由于是数组对象拼接,我们要获得每一个数组里的对象,然后把每一个对象的url拿出来拼成一个新的数组,新的url,随后做预览功能 -->
  <view class="actor-wapper" v-for="(director, staffIndex) in directorArray" :key="director.staffId">
  	<image :src="director.photo" class="single-actor" mode="aspectFill" @click="lookStaffs" :data-staffIndex="staffIndex"></image>
  	<view class="actor-name">{{ director.name }}</view>
  	<view class="actor-role">{{ director.actName }}</view>
  </view>
  <view class="actor-wapper" v-for="(actor, actorIndex) in actorArray" :key="actor.staffId">
  	<!-- 拼接index使用 -->
  	<image :src="actor.photo" class="single-actor" mode="aspectFill" @click="lookStaffs" :data-staffIndex="actorIndex + directorArray.length"></image>
  	<view class="actor-name">{{ actor.name }}</view>
  	<view class="actor-role">{{ actor.actName }}</view>
  </view>
</scroll-view>
<script>
methods: {
  lookStaffs(e) {
  	var staffIndex = e.currentTarget.dataset.staffindex;
  	// 拼接导演和演员的数组,成为一个新数组
  	var directorArray = this.directorArray;
  	var actorArray = this.actorArray;
  	var newStaffArray = [];
  	newStaffArray = newStaffArray.concat(directorArray).concat(actorArray);
  	var urls = [];
  	for (var i = 0; i < newStaffArray.length; i++) {
  		var tempPhoto = newStaffArray[i].photo;
  		urls.push(tempPhoto);
  	}
  	uni.previewImage({
  		current: urls[staffIndex],
  		urls: urls
  	});
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

uni.showActionSheet​显示操作菜单

  • 显示底部操作的菜单项
参数 类型 必填 说明 平台差异说明
itemList Array(String) 按钮的文字数组 微信、百度、头条小程序数组长度最大为6个
itemColor HexColor 按钮的文字颜色,字符串格式,默认为"#000000" 头条小程序不支持
success Function 接口调用成功的回调函数,详见返回参数说明
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
uni.showActionSheet({
    itemList: ['A', 'B', 'C'],
    success: function (res) {
        console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
    },
    fail: function (res) {
        console.log(res.errMsg);
    }
});
1
2
3
4
5
6
7
8
9

自定义图片预览效果

  • 自定义图片预览可以在组件中添加自定义操作,如保存图片
<!-- cover.vue -->
<template>
	<view class="black"><image :src="cover" mode="widthFix" @longpress="operator"></image></view>
</template>

<script>
export default {
	data() {
		return {
			cover: ''
		};
	},
	onLoad(params) {
		this.cover = params.cover;
	},
	methods: {
		operator() {
			var me = this;
			uni.showActionSheet({
				itemList: ['保存图片到本地'],
				success: function(res) {
					// console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
					if (res.tapIndex == 0) {
						uni.showLoading({
							title: '图片保存中'
						});
						uni.downloadFile({
							url: me.cover, //仅为示例,并非真实的资源
							success: res => {
								if (res.statusCode === 200) {
									var tempFilePath = res.tempFilePath;
									// console.log(tempFilePath);
									uni.saveImageToPhotosAlbum({
										filePath: tempFilePath,
										success: function() {
											uni.showToast({
												title: '保存成功',
												mask: false,
												duration: 1500
											});
										},
										complete() {
											uni.hideLoading();
										}
									});

									
								}
							}
						});
					}
				},
				fail: function(res) {
					console.log(res.errMsg);
				}
			});
		}
	}
};
</script>

<style lang="less">
.black {
	position: fixed;
	display: flex;
	justify-content: center;
	align-items: center;
	width: 100%;
	height: 100%;
	background: #000;
}
</style>

<!-- 组件调用 -->
<navigator :url="'../cover/cover?cover=' + trailerInfo.cover">
    <image :src="trailerInfo.cover" class="cover"></image>
</navigator>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

第三方分享接口uni.share

  // 此函数仅仅支持在小程序端的分享,分享到微信群或微信好友
onShareAppMessage(res) {
  var me = this;
  return {
  	title: me.trailerInfo.name,
  	path: '/pages/movie/movie?trailerId=' + me.trailerInfo.id
  };
  },
  //监听导航栏的按钮
onNavigationBarButtonTap(e) {
  var me = this;
  var index = e.index;
  // console.log(index)

  var trailerInfo = this.trailerInfo;
  var trailerId = trailerInfo.id;
  var trailerName = trailerInfo.name;
  var cover = trailerInfo.cover;
  var poster = trailerInfo.poster;
  //index为0,进行分享
  if (index == 0) {
  	uni.share({
  			provider: "weixin",
  			scene: "WXSenceTimeline",
  			type: 0,
  			href: "http://www.imovietrailer.com/#/pages/movie/movie?trailerId=" + trailerId,
  			title: "NEXT超英预告:《" + trailerName + "》",
  			summary: "NEXT超英预告:《" + trailerName + "》",
  			imageUrl: cover,
  			success: function (res) {
  				console.log("success:" + JSON.stringify(res));
  			}
  		});
  }
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

表单组件

属性名 类型 说明 平台支持
report-submit Boolean 是否返回 formId 用于发送模板消息 微信小程序、支付宝小程序
@submit EventHandle 携带 form 中的数据触发 submit 事件,event.detail = {value : {'name': 'value'} , formId: ''},report-submit 为 true 时才会返回 formId
@reset EventHandle 表单重置时会触发 reset 事件

数据缓存

  • uni.setStorage将数据存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个异步接口。
参数名 类型 必填 说明
key String 本地缓存中的指定的 key
data Object/String 需要存储的内容
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
uni.setStorage({
    key: 'storage_key',
    data: 'hello',
    success: function () {
        console.log('success');
    }
});
1
2
3
4
5
6
7
  • uni.getStorage从本地缓存中异步获取指定 key 对应的内容。
参数名 类型 必填 说明
key String 本地缓存中的指定的 key
success Function 接口调用的回调函数,res = {data: key对应的内容}
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
uni.getStorage({
    key: 'storage_key',
    success: function (res) {
        console.log(res.data);
    }
});
1
2
3
4
5
6
  • uni.removeStorage从本地缓存中异步移除指定 key。
参数名 类型 必填 说明
key String 本地缓存中的指定的 key
success Function 接口调用的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
uni.removeStorage({
    key: 'storage_key',
    success: function (res) {
        console.log('success');
    }
});
1
2
3
4
5
6
  • uni.setStorageSync将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。
参数 类型 必填 说明
key String 本地缓存中的指定的 key
data Object/String 需要存储的内容
uni.setStorageSync('storage_key', 'hello');
1
  • uni.getStorageSync从本地缓存中同步获取指定 key 对应的内容。
uni.getStorageSync('storage_key');
1
  • uni.removeStorageSync从本地缓存中同步移除指定 key。
uni.removeStorageSync('storage_key');
1
  • uni.clearStorage()清理本地数据缓存。
  • uni.clearStorageSync()同步清理本地数据缓存。

登录/注册

  • 登录注册调用同一个接口,通过判断账号创建时间,判断此账号时候注册,如果注册过及执行登录,否则注册并且登录
<form @submit="formSubmit">
	<view class="face-wapper"><image src="../../static/icos/default-face.png" class="face"></image></view>
	<view class="info-wapper">
		<label class="words-lbl">账号</label>
		<input name="username" type="text" value="" class="input" placeholder="请输入用户名" placeholder-class="graywords" />
	</view>

	<view class="info-wapper" style="margin-top: 40upx;">
		<label class="words-lbl">密码</label>
		<input name="password" type="text" value="" password="true" class="input" placeholder="请输入密码" placeholder-class="graywords" />
	</view>
	<button type="primary" form-type="submit" style="margin-top: 60upx;width: 90%;">注册/登录</button>
</form>
<script>
formSubmit(e) {
	var me = this;
	var username = e.detail.value.username;
	var password = e.detail.value.password;
	// console.log(username)
	// console.log(password)

	uni.showLoading({
		title: '加载中...',
		mask: true
	});
	uni.showNavigationBarLoading();

	var serverUrl = this.serverUrl;
	// 猜你喜欢
	uni.request({
		url: serverUrl + '/user/registOrLogin?qq=466481615',
		method: 'POST',
		header: {
			'content-type': 'application/json;charset=utf-8'
		},
		data: {
			username: username,
			password: password
		},
		success: res => {
			console.log(res.data);
			// 获取真实数据之前,务必判断状态是否为200
			if (res.data.status == 200) {
				var userInfo = res.data.data;
				// console.log(userInfo)
				//保存用户信息到全局的缓存中
				uni.setStorageSync('globalUser', userInfo);
				//tabBar页面跳转
				uni.switchTab({
					url: '../me/me'
				});
			} else if (res.data.status == 500) {
				// uni.showToast({
				// 	title: res.data.msg,
				// 	duration: 2000,
				// 	image: "../../static/icos/error.png"
				// })
				uni.showModal({
					title: '提示',
					content: res.data.msg,
					success: function(res) {
						if (res.confirm) {
							// console.log('用户点击确定');
						} else if (res.cancel) {
							// console.log('用户点击取消');
						}
					}
				});
			}
		},
		complete: () => {
			uni.hideNavigationBarLoading();
			uni.hideLoading();
		}
	});
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

vue原型挂载判断登录信息方法

Vue.prototype.getGlobalUser = function(key) {
	var userInfo = uni.getStorageSync("globalUser");
	if (userInfo != null && userInfo != "" && userInfo != undefined) {
		return userInfo;
	} else {
		return null;
	}
}
1
2
3
4
5
6
7
8

第三方登录

参数名 类型 必填 说明 平台差异说明
provider String 登录服务提供商,通过 uni.getProvider 获取,如果不设置则弹出登录列表选择界面
scopes String/Array 见平台差异说明 授权类型,默认 auth_base。支持 auth_base(静默授权)/ auth_user(主动授权) / auth_zhima(芝麻信用) 支付宝小程序
timeout Number 超时时间,单位ms 微信小程序、百度小程序
success Function 接口调用成功的回调
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
  • uni.getUserInfo 获取用户信息。
参数名 类型 必填 说明 平台差异说明
provider String 登录服务提供商,通过 uni.getProvider 获取
withCredentials Boolean 是否带上登录态信息。 微信小程序、头条小程序
lang Number 指定返回用户信息的语言,默认为 en。更多值请参考下面的说明。 微信小程序
timeout Number 超时时间,单位 ms。 微信小程序
success Function 接口调用成功的回调
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
<!-- 第三方登录H5不支持,所以隐藏掉 -->
<!-- #ifndef H5 -->
<view class="third-wapper">
	<view class="third-line">
		<view class="single-line"><view class="line"></view></view>

		<view class="third-words">第三方账号登录</view>

		<view class="single-line"><view class="line"></view></view>
	</view>

	<view class="third-icos-wapper">
		<!-- 5+app 用qq/微信/微博 登录 小程序用微信小程序登录 h5不支持 -->
		<!-- #ifdef APP-PLUS -->
		<image src="../../static/icos/weixin.png" data-logintype="weixin" @click="appOAuthLogin" class="third-ico"></image>
		<image src="../../static/icos/QQ.png" data-logintype="qq" @click="appOAuthLogin" class="third-ico" style="margin-left: 80upx;"></image>
		<image src="../../static/icos/weibo.png" data-logintype="sinaweibo" @click="appOAuthLogin" class="third-ico" style="margin-left: 80upx;"></image>
		<!-- #endif -->
		<!-- #ifdef MP-WEIXIN -->
		<button open-type="getUserInfo" @getuserinfo="wxLogin" class="third-btn-ico"></button>
		<!-- #endif -->
	</view>
</view>
<!-- #endif -->
<script>
appOAuthLogin(e) {
			var me = this;
			// 获取用户的登录类型
			var logintype = e.currentTarget.dataset.logintype;

			// 授权登录
			uni.login({
				provider: logintype,
				success(loginRes) {
					// 授权登录成功以后,获取用户的信息
					uni.getUserInfo({
						provider: logintype,
						success(info) {
							// console.log(JSON.stringify(info));

							var userInfo = info.userInfo;
							var face = '';
							var nickname = '';
							var openIdOrUid = '';
							if (logintype == 'weixin') {
								face = userInfo.avatarUrl;
								nickname = userInfo.nickName;
								openIdOrUid = userInfo.openId;
							} else if (logintype == 'qq') {
								openIdOrUid = userInfo.openId;
								nickname = userInfo.nickname;
								face = userInfo.figureurl_qq_2;
							} else if (logintype == 'sinaweibo') {
								openIdOrUid = userInfo.id;
								nickname = userInfo.nickname;
								face = userInfo.avatar_large;
							}

							// 调用开发者后台,执行一键注册或登录
							uni.request({
								url: me.serverUrl + '/appUnionLogin/' + logintype + '?qq=466481615',
								data: {
									openIdOrUid: openIdOrUid,
									nickname: nickname,
									face: face
								},
								method: 'POST',
								success(result) {
									if (result.data.status == 200) {
										var userInfo = result.data.data;
										// 保存用户信息到全局的缓存中
										uni.setStorageSync('globalUser', userInfo);
										// 切换页面跳转,使用tab切换的api
										uni.switchTab({
											url: '../me/me'
										});
									} else if (userResult.data.status == 500) {
										uni.showModal({
											title: '提示',
											content: userResult.data.message,
											success: function(res) {
												if (res.confirm) {
													// console.log('用户点击确定');
												} else if (res.cancel) {
													// console.log('用户点击取消');
												}
											}
										});
									}
								}
							});
						}
					});
				}
			});
		},
		// 实现在微信小程序端的微信登录
		wxLogin(e) {
			var me = this;
			console.log(e);
			// 通过微信开发能力,获得微信用户的基本信息
			var userInfo = e.detail.userInfo;

			// 实现微信登录
			uni.login({
				provider: 'weixin',
				success(loginResult) {
					// console.log(loginResult);
					// 获得微信登录的code:授权码
					var code = loginResult.code;
					// 设置登录到哪个对于的微信小程序,大家可以根据自己的后端去实现业务参数
					// [0:NEXT超英预告][1:超英预告][2:NEXT学院电影预告]
					var loginToWhichMP = 1;
					uni.request({
						url: me.serverUrl + '/mpWXLogin/' + code + '?qq=466481615',
						header: {
							'content-type': 'application/json;charset=utf-8'
						},
						data: {
							avatarUrl: userInfo.avatarUrl,
							nickName: userInfo.nickName,
							whichMP: loginToWhichMP
						},
						method: 'POST',
						success(userResult) {
							console.log(userResult);
							if (userResult.data.status == 200) {
								var userInfo = userResult.data.data;
								// 保存用户信息到全局的缓存中
								uni.setStorageSync('globalUser', userInfo);
								// 切换页面跳转,使用tab切换的api
								uni.switchTab({
									url: '../me/me'
								});
							} else if (userResult.data.status == 500) {
								uni.showModal({
									title: '提示',
									content: userResult.data.message,
									success: function(res) {
										if (res.confirm) {
											// console.log('用户点击确定');
										} else if (res.cancel) {
											// console.log('用户点击取消');
										}
									}
								});
							}
						}
					});
				}
			});
		},
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

我的页面获取登录信息

  • 通过userIsLogin判断是否登录,登录显示登录信息,未登录显示点击注册登录
<template>
	<view class="page page-fill">
		<view class="header">
			<view v-if="userIsLogin">
				<image :src="userInfo.faceImage" class="face"></image>
			</view>
			<view v-else>
				<image src="http://122.152.205.72:88/group1/M00/00/05/CpoxxFw_-5-AFyVyAABLIH8xBTw233.png" class="face"></image>
			</view>
			
			<view class="info-wapper" v-if="userIsLogin">
				<view class="nickname">
					{{userInfo.nickname}}
				</view>
				<view class="nav-info">ID:{{userInfo.id}}</view>
			</view>
			<view v-else>
				<navigator url="../registLogin/registLogin">
					<view class="nickname regist-login">
						注册/登录
					</view>
				</navigator>
			</view>
			
			<view class="set-wapper" v-if="userIsLogin">
				<navigator url="../meInfo/meInfo">
					<image src="../../static/icos/settings.png" class="settings"></image>	
				</navigator>
			</view>
		</view>
		
	</view>
</template>

<script>
	export default {
		data() {
			return {
				userIsLogin: false,
				userInfo: {}	
			};
		},
		onShow() {
			var me = this;
			// 用户状态的切换
			// var userInfo = uni.getStorageSync("globalUser");
			// if (userInfo != null && userInfo != "" && userInfo != undefined) {
			// 	me.userIsLogin = true;
			// 	me.userInfo = userInfo;
			// } else {
			// 	me.userIsLogin = false;
			// 	me.userInfo = {};
			// }

			// 使用挂载方法获取用户数据
			var userInfo = me.getGlobalUser("globalUser");
			if (userInfo != null) {
				me.userIsLogin = true;
				me.userInfo = userInfo;
			} else {
				me.userIsLogin = false;
				me.userInfo = {};
			}
		}
	}
</script>

<style lang="less">
@import url('me.less');
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

个人中心页面设置退出登录

  • 退出登录时,后端也有用户登录的会话数据,前后端都需要清理用户登录数据,用户发起请求,后端退出登录成功前台清理用户信息
<template>
	<view class="page page-fill">
		<view class="page-block info-list">
			<!-- 头像 -->
			<view class="item-wapper face-line-upbottom" @click="operator">
				<view class="info-words">头像</view>

				<view class="right-wapper">
					<image :src="globalUser.faceImage" class="face"></image>
					<view class="arrow-block">
						<image src="../../static/icos/left-gray-arrow.png" class="arrow-ico"></image>
					</view>
				</view>
			</view>
			
			<view class="line-top">
				<view class="line"></view>
			</view>
			
			<!-- 昵称 -->
			<view class="item-wapper" @click="modifyNickname">
				<view class="info-words">昵称</view>
				
				<view class="right-wapper">
					<view class="gray-fields">
						{{globalUser.nickname}}
					</view>
					<view class="arrow-block">
						<image src="../../static/icos/left-gray-arrow.png" class="arrow-ico"></image>
					</view>
				</view>
			</view>
			
			<view class="line-top">
				<view class="line"></view>
			</view>
			
			<!-- 生日 -->
			<view class="item-wapper" @click="modifyBirthday">
				<view class="info-words">生日</view>
				
				<view class="right-wapper">
					<view class="gray-fields">
						{{globalUser.birthday}}
					</view>
					<view class="arrow-block">
						<image src="../../static/icos/left-gray-arrow.png" class="arrow-ico"></image>
					</view>
				</view>
			</view>
			
			<view class="line-top">
				<view class="line"></view>
			</view>
			
			<!-- 性别 -->
			<view class="item-wapper" @click="modifySex">
				<view class="info-words">性别</view>
				
				<view class="right-wapper">
					<view class="gray-fields">
						
						<view v-if="globalUser.sex == 1"></view>
						<view v-else-if="globalUser.sex == 0"></view>
						<view v-else>
							未选择
						</view>
						
					</view>
					<view class="arrow-block">
						<image src="../../static/icos/left-gray-arrow.png" class="arrow-ico"></image>
					</view>
				</view>
			</view>
			
		</view>
		
		<view class="footer-wapper">
			<view class="footer-words" @click="cleanStorage">
				清理缓存
			</view>
			<view class="footer-words" style="margin-top: 10upx;" @click="logout">
				退出登录
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				globalUser: {}
			}
		},
		onShow() {
			var me = this;
			var globalUser = me.getGlobalUser("globalUser");
			me.globalUser = globalUser;
		},
		methods: {
			modifySex() {
				uni.navigateTo({
					url: "../sex/sex"
				})
			},
			modifyBirthday() {
				uni.navigateTo({
					url: "../meBirthday/meBirthday"
				})
			},
			modifyNickname() {
				uni.navigateTo({
					url: "../meNickname/meNickname"
				})
			},
			operator() {
				var me = this;
				var globalUser = me.getGlobalUser("globalUser");
				uni.showActionSheet({
					itemList: ["查看我的头像", "从相册选择上传"],
					success(res) {
						var index = res.tapIndex;
						if (index == 0) {
							// 预览头像
							var faceArr = [];
							faceArr.push(globalUser.faceImage);
							uni.previewImage({
								urls: faceArr,
								current: faceArr[0]
							})
						} else if (index == 1) {
							// 选择上传头像
							uni.chooseImage({
								count: 1,
								sizeType: ["compressed"],
								sourceType: ["album"],
								success(res) {
									// 获得临时路径
									var tempFilePath = res.tempFilePaths[0];
									// #ifdef H5
									uni.navigateTo({
										url: "../meFace/meFace?tempFilePath=" + tempFilePath
									})
									// #endif
									// #ifndef H5
									uni.navigateTo({
										url: "../faceCrop/faceCrop?tempFilePath=" + tempFilePath
									})
									// #endif
								}
							})
						}
					}
				})
			},
			cleanStorage() {
				uni.clearStorageSync();
				uni.showToast({
					title: "清理缓存成功",
					mask: false,
					duration: 1500
				})
			},
			logout() {
				//后端也有用户登录的会话数据,前后端都需要清理用户登录数据,用户发起请求,后端退出登录成功前台清理用户信息
				var globalUser = this.getGlobalUser("globalUser");
				var serverUrl = this.serverUrl;
				uni.request({
					url: serverUrl + '/user/logout?userId=' + globalUser.id,
					method: "POST",
					header: {
						'content-type': 'application/x-www-form-urlencoded'
					},
					data: {
						qq: '466481615',
					},
					success: (res) => {
						// 获取真实数据之前,务必判断状态是否为200
						if (res.data.status == 200) {
							// 如果服务器返回200,代表用户在服务端退出登录成功
							uni.removeStorageSync("globalUser");
							uni.switchTab({
								url: "../me/me"
							})
						}
					}
				});
			}
		}
	}
</script>

<style>
@import url('meInfo.less');
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

上传功能uni.uploadFile

参数名 类型 必填 说明 平台差异说明
url String 开发者服务器 url
files Array 需要上传的文件列表。使用 files 时,filePath 和 name 不生效。 5+App
fileType String 见平台差异说明 文件类型,image/video/audio 仅支付宝小程序,且必填。
filePath String 要上传文件资源的路径。
name String 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header Object HTTP 请求 Header, header 中不能设置 Referer。
formData Object HTTP 请求中其他额外的 form data
success Function 接口调用成功的回调函数
fail Function 接口调用失败的回调函数
complete Function 接口调用结束的回调函数(调用成功、失败都会执行)
<!-- meFace.vue -->
<template>
	<view class="page page-fill">
		
		<view class="pending-wapper">
			<image id="face" :src="tempFace" class="pending-face" mode="scaleToFill"></image>
		</view>
		
		<view class="notice">
			<view class="notice-words">
				* 请从相册中选择等比宽高的图片噢~
			</view>
		</view>
		
		<view class="footer-opertor">
			<view class="opertor-words" @click="changePendingFace">
				重新选择
			</view>
			<view class="opertor-words" @click="upload">
				确认上传
			</view>
		</view>
		
	</view>
</template>

<script>
	export default {
		data() {
			return {
				tempFace: ""
			};
		},
		onLoad(params) {
			var tempFilePath = params.tempFilePath;
			this.tempFace = tempFilePath;
		},
		methods: {
			// 重新选择头像
			changePendingFace() {
				var me = this;
				uni.chooseImage({
					count: 1,
					sizeType: ["compressed"],
					sourceType: ["camera"],
					success(res) {
						// 获得临时路径
						var tempFilePath = res.tempFilePaths[0];
						me.tempFace = tempFilePath;
					}
				})
			},
			upload() {
				var me = this;
				var globalUser = me.getGlobalUser("globalUser");
				
				uni.showLoading({
					mask: true,
					title: "上传中,请稍后"
				});
				//文件上传服务器
				uni.uploadFile({
					url: me.serverUrl + "/user/uploadFace?userId=" + globalUser.id + '&qq=466481615',
					filePath: me.tempFace,
					name: "file",
					header: {
						"headerUserId": globalUser.id,
						"headerUserToken": globalUser.userUniqueToken
					},
					success(res) {
						// debugger;
						var resDataStr = res.data;
						// console.log(typeof(resDataStr));
						//上传文件返回的res是一个字符串,需要进行转换
						var resData = JSON.parse(resDataStr);
						// console.log(typeof(resData));
						if (resData.status == 200) {
							// 获得最新的用户数据
							var userInfo = resData.data;
							uni.setStorageSync("globalUser", userInfo);
							uni.navigateBack({
								delta: 1
							})
						} else if (resData.status == 502 || resData.status == 500) {
							uni.showToast({
								title: res.data.msg,
								image: "../../static/icos/error.png",
								duration: 2000
							})
						}
					},
					complete() {
						uni.hideLoading();
					}
				});
				
			}
		}
	}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

自定义图片裁剪上传功能

<!-- faceCrop -->
<template>
	<view class="page page-fill">
		<view class="page-body uni-content-info">
			<view class='cropper-content'>
				<view v-if="isShowImg" class="uni-corpper myDistance" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'">
					<view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'">
						<image :src="imageSrc" class="myImage" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image>
						<view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing" @touchend.stop="contentTouchEnd"
							:style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'">
							<view class="uni-cropper-view-box">
								<view class="uni-cropper-dashed-h"></view>
								<view class="uni-cropper-dashed-v"></view>
								<view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-point point-tr" data-drag="topTight"></view>
								<view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove" @touchend.stop="dragEnd"></view>
								<view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view>
								<view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
								<view class="uni-cropper-point point-lt" data-drag="leftTop"></view>
							</view>
						</view>
					</view>
				</view>
			</view>
			<!-- <view class='cropper-config'>
				<button type="primary reverse" @click="getImage" style='margin-top: 30upx;'> 选择图片 </button>
				<button type="warn" @click="getImageInfo" style='margin-top: 30upx;'> 点击生成图片 </button>
			</view> -->
			<view class="footer-opertor">
				<view class="opertor-words" @click="getImage">
					重新选择
				</view>
				<view class="opertor-words" @click="getImageInfo">
					确认上传
				</view>
			</view>
			<canvas canvas-id="myCanvas" :style="'position:absolute;border: 1px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas>
		</view>
		<!-- <page-foot :name="name"></page-foot> -->
	</view>
</template>

<script>
	let sysInfo = uni.getSystemInfoSync();
	let SCREEN_WIDTH = sysInfo.screenWidth
	let PAGE_X, // 手按下的x位置
		PAGE_Y, // 手按下y的位置 
		PR = sysInfo.pixelRatio, // dpi
		T_PAGE_X, // 手移动的时候x的位置
		T_PAGE_Y, // 手移动的时候Y的位置
		CUT_L, // 初始化拖拽元素的left值
		CUT_T, // 初始化拖拽元素的top值
		CUT_R, // 初始化拖拽元素的
		CUT_B, // 初始化拖拽元素的
		CUT_W, // 初始化拖拽元素的宽度
		CUT_H, //  初始化拖拽元素的高度
		IMG_RATIO, // 图片比例
		IMG_REAL_W, // 图片实际的宽度
		IMG_REAL_H, // 图片实际的高度
		DRAFG_MOVE_RATIO = 1, //移动时候的比例,
		INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
		DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度

	export default {
		/**
		 * 页面的初始数据
		 */
		data() {
			return {
				// name:'杨大宝',
				imageSrc: '',
				isShowImg: false,
				// 初始化的宽高
				cropperInitW: SCREEN_WIDTH,
				cropperInitH: SCREEN_WIDTH,
				// 动态的宽高
				cropperW: SCREEN_WIDTH,
				cropperH: SCREEN_WIDTH,
				// 动态的left top值
				cropperL: 0,
				cropperT: 0,

				transL: 0,
				transT: 0,

				// 图片缩放值
				scaleP: 0,
				imageW: 0,
				imageH: 0,

				// 裁剪框 宽高
				cutL: 0,
				cutT: 0,
				cutB: SCREEN_WIDTH,
				cutR: '100%',
				qualityWidth: DRAW_IMAGE_W,
				innerAspectRadio: DRAFG_MOVE_RATIO
			}
		},
		/**
		 * 生命周期函数--监听页面加载
		 */
		onLoad: function (params) {
			var imageSrc = params.tempFilePath;
			this.imageSrc = imageSrc;
			
			var globalUser = this.getGlobalUser('globalUser');
			this.globalUser = globalUser;
		},
		/**
		 * 生命周期函数--监听页面初次渲染完成
		 */
		mounted: function () {

			this.loadImage();

		},
		methods: {
			setData: function (obj) {
				let that = this;
				Object.keys(obj).forEach(function (key) {
					that.$set(that.$data, key, obj[key])

				});
			},
			getImage: function () {
				var _this = this
				uni.chooseImage({
					success: function (res) {
						_this.setData({
							imageSrc: res.tempFilePaths[0],
						})
						_this.loadImage();
					},
				})
			},
			loadImage: function () {
				var _this = this
				uni.showLoading({
					title: '图片加载中...',
				})

				uni.getImageInfo({
					src: _this.imageSrc,
					success: function success(res) {
						IMG_RATIO = res.width / res.height
						if (IMG_RATIO >= 1) {
							IMG_REAL_W = SCREEN_WIDTH
							IMG_REAL_H = SCREEN_WIDTH / IMG_RATIO
						} else {
							IMG_REAL_W = SCREEN_WIDTH * IMG_RATIO
							IMG_REAL_H = SCREEN_WIDTH
						}
						let minRange = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H
						INIT_DRAG_POSITION = minRange > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : minRange
						// 根据图片的宽高显示不同的效果   保证图片可以正常显示
						if (IMG_RATIO >= 1) {
							let cutT = Math.ceil((SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - INIT_DRAG_POSITION)) / 2);
							let cutB = cutT;
							let cutL = Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH + INIT_DRAG_POSITION) / 2);
							let cutR = cutL;
							_this.setData({
								cropperW: SCREEN_WIDTH,
								cropperH: SCREEN_WIDTH / IMG_RATIO,
								// 初始化left right
								cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
								cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2),
								cutL: cutL,
								cutT: cutT,
								cutR: cutR,
								cutB: cutB,
								// 图片缩放值
								imageW: IMG_REAL_W,
								imageH: IMG_REAL_H,
								scaleP: IMG_REAL_W / SCREEN_WIDTH,
								qualityWidth: DRAW_IMAGE_W,
								innerAspectRadio: IMG_RATIO
							})
						} else {
							let cutL = Math.ceil((SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO)) / 2);
							let cutR = cutL;
							let cutT = Math.ceil((SCREEN_WIDTH - INIT_DRAG_POSITION) / 2);
							let cutB = cutT;
							_this.setData({
								cropperW: SCREEN_WIDTH * IMG_RATIO,
								cropperH: SCREEN_WIDTH,
								// 初始化left right
								cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2),
								cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),

								cutL: cutL,
								cutT: cutT,
								cutR: cutR,
								cutB: cutB,
								// 图片缩放值
								imageW: IMG_REAL_W,
								imageH: IMG_REAL_H,
								scaleP: IMG_REAL_W / SCREEN_WIDTH,
								qualityWidth: DRAW_IMAGE_W,
								innerAspectRadio: IMG_RATIO
							})
						}
						_this.setData({
							isShowImg: true
						})
						uni.hideLoading()
					}
				})
			},
			// 拖动时候触发的touchStart事件
			contentStartMove(e) {
				PAGE_X = e.touches[0].pageX
				PAGE_Y = e.touches[0].pageY
			},

			// 拖动时候触发的touchMove事件
			contentMoveing(e) {
				var _this = this
				var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
				var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
				// 左移
				if (dragLengthX > 0) {
					if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL
				} else {
					if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR
				}

				if (dragLengthY > 0) {
					if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT
				} else {
					if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB
				}
				this.setData({
					cutL: this.cutL - dragLengthX,
					cutT: this.cutT - dragLengthY,
					cutR: this.cutR + dragLengthX,
					cutB: this.cutB + dragLengthY
				})

				PAGE_X = e.touches[0].pageX
				PAGE_Y = e.touches[0].pageY
			},

			contentTouchEnd() {

			},

			// 获取图片
			getImageInfo() {
				var _this = this;
				uni.showLoading({
					title: '图片生成中...',
				});
				
				// 将图片写入画布
				const ctx = uni.createCanvasContext('myCanvas');
				ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H);
				ctx.draw(true, () => {
					// 获取画布要裁剪的位置和宽度   均为百分比 * 画布中图片的宽度    保证了在微信小程序中裁剪的图片模糊  位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
					var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W;
					var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H;
					var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W;
					var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H;
					uni.canvasToTempFilePath({
						x: canvasL,
						y: canvasT,
						width: canvasW,
						height: canvasH,
						destWidth: canvasW,
						destHeight: canvasH,
						quality: 0.5,
						canvasId: 'myCanvas',
						success: function (res) {
							uni.hideLoading();
							// 成功获得地址的地方
							var tempFacePath = res.tempFilePath;
						
							// 截取到新的像以后,就进行上传
								uni.showLoading({
									mask: true
								})
								
								uni.uploadFile({
								url: _this.serverUrl + '/user/uploadFace?userId=' + _this.globalUser.id + '&qq=466481615',
								filePath: tempFacePath,
								header: {
									"headerUserId": _this.globalUser.id,
									"headerUserToken": _this.globalUser.userUniqueToken
								},
								name: 'file',
								success: (res) => {
									// 注意,这里获得是一个string,需要转换一下
									var resDataStr = res.data;
									var resData = JSON.parse(resDataStr);
									
									if (resData.status == 200) {
										uni.hideLoading();
										var userInfo = resData.data;
										uni.setStorageSync('globalUser', userInfo);
										uni.navigateBack({
											delta: 1
										});
									} else if (resData.status == 500 || resData.status == 502) {
										uni.showToast({
											title: res.data.msg,
											image: "../../static/icos/error.png",
											duration: 2000
										})
									}
								},
								complete() {
									uni.hideLoading();
								}
							});

						}
					});
				});
			},

			// 设置大小的时候触发的touchStart事件
			dragStart(e) {
				T_PAGE_X = e.touches[0].pageX
				T_PAGE_Y = e.touches[0].pageY
				CUT_L = this.cutL
				CUT_R = this.cutR
				CUT_B = this.cutB
				CUT_T = this.cutT
			},

			// 设置大小的时候触发的touchMove事件
			dragMove(e) {
				var _this = this
				var dragType = e.target.dataset.drag
				switch (dragType) {
					case 'right':
						var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
						if (CUT_R + dragLength < 0) dragLength = -CUT_R
						this.setData({
							cutR: CUT_R + dragLength
						})
						break;
					case 'left':
						var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
						if (CUT_L - dragLength < 0) dragLength = CUT_L
						if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR)
						this.setData({
							cutL: CUT_L - dragLength
						})
						break;
					case 'top':
						var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
						if (CUT_T - dragLength < 0) dragLength = CUT_T
						if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB)
						this.setData({
							cutT: CUT_T - dragLength
						})
						break;
					case 'bottom':
						var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
						if (CUT_B + dragLength < 0) dragLength = -CUT_B
						this.setData({
							cutB: CUT_B + dragLength
						})
						break;
					case 'rightBottom':
						var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
						var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO

						if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
						if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
						let cutB = CUT_B + dragLengthY;
						let cutR = CUT_R + dragLengthX;

						this.setData({
							cutB: cutB,
							cutR: cutR
						})
						break;
					default:
						break;
				}
			}
		}
	}
</script>

<style>
	@import "faceCrop.css";
</style>

<!-- 调用组件 -->
<script>
uni.showActionSheet({
  itemList: ['查看我的头像', '从相册选择上传'],
  success(res) {
  	var index = res.tapIndex;
  	if (index == 0) {
  		// 预览头像
  		var faceArr = [];
  		faceArr.push(globalUser.faceImage);
  		uni.previewImage({
  			urls: faceArr,
  			current: faceArr[0]
  		});
  	} else if (index == 1) {
  		// 选择上传头像
  		uni.chooseImage({
  			count: 1,
  			sizeType: ['compressed'],
  			sourceType: ['album'],
  			success(res) {
  				// 获得临时路径
  				var tempFilePath = res.tempFilePaths[0];
  				// #ifdef H5
  				uni.navigateTo({
  					url: '../meFace/meFace?tempFilePath=' + tempFilePath
  				});
  				// #endif
  				// #ifndef H5
  				uni.navigateTo({
  					url: '../faceCrop/faceCrop?tempFilePath=' + tempFilePath
  				});
  				// #endif
  			}
  		});
  	}
  }
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436

选择器

  • 日期选择器
<template>
	<view class="page page-fill">
		
		<form @submit="formSubmitBirthday">
			
			<view class="page-block" style="margin-top: 20upx;">
				<picker mode="date" @change="dateChange">
					<view class="birthday">{{birthday}}</view>
				</picker>
			</view>
			<button type="primary" form-type="submit" class="submitBtn">提交</button>
			
		</form>
		
	</view>
</template>

<script>
	export default {
		data() {
			return {
				birthday: "",
				globalUser: {}
			};
		},
		onLoad() {
			var me = this;
			var globalUser = me.getGlobalUser("globalUser");
			me.globalUser = globalUser;
			me.birthday = globalUser.birthday;
		},
		methods: {
			dateChange(e) {
				this.birthday = e.detail.value;
			},
			formSubmitBirthday() {
				var me = this;
				var birthday = me.birthday;
				
				uni.request({
					url: me.serverUrl + "/user/modifyUserinfo?qq=466481615",
					data: {
						"userId": me.globalUser.id,
						"birthday": birthday
					},
					header: {
						"headerUserId": me.globalUser.id,
						"headerUserToken": me.globalUser.userUniqueToken
					},
					method: "POST",
					success(res) {
						var resData = res.data;
						// console.log(typeof(resData));
						if (resData.status == 200) {
							// 获得最新的用户数据
							var userInfo = resData.data;
							uni.setStorageSync("globalUser", userInfo);
							uni.navigateBack({
								delta: 1
							})
						} else if (resData.status == 502 || resData.status == 500) {
							uni.showToast({
								title: res.data.msg,
								image: "../../static/icos/error.png",
								duration: 2000
							})
						}
					}
				})
			}
		}
	}
</script>

<style>
/* 页面铺满屏幕 */
.page-fill {
	width:100%;
	height: 100%;
	position: absolute;
}

.birth-input {
	background-color: white;
	height: 80upx;
	line-height: 40upx;
	padding-left: 20upx;
}

.birthday {
	background-color: white;
	height: 80upx;
	padding-left: 20upx;
	padding-top: 30upx;
}

.submitBtn {
	width: 95%;
	margin-top: 40upx;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

单项选择器radio-group

  • 选择性别
<template>
	<view class="page page-fill">
		
		<form @submit="formSubmitSex">
			
			<view class="page-block" style="margin-top: 20upx;">
			
				<radio-group class="radio-sex" @change="sexChange">
					<label class="radio-single">
						<radio value="1" :checked="sex == 1"/></label>
					<label class="radio-single">
						<radio value="0" :checked="sex == 0"/></label>
				</radio-group>
				
			</view>
			
			<button type="primary" form-type="submit" class="submitBtn">提交</button>
			
		</form>
		
	</view>
</template>

<script>
	export default {
		data() {
			return {
				globalUser: {},
				sex: "-1"
			};
		},
		onLoad() {
			var me = this;
			var globalUser = me.getGlobalUser("globalUser");
			me.globalUser = globalUser;
			me.sex = globalUser.sex;
		},
		methods:{
			sexChange(e) {
				this.sex = e.detail.value;
			},
			formSubmitSex() {
				var me = this;
				var sex = me.sex;
				
				uni.request({
					url: me.serverUrl + "/user/modifyUserinfo?qq=466481615",
					data: {
						"userId": me.globalUser.id,
						"sex": sex
					},
					header: {
						"headerUserId": me.globalUser.id,
						"headerUserToken": me.globalUser.userUniqueToken
					},
					method: "POST",
					success(res) {
						var resData = res.data;
						// console.log(typeof(resData));
						if (resData.status == 200) {
							// 获得最新的用户数据
							var userInfo = resData.data;
							uni.setStorageSync("globalUser", userInfo);
							uni.navigateBack({
								delta: 1
							})
						} else if (resData.status == 502 || resData.status == 500) {
							uni.showToast({
								title: res.data.msg,
								image: "../../static/icos/error.png",
								duration: 2000
							})
						}
					}
				})
			
			}
		}
	}
</script>

<style>
/* 页面铺满屏幕 */
.page-fill {
	width:100%;
	height: 100%;
	position: absolute;
}

.radio-sex {
	display: flex;
	flex-direction: column;
}

.radio-single {
	padding: 20upx 20upx;
}

.submitBtn {
	width: 95%;
	margin-top: 40upx;
}
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106