赞
踩
目录
这是我3月-5月单人负责的一个数藏项目,巅峰时刻同时在线3w人,也算我第一个uniapp综合项目,不够目前我做的已经停运了,当初学了很多,但是缺乏整理,项目缺点也很多。我想通过这段时间把代码重新写一遍,整理一下。我不会把所有功能都重新梳理,但是重要的几样我肯定都一一梳理,而且会有增加,例如:注册、登录、一键登录、短信、抢购、转赠、支付宝支付、版本热更新、公告等
当然里面涉及的上链操作我不是很会,你们可以把这个当作一个售卖东西的App,里面不会涉及上一个公司的信息,只是单纯的整理一下目前项目主要的技术和自学时候的壁垒。
我这里使用的是Mac电脑,不影响window电脑的学习,排版可能不好看
github上面每天都在更新,如果认可,不要吝啬正反馈:GitHub - chenqi13814529300/light-ship: 复习和加强uniapp/unicloud学习
点赞收藏超过100,我将录播视频
开通空间
配置短信签名和模版
模版需要短信签名审核成功后才可以编辑模版
开通一键登录
需要购买至少3个月的服务器,然后实名认证啥的才可以,这些自己弄吧,然后我再域名解析为如下本项目使用的域名
绝大部分人都没有创建公司或者实体,所以这里的支付能力我将采用支付宝沙箱带你们一起如何使用支付宝。
1.3.2支付宝开放平台
你需要自己设置账户密码啥的,这个自己弄。
最终你可以得到几个有用的信息
appid:20210001111111111
mchid:2088621911111111
商家账户:wtesjg2746@sandbox.com
商家登录密码:111111
买家账户:qhsfny9975@sandbox.com
买家登录密码:111111
买家支付密码:111111
准备关于支付能力的密钥和证书,打开之前下的软件,点击生成应用密钥和应用公钥
方法一用阿里云公钥(二选一):
点击查看
然后把应用公钥放到里面,生成支付宝公钥
方法一有应用私钥和支付宝公钥即可进行支付测试
方法二用证书:
获取csr文件,组织/公司和域名填写商家账户
上传csr文件,然后下载3个证书
ok,支付支付能力准备好了!
这里使用的是vue2,unicloud,以及默认空模版
并关联腾讯云服务器空间
首先需要开通验证码,签名和模版都审核通过,并充值些许钱。
需要先引入uni-id组件
本次项目采用的是老版uni-id这个用起来比较顺手
引入uni-id后需要配置uni-id/config.json
config.json的配置参考官网文档uni-app官网
发送验证码——前端代码
我这边是做了发送验证码前进行判断(是否已经注册),避免浪费验证码次数
- async getCode() {
- if (this.regsiterInfo.mobile && this.isAgree) {
- this.settime()
- const sms = uniCloud.importObject("sms")
- // 正常查找到就说,该用户名已经被注册
- const isRegister = await sms.getUserByMobile(this.regsiterInfo.mobile)
- console.log(isRegister)
- if (isRegister.code == 0) {
- uni.showToast({
- title: "该用户名已经被注册",
- icon: "none"
- })
- this.countdown = 0
- }
- if (isRegister.code == -100) {
-
- const res = await sms.sendSms(this.regsiterInfo.mobile, "register")
- console.log(res)
-
- if (res.code != 0) {
- uni.showToast({
- title: "验证码发送失败",
- icon: "none"
- })
- return
- }
- uni.showToast({
- title: "验证码发送成功",
- icon: "none"
- })
- }
-
-
- } else {
- uni.showToast({
- title: "请输入手机号并且勾选协议",
- icon: "none"
- })
- }
- }
验证码冷却时间——前端代码
也是节约成本,同时减少后端请求
- settime() {
- let smsTime = null
- if (this.countdown == 0) {
- this.isdisabledFn = false
- this.isSms = "获取验证码"
- this.countdown = 60;
- clearInterval(smsTime)
- } else {
- this.isdisabledFn = true
- this.isSms = "重新发送(" + this.countdown + ")"
- this.countdown--;
- smsTime = setTimeout(() => {
- this.settime()
- }, 1000)
- }
- },
校验验证码——前端代码
参数就是手机号和验证码
- const sms = uniCloud.importObject("sms")
- const smsInfo = await sms.verifySmsCode(this.regsiterInfo.mobile, this.regsiterInfo.code,
- "register")
- console.log(smsInfo)
验证码方法——后端代码
- // 开发文档: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj
- const uniID = require('uni-id')
- const db = uniCloud.database();
- module.exports = {
- // 发送验证码
- async sendSms(mobile, type) {
- // 生成验证码可以按自己的需求来,这里以生成6位数字为例
- const randomStr = '00000' + Math.floor(Math.random() * 1000000)
- const code = randomStr.substring(randomStr.length - 4)
- const res = await uniID.sendSmsCode({
- mobile,
- templateId: "14408",
- code,
- type
- })
- return res
- },
-
- // 校验验证码
- async verifySmsCode(mobile, code, type) {
- const res = await uniID.verifyCode({
- mobile,
- code,
- type
- })
- return res
- },
-
-
- // 根据电话号码查找个人信息
- async getUserByMobile(mobile) {
- let {
- data
- } = await db.collection("uni-id-users").where({
- "mobile": mobile
- }).get()
- if (data[0]) {
- return {
- code: 0,
- msg: '查询成功',
- }
- } else {
- return {
- code: -100,
- }
- }
- }
- // 手机+短信 登录
- // 注册后的可以直接登录,如果没有注册过则去注册页面
-
- async smsLogin(mobile, code) {
- // 判断mobild是否注册了
- const sms = uniCloud.importObject("sms")
- try {
- const res = await sms.getUserByMobile(mobile)
- } catch (e) {
- // 异常则显示未注册-100
- return {
- res: e
- }
- }
-
-
-
-
- try {
- const res = await uniID.loginBySms({
- mobile,
- code
- })
- return {
- res: res
- }
- } catch (e) {
- return {
- res: e
- }
- }
-
- }
-
-
- }
手机号、密码、验证码、用户协议都填写才可以注册,上述完成后,便在云数据库中产生一条数据。
同时需要把注册的token保存下来,vuex保存下来。并跳转到登录页面
注册逻辑——前端代码
- uniCloud.callFunction({
- name: 'register',
- data,
- async success(res) {
- console.log(res)
- if (res.result.code === 0) {
- uni.showToast({
- title: '注册成功',
- icon: 'none'
- })
- // 2.8.0版本起调整为蛇形uni_id_token(调整后在一段时间内兼容驼峰uniIdToken)
- uni.setStorageSync('uni_id_token', res.result.token)
- uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
-
-
- // 把邀请码清单归到邀请码数据库之中(存在就入库不存在不执行)
- if (that.regsiterInfo.inviteCode) {
- console.log("我有邀请码奥")
- const userInfo = {
- "registerUserId": res.result.uid,
- "mobile": res.result.mobile,
- "username": res.result.username
- }
- const invite = uniCloud.importObject("invite")
- await invite.updateInvite(that.regsiterInfo.inviteCode,
- userInfo)
- }
-
- // 跳转到登录页
- setTimeout(function() {
- uni.navigateTo({
- url: "/pages/login/Login"
- })
- }, 1000)
-
-
- } else if (res.result.code == -100) {
- uni.showToast({
- title: res.result.msg,
- icon: "none"
- })
- } else {
- uni.redirectTo({
- content: res.result.message,
- showCancel: false
- })
- }
- },
- fail(res) {
- console.log(res)
- uni.showModal({
- content: '注册失败,请稍后再试',
- showCancel: false
- })
- }
- })
注册逻辑——后端代码
- // 云函数register的代码
- const uniID = require('uni-id')
- exports.main = async function(event, context) {
-
- const {
- mobile,
- password,
- inviteCode
- } = event
- // 自动验证用户名是否与已经注册的用户名重复,如果重复会直接返回错误。否则会自动生成token并加密password存储username、password、token到数据表uni-id-users,并返回如上响应参数
- const res = await uniID.register({ //支持传入任何值,比如可以直接传入mobile即可设置手机号码,切勿直接传入event否则这是一个极大的安全问题
- "username":mobile,
- "password": password,
- "mobile": mobile,
- "avatar": 'https://7463-tcb-d4ae9humf98e68-0dudeafb75fdc-1310755086.tcb.qcloud.la/head/good3.jpg',
- "goods_count": 0,
- "mobile_confirmed": 1,
- "realname_auth": 0,
- "invite_code": inviteCode,
- "create_invite_code": '',
- })
- return res
-
-
- }
注册成功!
Error: Method name required代表你创建的是云对象,不是云函数。云对象调用需要方法(例如一开始我创建的是register是云对象,一切操作符合文档结果报这个错误)
密码登录——前端代码
一个是判断是否是传统登录方式,登录成功后执行登录后的逻辑,保存token,以及记录登录时长(用于3天后清空token,登录失效,需要重新登录)
- if (this.isTradition) {
- const login = uniCloud.importObject("login")
- const loginInfo = await login.commonLogin(data)
- console.log(loginInfo)
- if (loginInfo.code == 0) {
- // 执行登录成功后的逻辑
- that.loginAfter(loginInfo)
- }
- }
-
-
-
- // 登录成功后的逻辑
- loginAfter(res) {
- // uni.closeAuthView()
- uni.showToast({
- title: '登录成功',
- icon: 'none',
- })
-
- // 保存token
- uni.setStorageSync('uni_id_token', res.token)
- uni.setStorageSync('uni_id_token_expired', res.tokenExpired)
-
- var dayAdd1 = new Date();
- dayAdd1 = dayAdd1.setDate(dayAdd1.getDate() + 1);
- dayAdd1 = new Date(dayAdd1);
- // 记入token终止日期,1天
- uni.setStorageSync('uni_id_token_end_time', dayAdd1.getTime())
- // 其他业务代码,如跳转到首页等
-
- this.setUserInfo(res.userInfo)
-
- setTimeout(function() {
- uni.switchTab({
- url: "/pages/index/Index"
- })
- }, 1000)
- },
密码登录——后端代码
- const uniID = require('uni-id')
-
- module.exports = {
-
- // 普通账号 密码登录
- async commonLogin(userInfo) {
-
- const {
- mobile,
- password
- } = userInfo
-
-
- // 自动完成mobile、password验证是否合法的逻辑
- const res = await uniID.login({
- username: mobile,
- password,
- })
-
- return res
-
- },
- // 重置密码
- async resetPwdBySms(resetInfo) {
- const {
- mobile,
- code,
- password
- } = resetInfo
-
- await uniID.resetPwdBySms({
- mobile,
- code,
- password
- })
-
- return {
- code:0
- }
- },
-
-
- }
短信登录——前端代码
短信登录其实跟注册获取验证码逻辑类似
- // 电话号码+验证码登录
- // 1.是否填写验证码
- if (that.loginInfo.code) {
- const sms = uniCloud.importObject("sms")
- const {
- res
- } = await sms.smsLogin(that.loginInfo.mobile, that.loginInfo.code)
- // res.code为-100则res.errMsg 未注册
- // 50202 errMsg "验证码错误或已失效"
- // 0 是成功的
- console.log(res)
- switch (res.code) {
- case 0:
- // 执行登录成功后的逻辑
- that.loginAfter(res)
- break;
- case -100:
- uni.showToast({
- title: res.errMsg,
- icon: 'none'
- });
- break;
- case 50202:
- uni.showToast({
- title: res.errMsg,
- icon: 'none'
- });
- break;
- case -300:
- uni.showToast({
- title: res.errMsg,
- icon: 'none'
- });
- break;
- }
- } else {
- uni.showToast({
- title: "请填写验证码",
- icon: 'none'
- })
- }
短信登录——后端代码
这部分代码,在注册的后端sms代码里面
一键登录——前端代码
目前有缺陷,ios一键登录uni.closeAuthView()不生效,是官方还没修复
- // 判断是否支持一键登录
- isAutoLogin() {
- let _that = this
- uni.getProvider({ //获取可用的服务提供商
- service: 'oauth',
- success: function(res) {
- console.log(res.provider) // ['weixin', qq', 'univerify']
- }
- });
- uni.preLogin({ //预登录
- provider: 'univerify', //用手机号登录
- success() {
- _that.autoStatus = true
- console.log('预登录成功')
- _that.fasterLogin()
- },
-
- fail(err) { //预登录失败
- _that.autoStatus = false
- _that.error = err
- console.log('错误码:' + err.errCode)
- console.log(err.errMsg)
- }
- })
- },
-
- async fasterLogin() {
-
- let that = this
-
- uni.login({
- provider: 'univerify',
- async success(res) { // 登录成功
- console.log(res.authResult); // {openid:'登录授权唯一标识',access_token:'接口返回的 token'}
- const login = uniCloud.importObject("login")
- const fastLoginRes = await login.fastLogin(res.authResult)
- console.log(fastLoginRes)
- if (fastLoginRes.code == -100) {
- uni.showToast({
- title: "请先注册",
- icon: "none"
- })
- setTimeout(function() {
- uni.closeAuthView()
- uni.navigateTo({
- url: "/pages/register/Register"
- })
- }, 1000)
-
- }
-
- if (fastLoginRes.code == 0) {
- uni.closeAuthView()
- that.loginAfter(fastLoginRes)
- }
-
- },
- fail(res) { // 登录失败
- console.log(res.errCode)
- uni.closeAuthView()
- }
- })
- },
一键登录——后端代码
type:指定操作类型,可选值为login
、register
,不传此参数时表现为手机号已注册则登录,手机号未注册则进行注册
- // 手机一键登录
- async fastLogin(userInfo) {
-
-
- const {
- access_token,
- openid
- } = userInfo
-
- const res = await uniID.loginByUniverify({
- access_token,
- openid,
- type:'login'
- })
- return res
- },
登录成功跳转到首页,并且显示登录状态和用户名
点击退出后如下
完整代码如下——前端代码
- <template>
- <view>
- <nav-bar>
- <view slot="left" class="left">启航</view>
- <view slot="right" v-if="isLogin" @click="goLogin">{{getUserInfo._id.slice(0,12)}} <text @click="outLogin"
- class="outLogin">退出</text></view>
- <view slot="right" v-else @click="goLogin">登录</view>
- </nav-bar>
- </view>
- </template>
-
- <script>
- import NavBar from '../../components/NavBar.vue'
- import {
- mapGetters,
- mapMutations
- } from "vuex";
- export default {
- components: {
- NavBar
- },
- data() {
- return {}
- },
- computed: {
- ...mapGetters(["getUserInfo"]),
- isLogin() {
- console.log(this.getUserInfo)
- if (Object.keys(this.getUserInfo).length > 0) {
- return true;
- } else {
- return false;
- }
- },
- },
- methods: {
- ...mapMutations(['setUserInfo']),
- // 去登录
- goLogin() {
- uni.navigateTo({
- url: "/pages/login/Login"
- })
- },
- // 退出登录
- outLogin() {
- this.setUserInfo({})
- uni.removeStorageSync('uni_id_token')
- uni.removeStorageSync('uni_id_token_expired')
- uni.navigateTo({
- url: "/pages/Login/Login"
- })
- },
- }
- }
- </script>
-
- <style>
-
- </style>
NavBar组件——前端代码
- <template>
- <view class="nav-bar">
- <view class="left"><slot name="left"></slot></view>
- <view class="center"><slot name="center"></slot></view>
- <view class="right"><slot name="right"></slot></view>
- </view>
-
-
- </template>
-
- <script>
- export default {
- name: "NavBar"
- }
- </script>
-
- <style scoped>
- .nav-bar {
- position: -webkit-sticky;
- position: sticky;
- top: var(--window-top);
- z-index: 99;
- height: 80rpx;
- overflow: hidden;
- line-height: 80rpx;
- background-color: #242729;
- color: white;
- }
- .left{
- float: left;
- }
- .right{
- float: right;
- }
-
-
- </style>
效果如下
点击轮播图进入图片详情
轮播图——前端代码
- <!-- 轮播图 -->
- <uni-swiper-dot :info="info" :current="current" field="content" mode="round">
- <swiper class="swiper-box" @change="change" autoplay="true">
- <swiper-item v-for="(item ,index) in info" :key="index">
- <image @click="bigImgDeatils(item)" :src="require(`@/static/lun/${item.content}`)" mode="widthFix">
- </image>
- </swiper-item>
- </swiper>
- </uni-swiper-dot>
- // 轮播图片 这里就不从后端获取了,大家应该都会
- info: [{
- content: 'lun2.jpeg',
- big: 'lun2-big.jpeg'
- },
- {
- content: 'lun1.jpeg',
- big: 'lun1-big.jpeg'
- },
-
- ],
- current: 0,
- // 轮播图详情页面
- bigImgDeatils(item) {
- // 预览图片
- let imgArr = [];
- /* 这里可以使用网络路径,也可以使用图片的base64编码 */
- imgArr.push(require(`@/static/lun/${item.big}`))
- //预览图片
- uni.previewImage({
- urls: imgArr,
- current: imgArr[0]
- });
- },
- // 轮播图改变都时候的方法
- change(e) {
- this.current = e.detail.current;
- },
效果如下
这里使用的是插件,效果不错
公告栏——前端代码
- <!-- 公告 -->
- <view class="lwNoticeBox">
- <lwNotice :showScale="true" @itemClick="newsItemDetails" :list="newsList"></lwNotice>
- <uni-icons class="more" @click="oatoUrl" type="bars" size="30" color="white"></uni-icons>
- </view>
- // 新闻公告栏 也是写死,反正这个简单
- newsList: [
- "启航官网jrx.jrxtiejin.com",
- "江河地笑CSDN",
- ]
- // 公告详情
- newsItemDetails(data) {
-
- // 数据库中的新闻是包含title和id字段
- // 通过title找到对应的新闻对象,然后通过新闻对象的id得到新闻详情,这里就不演示后端代码了
- // const currentNews = this.newsList.find(item => item.title == data)
- // uni.navigateTo({
- // url: '/pages/activities/OfficialNewsDeatils?id=' + currentNews.id
- // })
-
- },
商品展示环节很简单,做了几个组件,难点在于ThreeJS3d环节。然后点击进入到商品展示页面
然后点击购买进入付款环节,我们这次测试付款主要使用的是支付宝
皮肤设置数据库字段如下:
goods_type值得是皮肤类型,0是法师皮肤,1是战士皮肤,2是辅助皮肤
rank_index代表是皮肤展示前后顺序,0代表在最前面展示
sale_status代表是销售状态1:预销售,2:热销中,3:已售罄
首页展示单个皮肤组件——前端代码
- <!-- 单个皮肤展示组件 -->
- <digit-goods v-for="item in goodsList.data" :goodsItem="item" v-show="currentIndex==0"
- @click.native="toGoodsDetails(item)"></digit-goods>
- <template>
- <view class="goodsItem">
- <view class="goodsStatus">
- {{goodsStatus}}
- </view>
- <image class="goodImg" :src="goodsItem.goods_img" mode="widthFix"></image>
- <view class="content">
- <view class="title">
- {{goodsItem.goods_name}}
- <text class="remain_count">库存:{{goodsItem.goods_count}}</text>
- </view>
- <view class="tag">
- <text class="quality">限量:</text>
- <text class="count">{{goodsItem.goods_count}}份</text>
- <text class="classes">{{goodsItem.goods_desc}}</text>
- </view>
- <view class="price">
- <text class="left">
- <image :src="goodsItem.framer_img" mode="aspectFill"></image>{{goodsItem.framer}}
- </text>
- <text class="right">¥ {{goodsItem.goods_price.toFixed(2)}}</text>
- </view>
- </view>
-
- </view>
- </template>
-
- <script>
- export default {
- name: "DigitGoods",
- props: {
- goodsItem: {
- type: Object,
- default () {
- return {
- name: ''
- }
- }
- }
- },
- computed: {
- goodsStatus() {
- switch (this.goodsItem.sale_status) {
- case 1:
- return '⌚️预销售'
- case 2:
- return '声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/280528推荐阅读
相关标签
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。