基于uniCloud的性格测试小程序【开源】

5/26/2023

最近研究了uniCloud (opens new window),并用uniCloud开发了一个性格测试小程序,已经发布到服务器,完美的发布到抖音,在抖音可以搜索到该小程序。

源码和视频教程都开源给大家了。小孟已经发布到抖音,全部的已经跑通,所以小伙伴可以拿去学习,没有任何的问题。

也出了详细的教程了!视频的教程放在b站了。https://www.bilibili.com/video/BV19u411z7Jr?spm_id_from=333.999.0.0

1

# 一,什么是uniCloud:

uniCloud是uni-app (opens new window)的云开发库,是 DCloud 联合阿里云、腾讯云,为开发者提供的基于 serverless 模式和 js 编程的云开发平台。

uniCloud基本抹平了不同云厂商的差异,如果你熟悉mongoDB,那么就很容易上手了!

现在用uniCloud可以大大的提高开发的效率,而且可以降低运维方面的成本,因为现在serverless还处于免费阶段。

当然也有很多的缺点,新手上手的话不是很快,很多东西也不支持,例如删除数据,竟然还要去调api。

最近小孟和老王研究了下,感觉还是可以搞的,于是就测试并发布了整个通用的版本,发布到了抖音,这个大家可以搞一下,抖音的流量你懂的,但是我们现在对赚钱不感兴趣。所以有小伙伴想搞的,可以拿去源码学习,但是不要商用和贩卖!

# 二,系统界面截图:

2

3

4

5

7

8

9

11

12

13

# 三,核心代码演示:

<template>
	<view class="center">
		<view class="userInfo" @click.capture="toUserInfo">
			<uni-file-picker v-if="userInfo.avatar_file" v-model="userInfo.avatar_file"
				fileMediatype="image" :del-icon="false" return-type="object" :image-styles="listStyles" disablePreview
				disabled />
			<image v-else class="logo-img" src="/static/uni-center/defaultAvatarUrl.png"></image>
			<view class="logo-title">
				<text class="uer-name">{{userInfo.nickname||userInfo.username||userInfo.mobile||'未登录'}}</text>
			</view>
		</view>
		<uni-list class="center-list" v-for="(sublist , index) in ucenterList" :key="index">
			<uni-list-item v-for="(item,i) in sublist" :title="item.title" link :rightText="item.rightText" :key="i"
				:clickable="true" :to="item.to" @click="ucenterListClick(item)" :show-extra-icon="true"
				:extraIcon="{type:item.icon,color:'#999'}">
				<view v-if="item.showBadge" class="item-footer" slot="footer">
					<text class="item-footer-text">{{item.rightText}}</text>
					<view class="item-footer-badge"></view>
				</view>
			</uni-list-item>
		</uni-list>
	</view>
</template>

<script>
	import {
		mapGetters,
		mapMutations
	} from 'vuex';
	import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
	import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version';
	import uniShare from 'uni_modules/uni-share/js_sdk/uni-share.js';

	const db = uniCloud.database();
	export default {
		data() {
			return {
				ucenterList: [
					[{
						"title": '问题与反馈',
						"to": '/uni_modules/uni-feedback/pages/uni-feedback/uni-feedback',
						"icon": "help"
					}, {
						"title": '设置',
						"to": '/pages/ucenter/settings/settings',
						"icon": "gear"
					}],
					[{
						"title": '关于',
						"to": '/pages/ucenter/about/about',
						"icon": "info"
					}]
				],
				listStyles: {
					"height": "150rpx", // 边框高度
					"width": "150rpx", // 边框宽度
					"border": { // 如果为 Boolean 值,可以控制边框显示与否
						"color": "#eee", // 边框颜色
						"width": "1px", // 边框宽度
						"style": "solid", // 边框样式
						"radius": "100%" // 边框圆角,支持百分比
					}
				}
			}
		},
		onLoad() {
			//#ifdef APP-PLUS
			this.ucenterList[this.ucenterList.length - 2].unshift({
				title: '检查更新',
				rightText: this.appVersion.version + '-' + this.appVersion.versionCode,
				event: 'checkVersion',
				icon: 'loop',
				showBadge: this.appVersion.hasNew
			})
			//#endif
		},
		computed: {
			...mapGetters({
				userInfo: 'user/info',
				login: 'user/hasLogin'
			})
			// #ifdef APP-PLUS
			,
			appVersion() {
				return getApp().appVersion
			}
			// #endif
			,
			appConfig() {
				return getApp().globalData.config
			}
		},
		methods: {
			...mapMutations({
				setUserInfo: 'user/login'
			}),
			toSettings() {
				uni.navigateTo({
					url: "/pages/ucenter/settings/settings"
				})
			},
			/**
			 * 个人中心项目列表点击事件
			 */
			ucenterListClick(item) {
				if (!item.to && item.event) {
					this[item.event]();
				}
			},
			async checkVersion() {
				let res = await callCheckVersion()
				console.log(res);
				if (res.result.code > 0) {
					checkUpdate()
				} else {
					uni.showToast({
						title: res.result.message,
						icon: 'none'
					});
				}
			},
			toUserInfo() {
				uni.navigateTo({
					url: '/pages/ucenter/userinfo/userinfo'
				})
			},
			/**
			 * 去应用市场评分
			 */
			gotoMarket() {
				// #ifdef APP-PLUS
				if (uni.getSystemInfoSync().platform == "ios") {
					// 这里填写appstore应用id
					let appstoreid = this.appConfig.marketId.ios; // 'id1417078253';
					plus.runtime.openURL("itms-apps://" + 'itunes.apple.com/cn/app/wechat/' + appstoreid + '?mt=8');
				}
				if (uni.getSystemInfoSync().platform == "android") {
					var Uri = plus.android.importClass("android.net.Uri");
					var uri = Uri.parse("market://details?id=" + this.appConfig.marketId.android);
					var Intent = plus.android.importClass('android.content.Intent');
					var intent = new Intent(Intent.ACTION_VIEW, uri);
					var main = plus.android.runtimeMainActivity();
					main.startActivity(intent);
				}
				// #endif
			},
			async share() {
				let {result} = await uniCloud.callFunction({
					name: 'uni-id-cf',
					data: {
						action: 'getUserInviteCode'
					}
				})
				console.log(result);
				let myInviteCode = result.myInviteCode || result.userInfo.my_invite_code
				console.log(myInviteCode);
				let {
					appName,
					logo,
					company,
					slogan
				} = this.appConfig.about
				// #ifdef APP-PLUS
				uniShare({
					content: { //公共的分享类型(type)、链接(herf)、标题(title)、summary(描述)、imageUrl(缩略图)
						type: 0,
						href: this.appConfig.h5.url +
							`/#/pages/ucenter/invite/invite?code=${myInviteCode}`,
						title: appName,
						summary: slogan,
						imageUrl: logo + '?x-oss-process=image/resize,m_fill,h_100,w_100' //压缩图片解决,在ios端分享图过大导致的图片失效问题
					},
					menus: [{
							"img": "/static/app-plus/sharemenu/wechatfriend.png",
							"text": "微信好友",
							"share": {
								"provider": "weixin",
								"scene": "WXSceneSession"
							}
						},
						{
							"img": "/static/app-plus/sharemenu/wechatmoments.png",
							"text": "",
							"share": {
								"provider": "weixin",
								"scene": "WXSenceTimeline"
							}
						},
						{
							"img": "/static/app-plus/sharemenu/weibo.png",
							"text": "微博",
							"share": {
								"provider": "sinaweibo"
							}
						},
						{
							"img": "/static/app-plus/sharemenu/qq.png",
							"text": "QQ",
							"share": {
								"provider": "qq"
							}
						},
						{
							"img": "/static/app-plus/sharemenu/copyurl.png",
							"text": "复制",
							"share": "copyurl"
						},
						{
							"img": "/static/app-plus/sharemenu/more.png",
							"text": "更多",
							"share": "shareSystem"
						}
					],
					cancelText: "取消分享",
				}, e => { //callback
					console.log(e);
				})
				// #endif
			}
		}
	}
</script>

<style>
	/* #ifndef APP-PLUS-NVUE */
	page {
		background-color: #f8f8f8;
	}

	/* #endif*/

	.center {
		flex: 1;
		flex-direction: column;
		background-color: #f8f8f8;
	}

	.userInfo {
		width: 750rpx;
		padding: 20rpx;
		padding-top: 50px;
		background-image: url(../../static/uni-center/headers.png);
		flex-direction: column;
		align-items: center;
	}

	.logo-img {
		width: 150rpx;
		height: 150rpx;
		border-radius: 150rpx;
	}

	.logo-title {
		flex: 1;
		align-items: center;
		justify-content: space-between;
		flex-direction: row;
	}

	.uer-name {
		height: 100rpx;
		line-height: 100rpx;
		font-size: 38rpx;
		color: #FFFFFF;
	}

	.center-list {
		margin-bottom: 30rpx;
		background-color: #f9f9f9;
	}

	.center-list-cell {
		width: 750rpx;
		background-color: #007AFF;
		height: 40rpx;
	}

	.grid {
		background-color: #FFFFFF;
		margin-bottom: 6px;
	}

	.uni-grid .text {
		font-size: 30rpx;
		height: 25px;
		line-height: 25px;
		color: #817f82;
	}

	.uni-grid .item /deep/ .uni-grid-item__box {
		justify-content: center;
		align-items: center;
	}


	/*修改边线粗细示例*/
	/* #ifndef APP-NVUE */
	.center-list /deep/ .uni-list--border:after {
		-webkit-transform: scaleY(0.2);
		transform: scaleY(0.2);
		margin-left: 80rpx;
	}

	.center-list /deep/ .uni-list--border-top,
	.center-list /deep/ .uni-list--border-bottom {
		display: none;
	}

	/* #endif */
	.item-footer {
		flex-direction: row;
		align-items: center;
	}

	.item-footer-text {
		color: #999;
		font-size: 24rpx;
		padding-right: 10rpx;
	}

	.item-footer-badge {
		width: 20rpx;
		height: 20rpx;
		/* #ifndef APP-NVUE */
		border-radius: 50%;
		/* #endif */
		/* #ifdef APP-NVUE */
		border-radius: 10rpx;
		/* #endif */
		background-color: #DD524D;
	}
</style>


<script>
 import uQRCode from './uqrcode.js'

 export default {
  props: {
   cid: {
    type: String,
    default(){
     return Date.now()+Math.random()+'';
    }
   },
   text: {
    type: String,
    required: true
   },
   size: {
    type: Number,
    default: uni.upx2px(200)
   },
   margin: {
    type: Number,
    default: 0
   },
   backgroundColor: {
    type: String,
    default: '#ffffff'
   },
   foregroundColor: {
    type: String,
    default: '#000000'
   },
   backgroundImage: {
    type: String
   },
   logo: {
    type: String
   },
   makeOnLoad: {
    type: Boolean,
    default: false
   }
  },
  data() {
   return {

   }
  },
  mounted() {
   if (this.makeOnLoad) {
    this.make()
   }
  },
  methods: {
   async make() {
    var options = {
     canvasId: this.cid,
     componentInstance: this,
     text: this.text,
     size: this.size,
     margin: this.margin,
     backgroundColor: this.backgroundImage ? 'rgba(255,255,255,0)' : this.backgroundColor,
     foregroundColor: this.foregroundColor
    }
    var filePath = await this.makeSync(options)

    if (this.backgroundImage) {
     filePath = await this.drawBackgroundImageSync(filePath)
    }

    if (this.logo) {
     filePath = await this.drawLogoSync(filePath)
    }

    this.makeComplete(filePath)
   },
   makeComplete(filePath) {
    this.$emit('makeComplete', filePath)
   },
   drawBackgroundImage(options) {
    var ctx = uni.createCanvasContext(this.cid, this)

    ctx.drawImage(this.backgroundImage, 0, 0, this.size, this.size)

    ctx.drawImage(options.filePath, 0, 0, this.size, this.size)

    ctx.draw(false, () => {
     uni.canvasToTempFilePath({
      canvasId: this.cid,
      success: res => {
       options.success && options.success(res.tempFilePath)
      },
      fail: error => {
       options.fail && options.fail(error)
      }
     }, this)
    })
   },
   async drawBackgroundImageSync(filePath) {
    return new Promise((resolve, reject) => {
     this.drawBackgroundImage({
      filePath: filePath,
      success: res => {
       resolve(res)
      },
      fail: error => {
       reject(error)
      }
     })
    })
   },
   fillRoundRect(ctx, r, x, y, w, h) {
    ctx.save()
    ctx.translate(x, y)
    ctx.beginPath()
    ctx.arc(w - r, h - r, r, 0, Math.PI / 2)
    ctx.lineTo(r, h)
    ctx.arc(r, h - r, r, Math.PI / 2, Math.PI)
    ctx.lineTo(0, r)
    ctx.arc(r, r, r, Math.PI, Math.PI * 3 / 2)
    ctx.lineTo(w - r, 0)
    ctx.arc(w - r, r, r, Math.PI * 3 / 2, Math.PI * 2)
    ctx.lineTo(w, h - r)
    ctx.closePath()
    ctx.setFillStyle('#ffffff')
    ctx.fill()
    ctx.restore()
   },
   drawLogo(options) {
    var ctx = uni.createCanvasContext(this.cid, this)

    ctx.drawImage(options.filePath, 0, 0, this.size, this.size)

    var logoSize = this.size / 4
    var logoX = this.size / 2 - logoSize / 2
    var logoY = logoX

    var borderSize = logoSize + 10
    var borderX = this.size / 2 - borderSize / 2
    var borderY = borderX
    var borderRadius = 5

    this.fillRoundRect(ctx, borderRadius, borderX, borderY, borderSize, borderSize)

    ctx.drawImage(this.logo, logoX, logoY, logoSize, logoSize)

    ctx.draw(false, () => {
     uni.canvasToTempFilePath({
      canvasId: this.cid,
      success: res => {
       options.success && options.success(res.tempFilePath)
      },
      fail: error => {
       options.fail && options.fail(error)
      }
     }, this)
    })
   },
   async drawLogoSync(filePath) {
    return new Promise((resolve, reject) => {
     this.drawLogo({
      filePath: filePath,
      success: res => {
       resolve(res)
      },
      fail: error => {
       reject(error)
      }
     })
    })
   },
   async makeSync(options) {
    return new Promise((resolve, reject) => {
     uQRCode.make({
      canvasId: options.canvasId,
      componentInstance: options.componentInstance,
      text: options.text,
      size: options.size,
      margin: options.margin,
      backgroundColor: options.backgroundColor,
      foregroundColor: options.foregroundColor,
      success: res => {
       resolve(res)
      },
      fail: error => {
       reject(error)
      }
     })
    })
   }
  }
 }
</script>

# 四,代码获取:

这个系统小孟也开发了很久,有小伙伴说需要学习,狠狠心,给大家开源了,拿去学习吧,关注下方公众号,输入:unicloud

spring公众号