欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  互联网

UniApp开发小程序怎么可以这么简单!

程序员文章站 2022-03-21 22:37:07
文章目录不会真的有前端不知道uniapp吧?!初试uniapp,爱了爱了前提涉及内容开发1、uniapp新建项目2、新建页面3、页面布局4、高德地图定位与pois获取5、uniapp中字体图标的使用6、微信小程分享完整页面代码项目代码获取我终于终于用上了uniapp!!!为什么发出这样的感叹呢?uniapp从诞生到现在已经有两年了吧,这两年间断断续续听到过uniapp的声音,但是一直没有兴趣去尝试下,总的原因有一下几点:1、开始阶段,相对于vscode,HbuilderX简直就是个弟弟,体验差,小b...

我终于终于用上了uniapp!!!

为什么发出这样的感叹呢?uniapp从诞生到现在已经有两年了吧,这两年间断断续续听到过uniapp的声音,但是一直没有兴趣去尝试下,总的原因有一下几点:

  • 1、开始阶段,相对于vscode,HbuilderX简直就是个弟弟,体验差,小bug多,手感不行!
  • 2、潜意识作怪,一个框架啥都想做估计啥都做好,然后就不想试;
  • 3、自己懒

不会真的有前端不知道uniapp吧?!

那么,什么是uniapp?

官网:uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台。

我:行行行,你说的都对,我们还是有突出一个“但是”;uniapp的愿景是好的,整体上来说基本符合官网的描述,但是,限制也是有的,有兴趣的小伙伴可以去官网(https://uniapp.dcloud.io/)看看,uniapp的很多api的对于不同的平台也是有兼容问题的。即使是这样,也不影响我们对uniapp的使用,因为他的确很方便,尤其是对于使用Vue的前端来说!

初试uniapp,爱了爱了

就像女朋友总是问我,你为什么喜欢我?这个题我回答不了女朋友,但是可以回答uniapp呀:

  • 1、我跟uniapp它爸Vue熟呀,这两就像一个模子(语法)刻出来的!
  • 2、作为前端,我累了,谁能实现最好的跨平台,我就爱了!
  • 3、看谁活的久点,uniapp的两个“金主”(H5、微信小程序)应该能够支撑它吧,这又是我认为其主要优势!

话不多说,先看看uniapp开发的微信小程序效果:
UniApp开发小程序怎么可以这么简单!

大家可以微信搜索“去哪吃喝玩乐呢”,搜索小程序试一试,也可以扫码:
UniApp开发小程序怎么可以这么简单!

前提

1、熟悉Vue.js开发,不会的同学可以过一遍官网(https://cn.vuejs.org/);

2、会在uniapp官网(https://uniapp.dcloud.io/)查找api和组件,也就是搜索能力;

(官网上说还要熟悉微信小程序开发,本人觉得没必要,直接开uniapp的api就可以了)

涉及内容

1、uniapp基本开发和调试;

2、uniapp开发微信小程序;

3、uniapp引用高德sdk定位、获取poi等;

4、uniapp使用字体图标;

5、微信小程序授权和分享;

开发

1、uniapp新建项目

打开HBuilderX,点击“文件”—“新建”—“项目”,根据自己的项目需求选择模板,此处我选择“uni-app项目”模板,最后点击”创建“,项目结构如图所示;
UniApp开发小程序怎么可以这么简单!
UniApp开发小程序怎么可以这么简单!

2、新建页面

在pages(视图目录)下新建index/index.vue文件,用于页面开发;

在pages.json(视图配置)下设置微信小程序的全局样式,以及index.vue页面的路由和相关样式;

具体api可以查看uni-app官网;

pages.json:

{
	"pages": [{
		"path": "pages/index/index",
		"style": {
			"navigationBarTitleText": "去哪·吃喝玩购"
		}
	}],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "去哪·吃喝玩购",
		"navigationBarBackgroundColor": "#abd8cf",
		"backgroundColor": "#FFFFFF"
	}
}

3、页面布局

开发之前,现将整体布局弄清楚,便于后面的组件化/模板化开发;(在此说明一点,由于个人偷懒和初试uniapp,因此并没有进行划分,都写在index.vue上,这样是不对的╮(╯▽╰)╭)
UniApp开发小程序怎么可以这么简单!
UniApp开发小程序怎么可以这么简单!

4、高德地图定位与pois获取

uniapp自带了地图定位的功能函数uni.getLocation(OBJECT),但是获取的位置信息不全且不适用,所以此处直接代用高德地图sdk进行定理信息相关操作。高德地图开发者的注册和sdk下载可以参考《uniapp 如何引入高德地图》文章https://www.jianshu.com/p/7935272776b1;

下载好高德地图的sdk(amap-wx.js)后将其放在目录components下;页面中引入:

import amap from '../../components/amap-wx.js'

微信小程序对于很多功能的调用都要求用户授权,地理位置也需要授权,下列代码进行授权和地理信息获取:

		methods: {
			//授权获取地理位置信息
			getAuthorizeInfo() {
				uni.authorize({  //调用授权操作
					scope: 'scope.userLocation',
					success: () => { // 允许授权
						this.getLocationInfo();
					},
					fail: () => { // 拒绝授权
						this.openConfirm();
						console.log("你拒绝了授权,无法获得周边信息")
					}
				})
			},
			// 获取地理位置
			getLocationInfo() {
				this.amapPlugin.getRegeo({ //调用高德地图sdk进行位置信息获取
					success: (res) => { //获取地理位置信息成功
						console.log("lbs", res)
						//地址,用于显示
						this.address = res[0].regeocodeData.formatted_address;
						//经纬度,用户查询周边吃喝玩乐信息
						this.lat = res[0].latitude;
						this.lng = res[0].longitude;
					},
					fail: (error) => { //获取地理位置信息失败
						console.log(error)
						//弹窗提示获取信息失败
						uni.showToast({
							title: '无法获得周边信息' + error,
							icon: 'none',
							duration: 2000
						})
					}
				});
			},
			//获取周边poi数据
			getPoiInfoAround() {
				uni.showLoading({
					title: '获取数据中'
				});
				//按照参数形式拼接经纬度
				let loc = this.lat + "," + this.lng
				//调用高德sdk获取周围的pois信息
				this.amapPlugin.getPoiAround({
					"location": loc,
					"querytypes": this.searchPoiType, //查询的pois类型(吃喝玩乐)
					"querykeywords": this.searchPoiKw, //关键字
					success: (data) => { //获取成功信息存储
						//成功回调
						this.searchPois = data.poisData
						this.setShowPois()
						uni.hideLoading();
						console.log("poi", this.searchPois)
					},
					fail: (info) => {  //获取失败信息说明
						//失败回调
						console.log(info)
						uni.hideLoading();
						uni.showToast({
							title: '无法获得周边信息,您可以手动添加内容',
							icon: 'none',
							duration: 2000
						})
					}
				})
			},
			// 再次获取授权
			// 当用户第一次拒绝后再次请求授权
			openConfirm() {
				uni.showModal({
					title: '请求授权当前位置',
					content: '需要获取您的地理位置,请确认授权',
					success: (res) => {
						if (res.confirm) {
							uni.openSetting(); // 打开地图权限设置
						} else if (res.cancel) {
							uni.showToast({
								title: '你拒绝了授权,无法获得周边信息',
								icon: 'none',
								duration: 2000
							})
						}
					}
				});
			},
		}

5、uniapp中字体图标的使用

字体图标是现在用的比较多的一种图标形式,体积小,适用于小的图片/图标的显示,并且颜色和大小都是可设置的,和设置字体的方式相仿;现在用的比较多的是阿里巴巴矢量图标库,iconfont的具体下载和使用方式大家自行百度,此处不做具体说明。可参考如何在uni-app使用iconfont

下载好iconfont文件后解压,放到static/fonticon目录下:
UniApp开发小程序怎么可以这么简单!

使用也比较简单(例如定位图标):

1、在main.js中引入iconfont图标:

import "./static/fonticon/iconfont.css"

2、在页面样式中使用:

<text class="iconfont icon-icon-test"></text>

**注意:**fonticon文件夹下的iconfont.js文件一定要删除,因为在微信小程序(uniapp)上线发布后iconfont的js文件会导致,线上发布版本运行报错且页面白屏;

6、微信小程分享

小程序中用户点击分享后,在 js 中定义 onShareAppMessage 处理函数(和 onLoad 等生命周期函数同级),设置该页面的分享信息;

		onShareAppMessage(res) {
			if (res.from === 'button') { // 来自页面内分享按钮
				console.log(res.target)
			}
			return {
				title: '分享去哪·吃喝玩乐小程序',
				path: '/pages/index/index'
			}
		}

完整页面代码

至此,已经介绍了该小程序开发中的一些主要功能点和应该注重的地方,接下来将页面代码放上来,里面的注释还算比较多,供大家自行学习:

<template>
	<view class="container">
		<view class="location">
			<text class="iconfont icon-icon-test icon-style"></text>
			<text class="address-text">{{ showAddress }}</text>
		</view>
		<view class="theme">
			<view class="sub-theme" @click="getPois(1)">
				<view class="item-style" :style="activeTheme==1 ? activeThemeStyle:''">
					吃
				</view>
			</view>
			<view class="sub-theme" @click="getPois(2)">
				<view class="item-style" :style="activeTheme==2 ? activeThemeStyle:''">
					喝
				</view>
			</view>
			<view class="sub-theme" @click="getPois(3)">
				<view class="item-style" :style="activeTheme==3 ? activeThemeStyle:''">
					玩
				</view>
			</view>
			<view class="sub-theme" @click="getPois(4)">
				<view class="item-style" :style="activeTheme==4 ? activeThemeStyle:''">
					购
				</view>
			</view>
		</view>
		<view class="pois-show">
			<scroll-view :scroll-top="scrollTop" scroll-y="true" class="scroll-y">
				<view class="item" v-for="poi in showPois" :key="poi.id">
					<text class="name-style">{{ poi.name }} </text>
					<text class="iconfont icon-icon_huabanfuben del-style" @click="delPoi(poi.id)"></text>
				</view>
			</scroll-view>
			<view class="limit-pois">
				<view class="limit-item" :style="activeLimit==5 ? activeLimitStyle:''" @click="changeShowPois(5)">
					5
				</view>
				<view class="limit-item" :style="activeLimit==10 ? activeLimitStyle:''" @click="changeShowPois(10)">
					10
				</view>
				<view class="limit-item" :style="activeLimit==15 ? activeLimitStyle:''" @click="changeShowPois(15)">
					15
				</view>
				<view class="limit-item" :style="activeLimit==20 ? activeLimitStyle:''" @click="changeShowPois(20)">
					20
				</view>
				<view class="limit-item" :style="activeLimit==0 ? activeLimitStyle:''" @click="changeShowPois(0)">
					清除
				</view>
			</view>
			<view class="user-defined">
				<input class="uni-input" @input="onKeyInput" :value="inputValue" placeholder="自定义添加" />
				<text class="iconfont icon-xinzeng add-style" @click="addItem"></text>
			</view>
		</view>
		<view class="touch-decision">
			<view class="tip-text" :animation="animationData">
				<view class="text-left">不知如何选择?</view>
				<view :animation="animationData" class="iconfont icon-choujiang zhanpan" @click="selectPoi">
				</view>
				<view class="text-right">点击我帮你决定!</view>
			</view>
		</view>
		<view v-if="isShowRandom" class="zp-mask"></view>
		<view v-if="isShowRandom" class="zp-style">
			{{ showResult }}
		</view>
		<view v-if="isShowRandom" class="iconfont icon-guanbi zp-close" @click="closeMask"></view>
	</view>
</template>

<script>
	import amap from '../../components/amap-wx.js'
	export default {
		data() {
			return {
				amapPlugin: null, //new地图
				key: '···', //对应wx小程序的高德地图key
				address: '', //详细地址
				lat: '', //纬度
				lng: '', //经度
				searchPoiType: "", //根据类型搜索poiß
				searchPoiKw: "", //根据关键词搜索poi
				searchPois: [], //存储获取的poi数据
				showPois: [], //用于显示的poi
				activeTheme: 1, //默认激活第一个主题
				poiType: {
					1: "050100|050200|050300|050400|050800|050900", //吃相关
					2: "050500|050600|050700", //喝相关
					3: "080100|080300|080400|080500|080600", //休闲娱乐
					4: "060100|060200|060400|060900|061000|061400" //购物
				},
				activeLimit: 10,
				addItemValue: {},
				inputValue: "",
				animationData: {},
				prizeIndex: 0, // 下一次操作的奖品,预先准备好
				isShowRandom: false,
				showResult: ""
			}
		},
		computed: {
			activeThemeStyle() {
				return "background-color:#abd8cf;color:#d2000f"
			},
			activeLimitStyle() {
				console.log(this.activeLimit)
				return "background-color:#d2000f;color:#abd8cf"
			},
			showAddress() {
				let index = this.address.indexOf("市")
				return this.address.substring(index + 1)
			}
		},
		onReady() {
			console.log("index onReady")
			//new实例化高德sdk
			this.amapPlugin = new amap.AMapWX({
				key: this.key
			});
			//默认初始化poi搜索的类型为吃
			this.searchPoiType = this.poiType[this.activeTheme]
			this.getAuthorizeInfo();

			console.log("index onshow")
			//初始化动画
			this.animation = uni.createAnimation({
				timingFunction: "ease-in-out",
			})
			//使用动画循环
			setInterval(() => {
				this.animation.scale(1.07 + Math.random() * 0.01).step({
					duration: 1000
				})
				this.animation.scale(1).step({
					duration: 1000
				})
				this.animationData = this.animation.export()
			}, 2500)
		},
		onShareAppMessage(res) {
			if (res.from === 'button') { // 来自页面内分享按钮
				console.log(res.target)
			}
			return {
				title: '分享去哪·吃喝玩乐小程序',
				path: '/pages/index/index'
			}
		},
		watch: {
			//当经纬度变化时重新获取poi
			lat() {
				this.getPoiInfoAround()
			}
		},
		methods: {
			//授权获取地理位置信息
			getAuthorizeInfo() {
				uni.authorize({
					scope: 'scope.userLocation',
					success: () => { // 允许授权
						this.getLocationInfo();
					},
					fail: () => { // 拒绝授权
						this.openConfirm();
						console.log("你拒绝了授权,无法获得周边信息")
					}
				})
			},
			// 获取地理位置
			getLocationInfo() {
				this.amapPlugin.getRegeo({
					success: (res) => {
						console.log("lbs", res)
						this.address = res[0].regeocodeData.formatted_address;
						this.lat = res[0].latitude;
						this.lng = res[0].longitude;
					},
					fail: (error) => {
						console.log(error)
						uni.showToast({
							title: '无法获得周边信息' + error,
							icon: 'none',
							duration: 2000
						})
					}
				});
			},
			//获取周边poi数据
			getPoiInfoAround() {
				uni.showLoading({
					title: '获取数据中'
				});
				let loc = this.lat + "," + this.lng
				this.amapPlugin.getPoiAround({
					"location": loc,
					"querytypes": this.searchPoiType,
					"querykeywords": this.searchPoiKw,
					success: (data) => {
						//成功回调
						this.searchPois = data.poisData
						this.setShowPois()
						uni.hideLoading();
						console.log("poi", this.searchPois)
					},
					fail: (info) => {
						//失败回调
						console.log(info)
						uni.hideLoading();
						uni.showToast({
							title: '无法获得周边信息,您可以手动添加内容',
							icon: 'none',
							duration: 2000
						})
					}
				})
			},
			// 再次获取授权
			// 当用户第一次拒绝后再次请求授权
			openConfirm() {
				uni.showModal({
					title: '请求授权当前位置',
					content: '需要获取您的地理位置,请确认授权',
					success: (res) => {
						if (res.confirm) {
							uni.openSetting(); // 打开地图权限设置
						} else if (res.cancel) {
							uni.showToast({
								title: '你拒绝了授权,无法获得周边信息',
								icon: 'none',
								duration: 2000
							})
						}
					}
				});
			},
			getPois(index) {
				this.activeTheme = index
				this.searchPoiType = this.poiType[this.activeTheme]
				uni.showLoading({
					title: '加载中'
				});
				setTimeout(function() {
					uni.hideLoading();
				}, 100);
				this.getPoiInfoAround()
			},
			delPoi(id) {
				console.log("删除poi", id)
				let temp = this.showPois
				for (let index = 0; index < temp.length; index++) {
					let poi = temp[index]
					if (poi.id == id) {
						this.showPois.splice(index, 1)
					}
				}
			},
			setShowPois() {
				this.showPois = this.searchPois.slice(0, this.activeLimit)
			},
			changeShowPois(index) {
				uni.showLoading({
					title: '加载中'
				});
				setTimeout(function() {
					uni.hideLoading();
				}, 100);
				this.activeLimit = index;
				this.setShowPois()
			},
			onKeyInput(e) {
				let ip = e.target.value
				this.inputValue = ip
				if (ip) {
					this.addItemValue["name"] = ip
					this.addItemValue["id"] = new Date().valueOf().toString()
				} else {
					this.addItemValue = {}
				}
			},
			addItem() {
				if (Object.keys(this.addItemValue).length == 0) {
					uni.showToast({
						title: '请先输入自定义内容',
						icon: 'none',
						duration: 1000
					})
				} else {
					this.showPois.push(this.addItemValue)
					uni.showLoading({
						title: '添加中'
					});
					setTimeout(function() {
						uni.hideLoading();
					}, 100);
					this.addItemValue = {}
					this.inputValue = ""
				}
			},
			selectPoi() {
				this.isShowRandom = true
				let range = this.showPois.length
				this.prizeIndex = Math.floor(Math.random() * range);

				let index = 0
				let sit = setInterval(() => {
					if (index >= this.showPois.length) {
						index = 0
					}
					this.showResult = this.showPois[index]["name"]
					console.log(this.showResult)
					index += 1
				}, 100)
				setTimeout(() => {
					clearInterval(sit)
					this.showResult = this.showPois[this.prizeIndex]["name"]
				}, 3500)
			},
			closeMask() {
				this.isShowRandom = false

			}
		}
	}
</script>

<style lang="less">
	.container {
		height: 100vh;
		width: 100vw;
		background-image: url('data:image/png;base64,···'); //小程序对于图片大小有限制,此处背景图转换为为base64
		background-repeat: no-repeat;
		background-size: 100% 100%;
		display: flex;
		flex-direction: column;
		justify-content: flex-start;
		align-items: center;
		opacity: 0.9;

		.location {
			height: 60upx;
			width: 100%;
			margin-top: 7%;
			display: flex;
			flex-direction: row;
			justify-content: center;
			align-items: center;
			font-size: 28upx;
			color: #ffffff;

			.icon-style {
				color: #ffffff;
				font-size: 30upx;
				font-weight: bolder;
				margin-right: 5upx;
			}

			.address-text {
				width: 60%;
				overflow: hidden;
				text-overflow: ellipsis;
				white-space: nowrap; //文本不换行,这样超出一行的部分被截取,显示...
			}
		}

		.theme {
			height: 110upx;
			width: 100%;
			margin-top: 30upx;
			display: flex;
			flex-direction: row;
			justify-content: center;

			.sub-theme {
				width: 100upx;
				height: 100upx;
				border-radius: 50%;
				border: 5upx solid #d2000f;
				padding: 4upx;
				margin: 0 15upx;
				box-sizing: border-box;

				.item-style {
					border-radius: 50%;
					background-color: #d2000f;
					width: 100%;
					height: 100%;
					font-size: 45upx;
					color: #ffffff;
					align-items: center;
					margin: 0 auto;
					display: flex;
					justify-content: center;
				}
			}
		}

		.pois-show {
			height: 700upx;
			width: 80%;
			background-image: url(/static/images/pois_bg.png);
			background-size: 100% 100%;
			margin-top: 30upx;
			padding: 20upx 30upx;

			.scroll-y {
				height: 75%;
				width: 100%;
				margin-top: 28upx;
				padding: 5upx 0 5upx 0;
				border-bottom: 1upx solid #d2000f;
				border-top: 1upx solid #d2000f;

				.item {
					height: 50upx;
					line-height: 50upx;
					margin: 2upx 30upx;
					border: 1upx solid #d2000f;
					border-radius: 30upx;
					font-size: 25upx;
					text-align: center;
					display: flex;
					flex-direction: row;
					justify-content: center;
					align-items: center;
					color: #d2000f;
					background-color: #abd8cf;
					opacity: 0.8;

					.name-style {
						width: 90%;
						overflow: hidden;
						text-overflow: ellipsis;
						white-space: nowrap;
					}

					.del-style {
						margin-right: 10upx;
						font-size: 30upx;
						font-weight: bolder;
					}
				}
			}

			.limit-pois {
				display: flex;
				flex-direction: row;
				justify-content: center;
				width: 100%;

				.limit-item {
					margin: 10upx;
					height: 45upx;
					line-height: 45upx;
					width: 15%;
					border-radius: 20upx;
					border: 1upx solid #d2000f;
					background-color: #abd8cf;
					opacity: 0.9;
					color: #d2000f;
					text-align: center;
					font-size: 25upx;
				}
			}

			.user-defined {
				display: flex;
				flex-direction: row;
				justify-content: center;
				margin-top: 10upx;

				.uni-input {
					height: 50upx;
					border: 5upx solid #abd8cf;
					border-radius: 20upx;
					color: #ffffff;
					margin-right: 10upx;
					font-size: 30upx;
					background-color: #d2000f;
					padding: 0upx 15upx;
					overflow: hidden;
					text-overflow: ellipsis;
					word-wrap: nowrap;
				}

				.add-style {
					width: 10%;
					line-height: 50upx;
					text-align: center;
					font-size: 35upx;
					font-weight: bolder;
					color: #fff;
					border: 5upx solid #abd8cf;
					border-radius: 5upx;
					background-color: #d2000f;

				}
			}
		}

		.touch-decision {
			margin-top: 10upx;
			display: flex;
			flex-direction: column;
			justify-content: center;
			align-items: center;

			.tip-text {
				width: 100%;
				display: flex;
				flex-direction: row;
				justify-content: space-around;
				align-items: center;
				margin-top: 5upx;

				.text-left {
					color: #fffe0e;
					height: 50upx;
					line-height: 50upx;
					font-size: 30upx;
					font-weight: bolder;
					border: 5upx solid #ffffff;
					background-color: #d2000f;
					margin-right: -5upx;
					border-right: none;
					padding-right: 10upx;
				}

				.text-right {
					color: #fffe0e;
					height: 50upx;
					line-height: 50upx;
					font-size: 30upx;
					font-weight: bolder;
					border: 5upx solid #ffffff;
					background-color: #d2000f;
					margin-left: -5upx;
					border-left: none;
					padding-left: 10upx;
				}

				.zhanpan {
					color: #abd8cf;
					font-size: 90upx;
					line-height: 90upx;
					border: 5upx solid #ffffff;
					border-radius: 50%;
					width: 90upx;
					height: 90upx;
				}
			}


		}

		.zp-mask {
			position: fixed;
			height: 100%;
			width: 100%;
			background-color: rgba(255, 255, 255, 0.6);
			z-index: 10;
		}

		.zp-style {
			display: fixed;
			width: 600upx;
			height: 60upx;
			background-color: #d2000f;
			margin-top: -100%;
			z-index: 20;
			opacity: 0.8;
			font-size: 40upx;
			color: #ffffff;
			border-radius: 40upx;
			text-align: center;
			padding-top: 10upx;
		}

		.zp-close {
			display: fixed;
			width: 100upx;
			height: 100upx;
			color: #d2000f;
			margin-top: 15%;
			z-index: 25;
			opacity: 0.8;
			font-size: 80upx;
			text-align: center;
		}
	}
</style>

项目代码获取

关注微信公众号:迟亦早(chiyizao)

发送:”wherewego“获取

本文地址:https://blog.csdn.net/qq_31967985/article/details/109272307