Commit 55ee2fcad7f1c5a30b4b747d8f4e48898f7766c6

Authored by YunaiV
1 parent d1291208

增加登陆界面

yudao-vue-ui/common/js/util.js 0 → 100644
  1 +let _debounceTimeout = null,
  2 + _throttleRunning = false
  3 +
  4 +/**
  5 + * 防抖
  6 + * 参考文章 https://juejin.cn/post/6844903669389885453
  7 + *
  8 + * @param {Function} 执行函数
  9 + * @param {Number} delay 延时ms
  10 + */
  11 +export const debounce = (fn, delay=500) => {
  12 + clearTimeout(_debounceTimeout);
  13 + _debounceTimeout = setTimeout(() => {
  14 + fn();
  15 + }, delay);
  16 +}
  17 +
  18 +/**
  19 + * 节流
  20 + * 参考文章 https://juejin.cn/post/6844903669389885453
  21 + *
  22 + * @param {Function} 执行函数
  23 + * @param {Number} delay 延时ms
  24 + */
  25 +export const throttle = (fn, delay=500) => {
  26 + if(_throttleRunning){
  27 + return;
  28 + }
  29 + _throttleRunning = true;
  30 + fn();
  31 + setTimeout(() => {
  32 + _throttleRunning = false;
  33 + }, delay);
  34 +}
  35 +
  36 +/**
  37 + * toast
  38 + */
  39 +export const msg = (title = '', param={}) => {
  40 + if(!title) return;
  41 + uni.showToast({
  42 + title,
  43 + duration: param.duration || 1500,
  44 + mask: param.mask || false,
  45 + icon: param.icon || 'none'
  46 + });
  47 +}
0 \ No newline at end of file 48 \ No newline at end of file
yudao-vue-ui/common/mixin/mixin.js 0 → 100644
  1 +// import {request} from '@/common/js/request'
  2 +
  3 +export default{
  4 + data() {
  5 + return {
  6 + page: 0, // 页码
  7 + pageNum: 6, // 每页加载数据量
  8 + loadingType: 1, // 加载类型。0 加载前;1 加载中;2 没有更多
  9 + isLoading: false, // 刷新数据
  10 + loaded: false, // 加载完毕
  11 + }
  12 + },
  13 + methods: {
  14 + /**
  15 + * 打印日志,方便调试
  16 + *
  17 + * @param {Object} data 数据
  18 + */
  19 + log(data) {
  20 + console.log(JSON.parse(JSON.stringify(data)))
  21 + },
  22 +
  23 + /**
  24 + * navigatorTo 跳转页面
  25 + *
  26 + * @param {String} url
  27 + * @param {Object} options 可选参数
  28 + * @param {Boolean} options.login 是否检测登录
  29 + */
  30 + navTo(url, options={}) {
  31 + this.$util.throttle(() => {
  32 + if (!url) {
  33 + return;
  34 + }
  35 + // 如果需要登陆,并且未登陆,则跳转到登陆界面
  36 + if ((~url.indexOf('login=1') || options.login) && !this.$store.getters.hasLogin){
  37 + url = '/pages/auth/login';
  38 + }
  39 + // 跳转到指定 url 地址
  40 + uni.navigateTo({
  41 + url
  42 + })
  43 + }, 300)
  44 + },
  45 +
  46 + /**
  47 + * $request云函数请求 TODO 芋艿:需要改成自己的
  48 + * @param {String} module
  49 + * @param {String} operation
  50 + * @param {Boolean} data 请求参数
  51 + * @param {Boolean} ext 附加参数
  52 + * @param {Boolean} ext.showLoading 是否显示loading状态,默认不显示
  53 + * @param {Boolean} ext.hideLoading 是否关闭loading状态,默认关闭
  54 + * @param {Boolean} ext.login 未登录拦截
  55 + * @param {Boolean} ext.setLoaded 加载完成是设置页面加载完毕
  56 + */
  57 + $request(module, operation, data={}, ext={}){
  58 + if(ext.login && !this.$util.isLogin()){
  59 + return;
  60 + }
  61 + if(ext.showLoading){
  62 + this.isLoading = true;
  63 + }
  64 + return new Promise((resolve, reject)=> {
  65 + request(module, operation, data, ext).then(result => {
  66 + if(ext.hideLoading !== false){
  67 + this.isLoading = false;
  68 + }
  69 + setTimeout(()=>{
  70 + if(this.setLoaded !== false){
  71 + this.loaded = true;
  72 + }
  73 + }, 100)
  74 + this.$refs.confirmBtn && this.$refs.confirmBtn.stop();
  75 + resolve(result);
  76 + }).catch((err) => {
  77 + reject(err);
  78 + })
  79 + })
  80 + },
  81 + imageOnLoad(data, key){ // TODO 芋艿:需要改成自己的
  82 + setTimeout(()=>{
  83 + this.$set(data, 'loaded', true);
  84 + }, 100)
  85 + },
  86 + showPopup(key){ // TODO 芋艿:需要改成自己的
  87 + this.$util.throttle(()=>{
  88 + this.$refs[key].open();
  89 + }, 200)
  90 + },
  91 + hidePopup(key){ // TODO 芋艿:需要改成自己的
  92 + this.$refs[key].close();
  93 + },
  94 + stopPrevent(){}, // TODO 芋艿:需要改成自己的
  95 + },
  96 +}
0 \ No newline at end of file 97 \ No newline at end of file
yudao-vue-ui/main.js
1 import App from './App' 1 import App from './App'
2 2
  3 +// 全局 Mixin
  4 +import mixin from './common/mixin/mixin'
  5 +Vue.mixin(mixin)
  6 +
  7 +// 全局 Util
  8 +import {
  9 + msg,
  10 + isLogin,
  11 + debounce,
  12 + throttle,
  13 + prePage,
  14 + date
  15 +} from '@/common/js/util'
  16 +Vue.prototype.$util = {
  17 + msg,
  18 + isLogin,
  19 + debounce,
  20 + throttle,
  21 + prePage,
  22 + date
  23 +}
  24 +
  25 +// 全局 Store
  26 +import store from './store'
  27 +Vue.prototype.$store = store
  28 +
3 // #ifndef VUE3 29 // #ifndef VUE3
4 import Vue from 'vue' 30 import Vue from 'vue'
5 Vue.config.productionTip = false 31 Vue.config.productionTip = false
yudao-vue-ui/pages.json
@@ -11,7 +11,21 @@ @@ -11,7 +11,21 @@
11 "navigationBarTitleText": "我的", 11 "navigationBarTitleText": "我的",
12 "navigationStyle": "custom" 12 "navigationStyle": "custom"
13 } 13 }
14 - } 14 + }, {
  15 + "path": "pages/auth/login",
  16 + "style": {
  17 + "navigationBarTitleText": "登录",
  18 + "navigationStyle":"custom",
  19 + "app-plus": {
  20 + "animationType": "slide-in-bottom"
  21 + }
  22 + }
  23 + }, {
  24 + "path" : "pages/set/userInfo",
  25 + "style" : {
  26 + "navigationBarTitleText": "个人资料"
  27 + }
  28 + }
15 ], 29 ],
16 "globalStyle": { 30 "globalStyle": {
17 "navigationBarTextStyle": "black", 31 "navigationBarTextStyle": "black",
yudao-vue-ui/pages/auth/login.vue 0 → 100644
  1 +<template>
  2 + <view class="app">
  3 + <view class="left-bottom-sign"></view>
  4 + <view class="back-btn mix-icon icon-guanbi" @click="navBack"></view>
  5 + <view class="right-top-sign"></view>
  6 + <view class="agreement center">
  7 + <text class="mix-icon icon-xuanzhong" :class="{active: agreement}" @click="checkAgreement"></text>
  8 + <text @click="checkAgreement">请认真阅读并同意</text>
  9 + <text class="tit" @click="navToAgreementDetail(1)">《用户服务协议》</text>
  10 + <text class="tit" @click="navToAgreementDetail(2)">《隐私权政策》</text>
  11 + </view>
  12 + <view class="wrapper">
  13 + <view class="left-top-sign">LOGIN</view>
  14 + <view class="welcome">
  15 + 欢迎回来!
  16 + </view>
  17 + <view class="input-content">
  18 + <view class="input-item">
  19 + <text class="tit">手机号码</text>
  20 + <view class="row">
  21 + <input
  22 + v-model="username"
  23 + type="number"
  24 + maxlength="11"
  25 + placeholder="请输入手机号码"
  26 + placeholder-style="color: #909399"
  27 + />
  28 + </view>
  29 + </view>
  30 + <view class="input-item">
  31 + <text class="tit">验证码</text>
  32 + <view class="row">
  33 + <input
  34 + v-model="code"
  35 + type="number"
  36 + maxlength="6"
  37 + placeholder="请输入手机验证码"
  38 + placeholder-style="color: #909399"
  39 + />
  40 + <mix-code :mobile="username" templateCode="SMS_194050994"></mix-code>
  41 + </view>
  42 + </view>
  43 + </view>
  44 + <mix-button ref="confirmBtn" text="登录" marginTop="60rpx" @onConfirm="login"></mix-button>
  45 +
  46 + <!-- #ifdef APP-PLUS || MP-WEIXIN -->
  47 + <view class="other-wrapper">
  48 + <view class="line center">
  49 + <text class="tit">快捷登录</text>
  50 + </view>
  51 + <view class="list row">
  52 + <!-- #ifdef MP-WEIXIN -->
  53 + <view class="item column center" @click="mpWxGetUserInfo">
  54 + <image class="icon" src="/static/icon/login-wx.png"></image>
  55 + </view>
  56 + <!-- #endif -->
  57 +
  58 + <!-- #ifdef APP-PLUS -->
  59 + <view v-if="canUseAppleLogin && false" class="item column center" style="width: 180rpx;" @click="loginByApple">
  60 + <image class="icon" src="/static/icon/apple.png"></image>
  61 + <text>Apple登录</text>
  62 + </view>
  63 + <view class="item column center" style="width: 180rpx;" @click="loginByWxApp">
  64 + <image class="icon" src="/static/icon/login-wx.png"></image>
  65 + <text>微信登录</text>
  66 + </view>
  67 + <!-- #endif -->
  68 + </view>
  69 + </view>
  70 + <!-- #endif -->
  71 + </view>
  72 +
  73 + <mix-loading v-if="isLoading"></mix-loading>
  74 + </view>
  75 +</template>
  76 +
  77 +<script>
  78 + import {checkStr} from '@/common/js/util'
  79 + import loginMpWx from './mixin/login-mp-wx.js'
  80 + import loginAppWx from './mixin/login-app-wx.js'
  81 + import loginApple from './mixin/login-apple.js'
  82 + export default{
  83 + mixins: [loginMpWx, loginAppWx, loginApple],
  84 + data(){
  85 + return {
  86 + canUseAppleLogin: false,
  87 + agreement: true,
  88 + username: '',
  89 + code: '',
  90 + }
  91 + },
  92 + onLoad() {
  93 + console.log(1);
  94 + },
  95 + methods: {
  96 + loginSuccessCallBack(data){
  97 + this.$util.msg('登陆成功');
  98 + this.$store.commit('setToken', data);
  99 + setTimeout(()=>{
  100 + uni.navigateBack();
  101 + }, 1000)
  102 + },
  103 + //手机号登录
  104 + async login(){
  105 + if(!this.agreement){
  106 + this.$util.msg('请阅读并同意用户服务及隐私协议');
  107 + this.$refs.confirmBtn.stop();
  108 + return;
  109 + }
  110 + const {username, code} = this;
  111 + if(!checkStr(username, 'mobile')){
  112 + this.$util.msg('请输入正确的手机号码');
  113 + this.$refs.confirmBtn.stop();
  114 + return;
  115 + }
  116 + if(!checkStr(code, 'mobileCode')){
  117 + this.$util.msg('验证码错误');
  118 + this.$refs.confirmBtn.stop();
  119 + return;
  120 + }
  121 + const res = await this.$request('user', 'login', {username,code});
  122 + this.$refs.confirmBtn.stop();
  123 +
  124 + if(res.status === 1){
  125 + this.loginSuccessCallBack(res.data);
  126 + }else{
  127 + this.$util.msg(res.msg);
  128 + }
  129 + },
  130 + navBack(){
  131 + uni.navigateBack();
  132 + },
  133 + //同意协议
  134 + checkAgreement(){
  135 + this.agreement = !this.agreement;
  136 + },
  137 + //打开协议
  138 + navToAgreementDetail(type){
  139 + this.navTo('/pages/public/article?param=' + JSON.stringify({
  140 + module: 'article',
  141 + operation: 'getAgreement',
  142 + data: {
  143 + type
  144 + }
  145 + }))
  146 + },
  147 + }
  148 + }
  149 +</script>
  150 +
  151 +<style>
  152 + page{
  153 + background: #fff;
  154 + }
  155 +</style>
  156 +<style scoped lang='scss'>
  157 + .app{
  158 + padding-top: 15vh;
  159 + position:relative;
  160 + width: 100vw;
  161 + height: 100vh;
  162 + overflow: hidden;
  163 + background: #fff;
  164 + }
  165 + .wrapper{
  166 + position:relative;
  167 + z-index: 90;
  168 + padding-bottom: 40rpx;
  169 + }
  170 + .back-btn{
  171 + position:absolute;
  172 + left: 20rpx;
  173 + top: calc(var(--status-bar-height) + 20rpx);
  174 + z-index: 90;
  175 + padding: 20rpx;
  176 + font-size: 32rpx;
  177 + color: #606266;
  178 + }
  179 + .left-top-sign{
  180 + font-size: 120rpx;
  181 + color: #f8f8f8;
  182 + position:relative;
  183 + left: -12rpx;
  184 + }
  185 + .right-top-sign{
  186 + position:absolute;
  187 + top: 80rpx;
  188 + right: -30rpx;
  189 + z-index: 95;
  190 +
  191 + &:before, &:after{
  192 + display:block;
  193 + content:"";
  194 + width: 400rpx;
  195 + height: 80rpx;
  196 + background: #b4f3e2;
  197 + }
  198 + &:before{
  199 + transform: rotate(50deg);
  200 + border-top-right-radius: 50px;
  201 + }
  202 + &:after{
  203 + position: absolute;
  204 + right: -198rpx;
  205 + top: 0;
  206 + transform: rotate(-50deg);
  207 + border-top-left-radius: 50px;
  208 + }
  209 + }
  210 + .left-bottom-sign{
  211 + position:absolute;
  212 + left: -270rpx;
  213 + bottom: -320rpx;
  214 + border: 100rpx solid #d0d1fd;
  215 + border-radius: 50%;
  216 + padding: 180rpx;
  217 + }
  218 + .welcome{
  219 + position:relative;
  220 + left: 50rpx;
  221 + top: -90rpx;
  222 + font-size: 46rpx;
  223 + color: #555;
  224 + text-shadow: 1px 0px 1px rgba(0,0,0,.3);
  225 + }
  226 + .input-content{
  227 + padding: 0 60rpx;
  228 + }
  229 + .input-item{
  230 + display:flex;
  231 + flex-direction: column;
  232 + align-items:flex-start;
  233 + justify-content: center;
  234 + padding: 0 30rpx;
  235 + background: #f8f6fc;
  236 + height: 120rpx;
  237 + border-radius: 4px;
  238 + margin-bottom: 50rpx;
  239 +
  240 + &:last-child{
  241 + margin-bottom: 0;
  242 + }
  243 + .row{
  244 + width: 100%;
  245 + }
  246 + .tit{
  247 + height: 50rpx;
  248 + line-height: 56rpx;
  249 + font-size: 26rpx;
  250 + color: #606266;
  251 + }
  252 + input{
  253 + flex: 1;
  254 + height: 60rpx;
  255 + font-size: 30rpx;
  256 + color: #303133;
  257 + width: 100%;
  258 + }
  259 + }
  260 + /* 其他登录方式 */
  261 + .other-wrapper{
  262 + display: flex;
  263 + flex-direction: column;
  264 + align-items: center;
  265 + padding-top: 20rpx;
  266 + margin-top: 80rpx;
  267 +
  268 + .line{
  269 + margin-bottom: 40rpx;
  270 +
  271 + .tit{
  272 + margin: 0 32rpx;
  273 + font-size: 24rpx;
  274 + color: #606266;
  275 + }
  276 + &:before, &:after{
  277 + content: '';
  278 + width: 160rpx;
  279 + height: 0;
  280 + border-top: 1px solid #e0e0e0;
  281 + }
  282 + }
  283 + .item{
  284 + font-size: 24rpx;
  285 + color: #606266;
  286 + background-color: #fff;
  287 + border: 0;
  288 +
  289 + &:after{
  290 + border: 0;
  291 + }
  292 + }
  293 + .icon{
  294 + width: 90rpx;
  295 + height: 90rpx;
  296 + margin: 0 24rpx 16rpx;
  297 + }
  298 + }
  299 + .agreement{
  300 + position: absolute;
  301 + left: 0;
  302 + bottom: 6vh;
  303 + z-index: 1;
  304 + width: 750rpx;
  305 + height: 90rpx;
  306 + font-size: 24rpx;
  307 + color: #999;
  308 +
  309 + .mix-icon{
  310 + font-size: 36rpx;
  311 + color: #ccc;
  312 + margin-right: 8rpx;
  313 + margin-top: 1px;
  314 +
  315 + &.active{
  316 + color: $base-color;
  317 + }
  318 + }
  319 + .tit{
  320 + color: #40a2ff;
  321 + }
  322 + }
  323 +</style>
yudao-vue-ui/pages/auth/mixin/login-app-wx.js 0 → 100644
  1 +export default{
  2 + // #ifdef APP-PLUS
  3 + methods: {
  4 + /**
  5 + * 微信App登录
  6 + * "openId": "o0yywwGWxtBCvBuE8vH4Naof0cqU",
  7 + * "nickName": "S .",
  8 + * "gender": 1,
  9 + * "city": "临沂",
  10 + * "province": "山东",
  11 + * "country": "中国",
  12 + * "avatarUrl": "http://thirdwx.qlogo.cn/mmopen/vi_32/xqpCtHRBBmdlf201Fykhtx7P7JcicIbgV3Weic1oOvN6iaR3tEbuu74f2fkKQWXvzK3VDgNTZzgf0g8FqPvq8LCNQ/132",
  13 + * "unionId": "oYqy4wmMcs78x9P-tsyMeM3MQ1PU"
  14 + */
  15 + loginByWxApp(userInfoData){
  16 + if(!this.agreement){
  17 + this.$util.msg('请阅读并同意用户服务及隐私协议');
  18 + return;
  19 + }
  20 + this.$util.throttle(async ()=>{
  21 + let [err, res] = await uni.login({
  22 + provider: 'weixin'
  23 + })
  24 + if(err){
  25 + console.log(err);
  26 + return;
  27 + }
  28 + uni.getUserInfo({
  29 + provider: 'weixin',
  30 + success: async res=>{
  31 + const response = await this.$request('user', 'loginByWeixin', {
  32 + userInfo: res.userInfo,
  33 + }, {
  34 + showLoading: true
  35 + });
  36 + if(response.status === 0){
  37 + this.$util.msg(response.msg);
  38 + return;
  39 + }
  40 + if(response.hasBindMobile && response.data.token){
  41 + this.loginSuccessCallBack({
  42 + token: response.data.token,
  43 + tokenExpired: response.data.tokenExpired
  44 + });
  45 + }else{
  46 + this.navTo('/pages/auth/bindMobile?data='+JSON.stringify(response.data))
  47 + }
  48 + plus.oauth.getServices(oauthRes=>{
  49 + oauthRes[0].logout(logoutRes => {
  50 + console.log(logoutRes);
  51 + }, error => {
  52 + console.log(error);
  53 + })
  54 + })
  55 + },
  56 + fail(err) {
  57 + console.log(err);
  58 + }
  59 + })
  60 + })
  61 + }
  62 + }
  63 + // #endif
  64 +}
  65 +
  66 +
  67 +
  68 +
  69 +
  70 +
  71 +
  72 +
  73 +
yudao-vue-ui/pages/auth/mixin/login-apple.js 0 → 100644
  1 +export default{
  2 + onLoad() {
  3 + if(this.systemInfo.platform !== 'ios'){
  4 + return;
  5 + }
  6 + const systemVersion = +this.systemInfo.system.split('.')[0];
  7 + if(systemVersion >= 13){
  8 + this.canUseAppleLogin = true;
  9 + }
  10 + },
  11 + methods: {
  12 + //苹果登录
  13 + async loginByApple(){
  14 + /* if(!this.canUseAppleLogin){
  15 + this.$util.msg('系统版本过低,无法使用苹果登录');
  16 + return;
  17 + } */
  18 + if(!this.agreement){
  19 + this.$util.msg('请阅读并同意用户服务及隐私协议');
  20 + this.$refs.confirmBtn.stop();
  21 + return;
  22 + }
  23 + uni.login({
  24 + provider: 'apple',
  25 + success: loginRes=> {
  26 + // 登录成功
  27 + uni.getUserInfo({
  28 + provider: 'apple',
  29 + success: async userRes=> {
  30 + console.log(userRes);
  31 + const response = await this.$request('user', 'loginByApple', {
  32 + authorizationCode: userRes.userInfo.authorizationCode,
  33 + identityToken: userRes.userInfo.identityToken
  34 + }, {
  35 + showLoading: true
  36 + });
  37 + console.log(response);
  38 + //注销苹果登录
  39 + this.appleLogout();
  40 + console.log(response);
  41 + if(response.status === 0){
  42 + this.$util.msg(response.msg);
  43 + return;
  44 + }
  45 + if(response.hasBindMobile && response.data.token){
  46 + this.loginSuccessCallBack({
  47 + token: response.data.token,
  48 + tokenExpired: response.data.tokenExpired
  49 + });
  50 + }else{
  51 + this.navTo('/pages/auth/bindMobile?type=apple&data='+JSON.stringify(response.data))
  52 + }
  53 + }
  54 + })
  55 + },
  56 + fail: err=> {
  57 + console.log(err);
  58 + this.$util.msg('登录失败');
  59 + this.appleLogout();
  60 + }
  61 + })
  62 + },
  63 + appleLogout(){
  64 + plus.oauth.getServices(oauthRes=>{
  65 + const oIndex = oauthRes.findIndex(oItem=> oItem.id === 'apple');
  66 + oauthRes[oIndex].logout(loRes => {
  67 + console.log('appleLogout success=> ', loRes);
  68 + }, loErr => {
  69 + console.log('appleLogout error=> ', loErr);
  70 + })
  71 + })
  72 + }
  73 + }
  74 +}
  75 +
  76 +
  77 +
  78 +
  79 +
  80 +
  81 +
  82 +
  83 +
yudao-vue-ui/pages/auth/mixin/login-mp-wx.js 0 → 100644
  1 +export default{
  2 + // #ifdef MP-WEIXIN
  3 + data(){
  4 + return {
  5 + mpCodeTimer: 0,
  6 + mpWxCode: '',
  7 + }
  8 + },
  9 + computed: {
  10 + timerIdent(){
  11 + return this.$store.state.timerIdent;
  12 + }
  13 + },
  14 + watch: {
  15 + timerIdent(){
  16 + this.mpCodeTimer ++;
  17 + if(this.mpCodeTimer % 30 === 0){
  18 + this.getMpWxCode();
  19 + }
  20 + }
  21 + },
  22 + onShow(){
  23 + this.getMpWxCode();
  24 + },
  25 + methods: {
  26 + //微信小程序登录
  27 + mpWxGetUserInfo(){
  28 + if(!this.agreement){
  29 + this.$util.msg('请阅读并同意用户服务及隐私协议');
  30 + return;
  31 + }
  32 +
  33 + this.$util.throttle(()=>{
  34 + uni.getUserProfile({
  35 + desc: '用于展示您的头像及昵称',
  36 + success: async profileRes=> {
  37 + const res = await this.$request('user', 'loginByWeixin', {
  38 + code: this.mpWxCode,
  39 + ...profileRes.userInfo
  40 + }, {
  41 + showLoading: true
  42 + });
  43 + if(res.status === 0){
  44 + this.$util.msg(res.msg);
  45 + return;
  46 + }
  47 + if(res.hasBindMobile && res.data.token){
  48 + this.loginSuccessCallBack({
  49 + token: res.data.token,
  50 + tokenExpired: res.data.tokenExpired
  51 + });
  52 + }else{
  53 + this.navTo('/pages/auth/bindMobile?data='+JSON.stringify(res.data))
  54 + }
  55 + console.log(res)
  56 + }
  57 + })
  58 + })
  59 + },
  60 + //获取code
  61 + getMpWxCode(){
  62 + uni.login({
  63 + provider: 'weixin',
  64 + success: res=> {
  65 + this.mpWxCode = res.code;
  66 + }
  67 + })
  68 + },
  69 +
  70 + }
  71 + // #endif
  72 +}
  73 +
  74 +
  75 +
  76 +
  77 +
  78 +
  79 +
  80 +
  81 +
yudao-vue-ui/pages/set/userInfo.vue 0 → 100644
  1 +<template>
  2 + <view class="app">
  3 + <view class="cell">
  4 + <text class="tit fill">头像</text>
  5 + <view class="avatar-wrap" @click="chooseImage">
  6 + <image class="avatar" :src="tempAvatar || userInfo.avatar || '/static/icon/default-avatar.png'" mode="aspectFill"></image>
  7 + <!-- 进度遮盖 -->
  8 + <view
  9 + class="progress center"
  10 + :class="{
  11 + 'no-transtion': uploadProgress === 0,
  12 + show: uploadProgress != 100
  13 + }"
  14 + :style="{
  15 + width: uploadProgress + '%',
  16 + height: uploadProgress + '%',
  17 + }"
  18 + ></view>
  19 + </view>
  20 + </view>
  21 + <view class="cell b-b">
  22 + <text class="tit fill">昵称</text>
  23 + <input class="input" v-model="userInfo.nickname" type="text" maxlength="8" placeholder="请输入昵称" placeholder-class="placeholder">
  24 + </view>
  25 + <view class="cell b-b">
  26 + <text class="tit fill">性别</text>
  27 + <view class="checkbox center" @click="changeGender(1)">
  28 + <text v-if="userInfo.gender == 1" class="mix-icon icon-xuanzhong"></text>
  29 + <text v-else class="mix-icon icon-yk_yuanquan"></text>
  30 + <text>男</text>
  31 + </view>
  32 + <view class="checkbox center" @click="changeGender(2)">
  33 + <text v-if="userInfo.gender == 2" class="mix-icon icon-xuanzhong"></text>
  34 + <text v-else class="mix-icon icon-yk_yuanquan"></text>
  35 + <text>女</text>
  36 + </view>
  37 + </view>
  38 +
  39 + <mix-button ref="confirmBtn" text="保存资料" marginTop="80rpx" @onConfirm="confirm"></mix-button>
  40 + </view>
  41 +</template>
  42 +
  43 +<script>
  44 + export default {
  45 + data() {
  46 + return {
  47 + uploadProgress: 100, //头像上传进度
  48 + tempAvatar: '',
  49 + userInfo: {},
  50 + }
  51 + },
  52 + computed: {
  53 + curUserInfo(){
  54 + return this.$store.state.userInfo
  55 + }
  56 + },
  57 + watch: {
  58 + curUserInfo(curUserInfo){
  59 + const {avatar, nickname, gender} = curUserInfo;
  60 + this.userInfo = {avatar, nickname, gender,};
  61 + }
  62 + },
  63 + onLoad() {
  64 + const {avatar, nickname, gender, anonymous} = this.curUserInfo;
  65 + this.userInfo = {avatar, nickname, gender};
  66 + },
  67 + methods: {
  68 + //提交修改
  69 + async confirm(){
  70 + const {uploadProgress, userInfo, curUserInfo} = this;
  71 + let isUpdate = false;
  72 + for(let key in userInfo){
  73 + if(userInfo[key] !== curUserInfo[key]){
  74 + isUpdate = true;
  75 + break;
  76 + }
  77 + }
  78 + if (isUpdate === false) {
  79 + this.$util.msg('信息未修改');
  80 + this.$refs.confirmBtn.stop();
  81 + return;
  82 + }
  83 + if (!userInfo.avatar) {
  84 + this.$util.msg('请上传头像');
  85 + this.$refs.confirmBtn.stop();
  86 + return;
  87 + }
  88 + if (uploadProgress !== 100) {
  89 + this.$util.msg('请等待头像上传完毕');
  90 + this.$refs.confirmBtn.stop();
  91 + return;
  92 + }
  93 + if (!userInfo.nickname) {
  94 + this.$util.msg('请输入您的昵称');
  95 + this.$refs.confirmBtn.stop();
  96 + return;
  97 + }
  98 + if (!userInfo.gender) {
  99 + this.$util.msg('请选择您的性别');
  100 + this.$refs.confirmBtn.stop();
  101 + return;
  102 + }
  103 + const res = await this.$request('user', 'update', userInfo);
  104 + this.$refs.confirmBtn.stop();
  105 + this.$util.msg(res.msg);
  106 + if(res.status === 1){
  107 + this.$store.dispatch('getUserInfo'); //刷新用户信息
  108 + setTimeout(()=>{
  109 + uni.navigateBack();
  110 + }, 1000)
  111 + }
  112 + },
  113 + //选择头像
  114 + chooseImage(){
  115 + uni.chooseImage({
  116 + count: 1,
  117 + success: res=> {
  118 + uni.navigateTo({
  119 + url: `./cutImage/cut?src=${res.tempFilePaths[0]}`
  120 + });
  121 + }
  122 + });
  123 + },
  124 + //裁剪回调
  125 + async setAvatar(filePath){
  126 + this.tempAvatar = filePath;
  127 + this.uploadProgress = 0;
  128 + const result = await uniCloud.uploadFile({
  129 + filePath: filePath,
  130 + cloudPath: + new Date() + ('000000' + Math.floor(Math.random() * 999999)).slice(-6) + '.jpg',
  131 + onUploadProgress: e=> {
  132 + this.uploadProgress = Math.round(
  133 + (e.loaded * 100) / e.total
  134 + );
  135 + }
  136 + });
  137 + if(!result.fileID){
  138 + this.$util.msg('头像上传失败');
  139 + return;
  140 + }
  141 + if(typeof uniCloud.getTempFileURL === 'undefined'){
  142 + this.userInfo.avatar = result.fileID;
  143 + }else{
  144 + const tempFiles = await uniCloud.getTempFileURL({
  145 + fileList: [result.fileID]
  146 + })
  147 + const tempFile = tempFiles.fileList[0];
  148 + if(tempFile.download_url || tempFile.fileID){
  149 + this.userInfo.avatar = tempFile.download_url || tempFile.fileID;
  150 + }else{
  151 + this.$util.msg('头像上传失败');
  152 + }
  153 + }
  154 + },
  155 + //修改性别
  156 + changeGender(gender){
  157 + this.$set(this.userInfo, 'gender', gender)
  158 + }
  159 + }
  160 + }
  161 +</script>
  162 +
  163 +<style scoped lang="scss">
  164 + .app{
  165 + padding-top: 16rpx;
  166 + }
  167 + .cell{
  168 + display: flex;
  169 + align-items: center;
  170 + min-height: 110rpx;
  171 + padding: 0 40rpx;
  172 +
  173 + &:first-child{
  174 + margin-bottom: 10rpx;
  175 + }
  176 + &:after{
  177 + left: 40rpx;
  178 + right: 40rpx;
  179 + border-color: #d8d8d8;
  180 + }
  181 + .tit{
  182 + font-size: 30rpx;
  183 + color: #333;
  184 + }
  185 + .avatar-wrap{
  186 + width: 120rpx;
  187 + height: 120rpx;
  188 + position: relative;
  189 + border-radius: 100rpx;
  190 + overflow: hidden;
  191 +
  192 + .avatar{
  193 + width: 100%;
  194 + height: 100%;
  195 + border-radius: 100rpx;
  196 + }
  197 + .progress{
  198 + position: absolute;
  199 + left: 50%;
  200 + top: 50%;
  201 + transform: translate(-50%, -50%);
  202 + width: 100rpx;
  203 + height: 100rpx;
  204 + box-shadow: rgba(0,0,0,.6) 0px 0px 0px 2005px;
  205 + border-radius: 100rpx;
  206 + transition: .5s;
  207 + opacity: 0;
  208 +
  209 + &.no-transtion{
  210 + transition: 0s;
  211 + }
  212 + &.show{
  213 + opacity: 1;
  214 + }
  215 + }
  216 + }
  217 + .input{
  218 + flex: 1;
  219 + text-align: right;
  220 + font-size: 28rpx;
  221 + color: #333;
  222 + }
  223 + switch{
  224 + margin: 0;
  225 + transform: scale(0.8) translateX(10rpx);
  226 + transform-origin: center right;
  227 + }
  228 + .tip{
  229 + margin-left: 20rpx;
  230 + font-size: 28rpx;
  231 + color: #999;
  232 + }
  233 + .checkbox{
  234 + padding: 12rpx 0 12rpx 40rpx;
  235 + font-size: 28rpx;
  236 + color: #333;
  237 +
  238 + .mix-icon{
  239 + margin-right: 12rpx;
  240 + font-size: 36rpx;
  241 + color: #ccc;
  242 + }
  243 + .icon-xuanzhong{
  244 + color: $base-color;
  245 + }
  246 + }
  247 + }
  248 +</style>
yudao-vue-ui/pages/tabbar/user.vue
@@ -59,7 +59,7 @@ @@ -59,7 +59,7 @@
59 <!-- <mix-list-cell icon="icon-shoucang_xuanzhongzhuangtai" iconColor="#54b4ef" title="我的收藏" @onClick="navTo('/pages/favorite/favorite', {login: true})"></mix-list-cell> --> 59 <!-- <mix-list-cell icon="icon-shoucang_xuanzhongzhuangtai" iconColor="#54b4ef" title="我的收藏" @onClick="navTo('/pages/favorite/favorite', {login: true})"></mix-list-cell> -->
60 <!-- <mix-list-cell icon="icon-pinglun-copy" iconColor="#ee883b" title="意见反馈" @onClick="navTo('/pages/feedback/feedback', {login: true})"></mix-list-cell> --> 60 <!-- <mix-list-cell icon="icon-pinglun-copy" iconColor="#ee883b" title="意见反馈" @onClick="navTo('/pages/feedback/feedback', {login: true})"></mix-list-cell> -->
61 <!-- <mix-list-cell icon="icon-shezhi1" iconColor="#37b0fb" title="设置" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell> --> 61 <!-- <mix-list-cell icon="icon-shezhi1" iconColor="#37b0fb" title="设置" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell> -->
62 - <mix-list-cell icon="icon-bianji" iconColor="#5fcda2" title="个人信息" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell> 62 + <mix-list-cell icon="icon-bianji" iconColor="#5fcda2" title="个人信息" border="" @onClick="navTo('/pages/set/userInfo', {login: true})"></mix-list-cell>
63 <mix-list-cell icon="icon-shezhi1" iconColor="#37b0fb" title="账号安全" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell> 63 <mix-list-cell icon="icon-shezhi1" iconColor="#37b0fb" title="账号安全" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell>
64 </view> 64 </view>
65 </view> 65 </view>
yudao-vue-ui/store/index.js 0 → 100644
  1 +import Vue from 'vue'
  2 +import Vuex from 'vuex'
  3 +// import {request} from '@/common/js/request'
  4 +
  5 +Vue.use(Vuex)
  6 +
  7 +const store = new Vuex.Store({
  8 + state: {
  9 + openExamine: false, // 是否开启审核状态。用于小程序、App 等审核时,关闭部分功能。TODO 芋艿:暂时没找到刷新的地方
  10 + token: '', // 用户身份 Token
  11 + userInfo: {}, // 用户基本信息
  12 + timerIdent: false, // 全局 1s 定时器,只在全局开启一个,所有需要定时执行的任务监听该值即可,无需额外开启 TODO 芋艿:需要看看
  13 + orderCount: {}, // 订单数量
  14 + },
  15 + getters: {
  16 + hasLogin(state){
  17 + return !!state.token;
  18 + }
  19 + },
  20 + mutations: {
  21 + //更新state数据
  22 + setStateAttr(state, param){
  23 + if(param instanceof Array){
  24 + for(let item of param){
  25 + state[item.key] = item.val;
  26 + }
  27 + }else{
  28 + state[param.key] = param.val;
  29 + }
  30 + },
  31 + //更新token
  32 + setToken(state, data){
  33 + const {token, tokenExpired} = data;
  34 + state.token = token;
  35 + uni.setStorageSync('uniIdToken', token);
  36 + uni.setStorageSync('tokenExpired', tokenExpired);
  37 + this.dispatch('getUserInfo'); //更新用户信息
  38 + this.dispatch('getCartCount');//更新购物车数量
  39 + uni.$emit('refreshCart');//刷新购物车
  40 + this.dispatch('getOrderCount'); //更新订单数量
  41 + },
  42 + // 退出登录
  43 + logout(state) {
  44 + state.token = '';
  45 + uni.removeStorageSync('uniIdToken');
  46 + this.dispatch('getCartCount');//更新购物车数量
  47 + uni.$emit('refreshCart');//刷新购物车
  48 + this.dispatch('getOrderCount'); //更新订单数量
  49 + setTimeout(()=>{
  50 + state.userInfo = {};
  51 + }, 1100)
  52 + },
  53 + },
  54 + actions: {
  55 + //更新用户信息
  56 + async getUserInfo({state, commit}){
  57 + const res = await request('user', 'get', {}, {
  58 + checkAuthInvalid: false
  59 + });
  60 + if(res.status === 1){
  61 + const userInfo = res.data;
  62 + commit('setStateAttr', {
  63 + key: 'userInfo',
  64 + val: userInfo
  65 + })
  66 + }
  67 + },
  68 + //更新用户订单数量
  69 + async getOrderCount({state, commit}){
  70 + let data = {
  71 + c0: 0,
  72 + c1: 0,
  73 + c2: 0,
  74 + c3: 0
  75 + }
  76 + if(state.token){
  77 + try {
  78 + const res = await request('order', 'getOrderCount');
  79 + data = res;
  80 + }catch (err){
  81 + console.error('更新用户订单数量 => ', err);
  82 + }
  83 + }
  84 + commit('setStateAttr', {
  85 + key: 'orderCount',
  86 + val: data
  87 + })
  88 + }
  89 + }
  90 +})
  91 +
  92 +
  93 +export default store