当前位置:   article > 正文

uniapp基础项目_uniapp项目

uniapp项目

目录

1 项目准备

1.1 腾讯云服务空间

1.2 准备阿里云域名

1.3 准备支付宝支付能力

1.3.1下载app支付宝助手

1.4新建项目

2 注册

2.1验证码使用

2.2注册逻辑

 2.3报错记录

3 登录

3.1传统密码登录

3.2短信密码登录 

3.3手机一键登录

4 登录与退出登录 

5 轮播图 

6 公告栏

7 ThreeJs商品展示

8 商品购买


这是我3月-5月单人负责的一个数藏项目,巅峰时刻同时在线3w人,也算我第一个uniapp综合项目,不够目前我做的已经停运了,当初学了很多,但是缺乏整理,项目缺点也很多。我想通过这段时间把代码重新写一遍,整理一下。我不会把所有功能都重新梳理,但是重要的几样我肯定都一一梳理,而且会有增加,例如:注册、登录、一键登录、短信、抢购、转赠、支付宝支付、版本热更新、公告等

当然里面涉及的上链操作我不是很会,你们可以把这个当作一个售卖东西的App,里面不会涉及上一个公司的信息,只是单纯的整理一下目前项目主要的技术和自学时候的壁垒

我这里使用的是Mac电脑,不影响window电脑的学习,排版可能不好看

github上面每天都在更新,如果认可,不要吝啬正反馈:GitHub - chenqi13814529300/light-ship: 复习和加强uniapp/unicloud学习 

点赞收藏超过100,我将录播视频

1 项目准备

1.1 腾讯云服务空间

开通空间 

 6b0c4746a9fb4c1c98350b0530538153.png

配置短信签名和模版

d0b17999f10449dbb070026a8b53fba8.png

模版需要短信签名审核成功后才可以编辑模版

开通一键登录

a57a18826ea04895a5bef4f569295518.png

1.2 准备阿里云域名

需要购买至少3个月的服务器,然后实名认证啥的才可以,这些自己弄吧,然后我再域名解析为如下本项目使用的域名

728c134db8264082a1fba67303a7883e.png

1.3 准备支付宝支付能力

绝大部分人都没有创建公司或者实体,所以这里的支付能力我将采用支付宝沙箱带你们一起如何使用支付宝。

1.3.1下载app支付宝助手

2f3de4d7e14c466bbad15d437f8e18a4.png

1.3.2支付宝开放平台

登录 - 支付宝

a562272854cd4b5cb560af14275fad08.png

 你需要自己设置账户密码啥的,这个自己弄。

最终你可以得到几个有用的信息

appid:20210001111111111

mchid:2088621911111111

商家账户:wtesjg2746@sandbox.com

商家登录密码:111111

买家账户:qhsfny9975@sandbox.com

买家登录密码:111111

买家支付密码:111111

准备关于支付能力的密钥和证书,打开之前下的软件,点击生成应用密钥和应用公钥

7a597ad34e1943428b1a174a72fc8fe9.png

方法一用阿里云公钥(二选一):

点击查看

然后把应用公钥放到里面,生成支付宝公钥

方法一有应用私钥和支付宝公钥即可进行支付测试 

方法二用证书:

获取csr文件,组织/公司和域名填写商家账户

98df9213dd8a4cb1bfd5b3def6480e5e.png

 b64632a8ba064b1ab07bcaa0a754e6ea.png

 上传csr文件,然后下载3个证书

2f2e2888a3834a9bb411fad85a3e1e3f.png

 641ae85cdce1479885641bd2d62ebb49.png

 ok,支付支付能力准备好了!

1.4新建项目

这里使用的是vue2,unicloud,以及默认空模版

b67e0de2f7184e74ba3f0899ebdd8c1c.png

并关联腾讯云服务器空间

 44c75a79792a441aa2911c6c4f5a2dd1.png

2 注册

2.1验证码使用

首先需要开通验证码,签名和模版都审核通过,并充值些许钱。

94f2ea5d13ec417093713a4f0aec0bc7.png

需要先引入uni-id组件

uni-id - DCloud 插件市场

本次项目采用的是老版uni-id这个用起来比较顺手

6ceb6286825a4f35913cf9ce28f70c97.png

 引入uni-id后需要配置uni-id/config.json

13f3f7fb34b447afbbf18a1155512165.png

config.json的配置参考官网文档uni-app官网

 发送验证码——前端代码

我这边是做了发送验证码前进行判断(是否已经注册),避免浪费验证码次数 

  1. async getCode() {
  2. if (this.regsiterInfo.mobile && this.isAgree) {
  3. this.settime()
  4. const sms = uniCloud.importObject("sms")
  5. // 正常查找到就说,该用户名已经被注册
  6. const isRegister = await sms.getUserByMobile(this.regsiterInfo.mobile)
  7. console.log(isRegister)
  8. if (isRegister.code == 0) {
  9. uni.showToast({
  10. title: "该用户名已经被注册",
  11. icon: "none"
  12. })
  13. this.countdown = 0
  14. }
  15. if (isRegister.code == -100) {
  16. const res = await sms.sendSms(this.regsiterInfo.mobile, "register")
  17. console.log(res)
  18. if (res.code != 0) {
  19. uni.showToast({
  20. title: "验证码发送失败",
  21. icon: "none"
  22. })
  23. return
  24. }
  25. uni.showToast({
  26. title: "验证码发送成功",
  27. icon: "none"
  28. })
  29. }
  30. } else {
  31. uni.showToast({
  32. title: "请输入手机号并且勾选协议",
  33. icon: "none"
  34. })
  35. }
  36. }

验证码冷却时间——前端代码

也是节约成本,同时减少后端请求

  1. settime() {
  2. let smsTime = null
  3. if (this.countdown == 0) {
  4. this.isdisabledFn = false
  5. this.isSms = "获取验证码"
  6. this.countdown = 60;
  7. clearInterval(smsTime)
  8. } else {
  9. this.isdisabledFn = true
  10. this.isSms = "重新发送(" + this.countdown + ")"
  11. this.countdown--;
  12. smsTime = setTimeout(() => {
  13. this.settime()
  14. }, 1000)
  15. }
  16. },

校验验证码——前端代码

参数就是手机号和验证码 

  1. const sms = uniCloud.importObject("sms")
  2. const smsInfo = await sms.verifySmsCode(this.regsiterInfo.mobile, this.regsiterInfo.code,
  3. "register")
  4. console.log(smsInfo)

 验证码方法——后端代码

  1. // 开发文档: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj
  2. const uniID = require('uni-id')
  3. const db = uniCloud.database();
  4. module.exports = {
  5. // 发送验证码
  6. async sendSms(mobile, type) {
  7. // 生成验证码可以按自己的需求来,这里以生成6位数字为例
  8. const randomStr = '00000' + Math.floor(Math.random() * 1000000)
  9. const code = randomStr.substring(randomStr.length - 4)
  10. const res = await uniID.sendSmsCode({
  11. mobile,
  12. templateId: "14408",
  13. code,
  14. type
  15. })
  16. return res
  17. },
  18. // 校验验证码
  19. async verifySmsCode(mobile, code, type) {
  20. const res = await uniID.verifyCode({
  21. mobile,
  22. code,
  23. type
  24. })
  25. return res
  26. },
  27. // 根据电话号码查找个人信息
  28. async getUserByMobile(mobile) {
  29. let {
  30. data
  31. } = await db.collection("uni-id-users").where({
  32. "mobile": mobile
  33. }).get()
  34. if (data[0]) {
  35. return {
  36. code: 0,
  37. msg: '查询成功',
  38. }
  39. } else {
  40. return {
  41. code: -100,
  42. }
  43. }
  44. }
  45. // 手机+短信 登录
  46. // 注册后的可以直接登录,如果没有注册过则去注册页面
  47. async smsLogin(mobile, code) {
  48. // 判断mobild是否注册了
  49. const sms = uniCloud.importObject("sms")
  50. try {
  51. const res = await sms.getUserByMobile(mobile)
  52. } catch (e) {
  53. // 异常则显示未注册-100
  54. return {
  55. res: e
  56. }
  57. }
  58. try {
  59. const res = await uniID.loginBySms({
  60. mobile,
  61. code
  62. })
  63. return {
  64. res: res
  65. }
  66. } catch (e) {
  67. return {
  68. res: e
  69. }
  70. }
  71. }
  72. }

2.2注册逻辑

 手机号、密码、验证码、用户协议都填写才可以注册,上述完成后,便在云数据库中产生一条数据。

同时需要把注册的token保存下来,vuex保存下来。并跳转到登录页面

注册逻辑——前端代码 

  1. uniCloud.callFunction({
  2. name: 'register',
  3. data,
  4. async success(res) {
  5. console.log(res)
  6. if (res.result.code === 0) {
  7. uni.showToast({
  8. title: '注册成功',
  9. icon: 'none'
  10. })
  11. // 2.8.0版本起调整为蛇形uni_id_token(调整后在一段时间内兼容驼峰uniIdToken)
  12. uni.setStorageSync('uni_id_token', res.result.token)
  13. uni.setStorageSync('uni_id_token_expired', res.result.tokenExpired)
  14. // 把邀请码清单归到邀请码数据库之中(存在就入库不存在不执行)
  15. if (that.regsiterInfo.inviteCode) {
  16. console.log("我有邀请码奥")
  17. const userInfo = {
  18. "registerUserId": res.result.uid,
  19. "mobile": res.result.mobile,
  20. "username": res.result.username
  21. }
  22. const invite = uniCloud.importObject("invite")
  23. await invite.updateInvite(that.regsiterInfo.inviteCode,
  24. userInfo)
  25. }
  26. // 跳转到登录页
  27. setTimeout(function() {
  28. uni.navigateTo({
  29. url: "/pages/login/Login"
  30. })
  31. }, 1000)
  32. } else if (res.result.code == -100) {
  33. uni.showToast({
  34. title: res.result.msg,
  35. icon: "none"
  36. })
  37. } else {
  38. uni.redirectTo({
  39. content: res.result.message,
  40. showCancel: false
  41. })
  42. }
  43. },
  44. fail(res) {
  45. console.log(res)
  46. uni.showModal({
  47. content: '注册失败,请稍后再试',
  48. showCancel: false
  49. })
  50. }
  51. })

注册逻辑——后端代码 

  1. // 云函数register的代码
  2. const uniID = require('uni-id')
  3. exports.main = async function(event, context) {
  4. const {
  5. mobile,
  6. password,
  7. inviteCode
  8. } = event
  9. // 自动验证用户名是否与已经注册的用户名重复,如果重复会直接返回错误。否则会自动生成token并加密password存储username、password、token到数据表uni-id-users,并返回如上响应参数
  10. const res = await uniID.register({ //支持传入任何值,比如可以直接传入mobile即可设置手机号码,切勿直接传入event否则这是一个极大的安全问题
  11. "username":mobile,
  12. "password": password,
  13. "mobile": mobile,
  14. "avatar": 'https://7463-tcb-d4ae9humf98e68-0dudeafb75fdc-1310755086.tcb.qcloud.la/head/good3.jpg',
  15. "goods_count": 0,
  16. "mobile_confirmed": 1,
  17. "realname_auth": 0,
  18. "invite_code": inviteCode,
  19. "create_invite_code": '',
  20. })
  21. return res
  22. }

注册成功! 

3a5327ae25cc45b083c75e88f513188f.png  

 2.3报错记录

Error: Method name required代表你创建的是云对象,不是云函数。云对象调用需要方法(例如一开始我创建的是register是云对象,一切操作符合文档结果报这个错误) 

3 登录

3.1传统密码登录

密码登录——前端代码

 一个是判断是否是传统登录方式,登录成功后执行登录后的逻辑,保存token,以及记录登录时长(用于3天后清空token,登录失效,需要重新登录)

  1. if (this.isTradition) {
  2. const login = uniCloud.importObject("login")
  3. const loginInfo = await login.commonLogin(data)
  4. console.log(loginInfo)
  5. if (loginInfo.code == 0) {
  6. // 执行登录成功后的逻辑
  7. that.loginAfter(loginInfo)
  8. }
  9. }
  10. // 登录成功后的逻辑
  11. loginAfter(res) {
  12. // uni.closeAuthView()
  13. uni.showToast({
  14. title: '登录成功',
  15. icon: 'none',
  16. })
  17. // 保存token
  18. uni.setStorageSync('uni_id_token', res.token)
  19. uni.setStorageSync('uni_id_token_expired', res.tokenExpired)
  20. var dayAdd1 = new Date();
  21. dayAdd1 = dayAdd1.setDate(dayAdd1.getDate() + 1);
  22. dayAdd1 = new Date(dayAdd1);
  23. // 记入token终止日期,1
  24. uni.setStorageSync('uni_id_token_end_time', dayAdd1.getTime())
  25. // 其他业务代码,如跳转到首页等
  26. this.setUserInfo(res.userInfo)
  27. setTimeout(function() {
  28. uni.switchTab({
  29. url: "/pages/index/Index"
  30. })
  31. }, 1000)
  32. },

密码登录——后端代码 

  1. const uniID = require('uni-id')
  2. module.exports = {
  3. // 普通账号 密码登录
  4. async commonLogin(userInfo) {
  5. const {
  6. mobile,
  7. password
  8. } = userInfo
  9. // 自动完成mobile、password验证是否合法的逻辑
  10. const res = await uniID.login({
  11. username: mobile,
  12. password,
  13. })
  14. return res
  15. },
  16. // 重置密码
  17. async resetPwdBySms(resetInfo) {
  18. const {
  19. mobile,
  20. code,
  21. password
  22. } = resetInfo
  23. await uniID.resetPwdBySms({
  24. mobile,
  25. code,
  26. password
  27. })
  28. return {
  29. code:0
  30. }
  31. },
  32. }

3.2短信密码登录 

短信登录——前端代码 

短信登录其实跟注册获取验证码逻辑类似

  1. // 电话号码+验证码登录
  2. // 1.是否填写验证码
  3. if (that.loginInfo.code) {
  4. const sms = uniCloud.importObject("sms")
  5. const {
  6. res
  7. } = await sms.smsLogin(that.loginInfo.mobile, that.loginInfo.code)
  8. // res.code为-100则res.errMsg 未注册
  9. // 50202 errMsg "验证码错误或已失效"
  10. // 0 是成功的
  11. console.log(res)
  12. switch (res.code) {
  13. case 0:
  14. // 执行登录成功后的逻辑
  15. that.loginAfter(res)
  16. break;
  17. case -100:
  18. uni.showToast({
  19. title: res.errMsg,
  20. icon: 'none'
  21. });
  22. break;
  23. case 50202:
  24. uni.showToast({
  25. title: res.errMsg,
  26. icon: 'none'
  27. });
  28. break;
  29. case -300:
  30. uni.showToast({
  31. title: res.errMsg,
  32. icon: 'none'
  33. });
  34. break;
  35. }
  36. } else {
  37. uni.showToast({
  38. title: "请填写验证码",
  39. icon: 'none'
  40. })
  41. }

短信登录——后端代码 

这部分代码,在注册的后端sms代码里面

3.3手机一键登录

一键登录——前端代码

目前有缺陷,ios一键登录uni.closeAuthView()不生效,是官方还没修复 

  1. // 判断是否支持一键登录
  2. isAutoLogin() {
  3. let _that = this
  4. uni.getProvider({ //获取可用的服务提供商
  5. service: 'oauth',
  6. success: function(res) {
  7. console.log(res.provider) // ['weixin', qq', 'univerify']
  8. }
  9. });
  10. uni.preLogin({ //预登录
  11. provider: 'univerify', //用手机号登录
  12. success() {
  13. _that.autoStatus = true
  14. console.log('预登录成功')
  15. _that.fasterLogin()
  16. },
  17. fail(err) { //预登录失败
  18. _that.autoStatus = false
  19. _that.error = err
  20. console.log('错误码:' + err.errCode)
  21. console.log(err.errMsg)
  22. }
  23. })
  24. },
  25. async fasterLogin() {
  26. let that = this
  27. uni.login({
  28. provider: 'univerify',
  29. async success(res) { // 登录成功
  30. console.log(res.authResult); // {openid:'登录授权唯一标识',access_token:'接口返回的 token'}
  31. const login = uniCloud.importObject("login")
  32. const fastLoginRes = await login.fastLogin(res.authResult)
  33. console.log(fastLoginRes)
  34. if (fastLoginRes.code == -100) {
  35. uni.showToast({
  36. title: "请先注册",
  37. icon: "none"
  38. })
  39. setTimeout(function() {
  40. uni.closeAuthView()
  41. uni.navigateTo({
  42. url: "/pages/register/Register"
  43. })
  44. }, 1000)
  45. }
  46. if (fastLoginRes.code == 0) {
  47. uni.closeAuthView()
  48. that.loginAfter(fastLoginRes)
  49. }
  50. },
  51. fail(res) { // 登录失败
  52. console.log(res.errCode)
  53. uni.closeAuthView()
  54. }
  55. })
  56. },

 一键登录——后端代码

type:指定操作类型,可选值为loginregister,不传此参数时表现为手机号已注册则登录,手机号未注册则进行注册 

  1. // 手机一键登录
  2. async fastLogin(userInfo) {
  3. const {
  4. access_token,
  5. openid
  6. } = userInfo
  7. const res = await uniID.loginByUniverify({
  8. access_token,
  9. openid,
  10. type:'login'
  11. })
  12. return res
  13. },

4 登录与退出登录 

登录成功跳转到首页,并且显示登录状态和用户名

点击退出后如下

完整代码如下——前端代码 

  1. <template>
  2. <view>
  3. <nav-bar>
  4. <view slot="left" class="left">启航</view>
  5. <view slot="right" v-if="isLogin" @click="goLogin">{{getUserInfo._id.slice(0,12)}} <text @click="outLogin"
  6. class="outLogin">退出</text></view>
  7. <view slot="right" v-else @click="goLogin">登录</view>
  8. </nav-bar>
  9. </view>
  10. </template>
  11. <script>
  12. import NavBar from '../../components/NavBar.vue'
  13. import {
  14. mapGetters,
  15. mapMutations
  16. } from "vuex";
  17. export default {
  18. components: {
  19. NavBar
  20. },
  21. data() {
  22. return {}
  23. },
  24. computed: {
  25. ...mapGetters(["getUserInfo"]),
  26. isLogin() {
  27. console.log(this.getUserInfo)
  28. if (Object.keys(this.getUserInfo).length > 0) {
  29. return true;
  30. } else {
  31. return false;
  32. }
  33. },
  34. },
  35. methods: {
  36. ...mapMutations(['setUserInfo']),
  37. // 去登录
  38. goLogin() {
  39. uni.navigateTo({
  40. url: "/pages/login/Login"
  41. })
  42. },
  43. // 退出登录
  44. outLogin() {
  45. this.setUserInfo({})
  46. uni.removeStorageSync('uni_id_token')
  47. uni.removeStorageSync('uni_id_token_expired')
  48. uni.navigateTo({
  49. url: "/pages/Login/Login"
  50. })
  51. },
  52. }
  53. }
  54. </script>
  55. <style>
  56. </style>

NavBar组件——前端代码 

  1. <template>
  2. <view class="nav-bar">
  3. <view class="left"><slot name="left"></slot></view>
  4. <view class="center"><slot name="center"></slot></view>
  5. <view class="right"><slot name="right"></slot></view>
  6. </view>
  7. </template>
  8. <script>
  9. export default {
  10. name: "NavBar"
  11. }
  12. </script>
  13. <style scoped>
  14. .nav-bar {
  15. position: -webkit-sticky;
  16. position: sticky;
  17. top: var(--window-top);
  18. z-index: 99;
  19. height: 80rpx;
  20. overflow: hidden;
  21. line-height: 80rpx;
  22. background-color: #242729;
  23. color: white;
  24. }
  25. .left{
  26. float: left;
  27. }
  28. .right{
  29. float: right;
  30. }
  31. </style>

5 轮播图 

效果如下

点击轮播图进入图片详情

轮播图——前端代码

  1. <!-- 轮播图 -->
  2. <uni-swiper-dot :info="info" :current="current" field="content" mode="round">
  3. <swiper class="swiper-box" @change="change" autoplay="true">
  4. <swiper-item v-for="(item ,index) in info" :key="index">
  5. <image @click="bigImgDeatils(item)" :src="require(`@/static/lun/${item.content}`)" mode="widthFix">
  6. </image>
  7. </swiper-item>
  8. </swiper>
  9. </uni-swiper-dot>
  1. // 轮播图片 这里就不从后端获取了,大家应该都会
  2. info: [{
  3. content: 'lun2.jpeg',
  4. big: 'lun2-big.jpeg'
  5. },
  6. {
  7. content: 'lun1.jpeg',
  8. big: 'lun1-big.jpeg'
  9. },
  10. ],
  11. current: 0,
  1. // 轮播图详情页面
  2. bigImgDeatils(item) {
  3. // 预览图片
  4. let imgArr = [];
  5. /* 这里可以使用网络路径,也可以使用图片的base64编码 */
  6. imgArr.push(require(`@/static/lun/${item.big}`))
  7. //预览图片
  8. uni.previewImage({
  9. urls: imgArr,
  10. current: imgArr[0]
  11. });
  12. },
  13. // 轮播图改变都时候的方法
  14. change(e) {
  15. this.current = e.detail.current;
  16. },

6 公告栏

效果如下

 这里使用的是插件,效果不错

公告栏——前端代码

  1. <!-- 公告 -->
  2. <view class="lwNoticeBox">
  3. <lwNotice :showScale="true" @itemClick="newsItemDetails" :list="newsList"></lwNotice>
  4. <uni-icons class="more" @click="oatoUrl" type="bars" size="30" color="white"></uni-icons>
  5. </view>
  1. // 新闻公告栏 也是写死,反正这个简单
  2. newsList: [
  3. "启航官网jrx.jrxtiejin.com",
  4. "江河地笑CSDN",
  5. ]
  1. // 公告详情
  2. newsItemDetails(data) {
  3. // 数据库中的新闻是包含title和id字段
  4. // 通过title找到对应的新闻对象,然后通过新闻对象的id得到新闻详情,这里就不演示后端代码了
  5. // const currentNews = this.newsList.find(item => item.title == data)
  6. // uni.navigateTo({
  7. // url: '/pages/activities/OfficialNewsDeatils?id=' + currentNews.id
  8. // })
  9. },

7 ThreeJs商品展示

商品展示环节很简单,做了几个组件,难点在于ThreeJS3d环节。然后点击进入到商品展示页面

 然后点击购买进入付款环节,我们这次测试付款主要使用的是支付宝

 

皮肤设置数据库字段如下:

goods_type值得是皮肤类型,0是法师皮肤,1是战士皮肤,2是辅助皮肤

rank_index代表是皮肤展示前后顺序,0代表在最前面展示

sale_status代表是销售状态1:预销售,2:热销中,3:已售罄

首页展示单个皮肤组件——前端代码

  1. <!-- 单个皮肤展示组件 -->
  2. <digit-goods v-for="item in goodsList.data" :goodsItem="item" v-show="currentIndex==0"
  3. @click.native="toGoodsDetails(item)"></digit-goods>
  1. <template>
  2. <view class="goodsItem">
  3. <view class="goodsStatus">
  4. {{goodsStatus}}
  5. </view>
  6. <image class="goodImg" :src="goodsItem.goods_img" mode="widthFix"></image>
  7. <view class="content">
  8. <view class="title">
  9. {{goodsItem.goods_name}}
  10. <text class="remain_count">库存:{{goodsItem.goods_count}}</text>
  11. </view>
  12. <view class="tag">
  13. <text class="quality">限量:</text>
  14. <text class="count">{{goodsItem.goods_count}}</text>
  15. <text class="classes">{{goodsItem.goods_desc}}</text>
  16. </view>
  17. <view class="price">
  18. <text class="left">
  19. <image :src="goodsItem.framer_img" mode="aspectFill"></image>{{goodsItem.framer}}
  20. </text>
  21. <text class="right">¥ {{goodsItem.goods_price.toFixed(2)}}</text>
  22. </view>
  23. </view>
  24. </view>
  25. </template>
  26. <script>
  27. export default {
  28. name: "DigitGoods",
  29. props: {
  30. goodsItem: {
  31. type: Object,
  32. default () {
  33. return {
  34. name: ''
  35. }
  36. }
  37. }
  38. },
  39. computed: {
  40. goodsStatus() {
  41. switch (this.goodsItem.sale_status) {
  42. case 1:
  43. return '⌚️预销售'
  44. case 2:
  45. return '
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/280528
    推荐阅读
    相关标签