Files
crmeb-mp/pages/annex/auth/index.vue

203 lines
8.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="auth-page">
<!-- 顶部说明 -->
<view class="auth-head">
<text class="auth-title">授权完善资料</text>
<text class="auth-sub">用于在应用里展示你的昵称 / 头像 / 手机号</text>
</view>
<!-- 授权表单卡片 -->
<view class="auth-card">
<!-- 头像:圆形选择区,点击选择;图片填满圆并裁剪,文字放圆外 -->
<view class="auth-avatar-wrap">
<button class="auth-avatar" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image v-if="authAvatar" class="auth-ava" :src="authAvatar" mode="aspectFill" />
<view v-else class="auth-ava-plus">+</view>
</button>
<text class="auth-ava-label">{{ authAvatar ? '点击更换头像' : '点击选择头像' }}</text>
</view>
<view class="auth-field">
<input class="auth-input" type="nickname" v-model="authNickname" placeholder="输入昵称" maxlength="16" placeholder-class="auth-ph" />
</view>
<button class="auth-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhone">{{ authPhone ? '✅ 已授权手机号' : '获取手机号' }}</button>
<view class="auth-actions">
<button class="auth-cancel" @tap="cancel">取消</button>
<button class="auth-ok" @tap="submitAuth">保存并返回</button>
</view>
</view>
<text class="auth-tip">微信授权仅用于在应用内展示你的资料,不会泄露给第三方。</text>
</view>
</template>
<script>
import store from '@/store';
import { HTTP_REQUEST_URL } from '@/config/app';
import { LOGIN_STATUS } from '@/config/cache';
function base64Decode(str) {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var output = '';
var i = 0;
str = String(str).replace(/[^A-Za-z0-9+/=]/g, '');
while (i < str.length) {
var b1 = chars.indexOf(str.charAt(i++));
var b2 = chars.indexOf(str.charAt(i++));
var b3 = chars.indexOf(str.charAt(i++));
var b4 = chars.indexOf(str.charAt(i++));
output += String.fromCharCode((b1 << 2) | (b2 >> 4));
if (b3 !== 64) output += String.fromCharCode(((b2 & 15) << 4) | (b3 >> 2));
if (b4 !== 64) output += String.fromCharCode(((b3 & 3) << 6) | b4);
}
return output;
}
function getOpenid() {
let oid = store.state.app.openid;
if (oid) return oid;
try { oid = uni.getStorageSync('OPENID'); } catch (e) {}
if (oid) return oid;
try {
const token = store.state.app.token || uni.getStorageSync(LOGIN_STATUS);
if (token) {
let parts = token.split('.');
if (parts.length === 3) {
let payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');
while (payload.length % 4 !== 0) payload += '=';
const data = JSON.parse(base64Decode(payload));
oid = data.openid || '';
if (oid) { store.commit('SET_OPENID', oid); return oid; }
}
}
} catch (e) {
console.warn('[auth] JWT decode openid failed:', e.message);
}
return '';
}
export default {
data() {
return {
authNickname: '',
authAvatar: '',
authPhone: false, // 手机号是否已授权(UI 反馈)
_authCode: '',
openid: ''
}
},
onLoad() {
this.openid = getOpenid();
if (!this.openid) uni.showToast({ title: '未获得 openid,请先登录', icon: 'none' });
},
methods: {
onChooseAvatar(e) {
const avatarUrl = e.detail.avatarUrl;
if (!avatarUrl) return uni.showToast({ title: '未获取到头像', icon: 'none' });
/* 上传到 box_app 公开接口(无鉴权):定制授权页设计为匿名可用,
不依赖 CRMEB 登录态 —— 用 CRMEB 的 uploadImgs 在退出登录后会 401 弹「请登录」。 */
uni.showLoading({ title: '上传头像中…' });
uni.uploadFile({
url: 'https://mp.yizhezhe.com/api/upload',
filePath: avatarUrl,
name: 'file',
success: (res) => {
try {
const d = JSON.parse(res.data);
if (d.code === 200 && d.result && d.result.url) {
this.authAvatar = d.result.url;
} else {
uni.showToast({ title: '头像上传失败', icon: 'none' });
}
} catch (err) {
uni.showToast({ title: '头像上传失败', icon: 'none' });
}
},
fail: () => uni.showToast({ title: '头像上传失败,请检查网络', icon: 'none' }),
complete: () => uni.hideLoading()
});
},
onGetPhone(e) {
if (e.detail.errMsg === 'getPhoneNumber:ok' && e.detail.code) {
this._authCode = e.detail.code;
this.authPhone = true;
} else {
uni.showToast({ title: '未授权手机号', icon: 'none' });
}
},
submitAuth() {
if (!this.authNickname && !this.authAvatar && !this._authCode) {
return uni.showToast({ title: '请至少授权一项', icon: 'none' });
}
const openid = getOpenid() || this.openid;
if (!openid) return uni.showToast({ title: '未获得 openid,请先登录', icon: 'none' });
const api = 'https://mp.yizhezhe.com/api/mp/public/auth/profile?openid=' + encodeURIComponent(openid) +
(this._authCode ? '&code=' + encodeURIComponent(this._authCode) : '') +
(this.authNickname ? '&nickname=' + encodeURIComponent(this.authNickname) : '') +
(this.authAvatar ? '&avatar=' + encodeURIComponent(this.authAvatar) : '');
uni.request({
url: api, method: 'POST', timeout: 10000,
success: (r) => {
const d = r.data || {};
if (d.code === 200) {
const res = d.result || {};
// 把授权结果写入 storage,web_view onShow 读取后刷新 H5 URL
uni.setStorageSync('__AUTH_RESULT__', {
openid: openid,
phone: res.phone || '',
nickname: res.userName || this.authNickname || '',
avatar: res.avatar || this.authAvatar || ''
});
uni.showToast({ title: '授权成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 600);
} else {
uni.showToast({ title: d.message || '授权保存失败', icon: 'none' });
}
},
fail: () => uni.showToast({ title: '授权请求失败,请检查网络', icon: 'none' })
});
},
cancel() { uni.navigateBack(); }
}
}
</script>
<style scoped>
/* 清 button 默认边框/padding(避免撑出卡片 + 消除默认灰底边框)。
不改 margin —— 统配 margin:0 会压掉各按钮自己的 margin-top,导致元素紧贴。 */
.auth-page button::after { border: none; }
.auth-page button { padding: 0; line-height: normal; box-sizing: border-box; border: none; -webkit-appearance: none; }
.auth-page input { box-sizing: border-box; margin: 0; }
.auth-page { min-height: 100vh; background: #F8F3EF; padding: 24px 20px 40px; display: flex; flex-direction: column; align-items: center; }
.auth-head { text-align: center; margin-top: 16px; }
.auth-title { font-size: 20px; font-weight: 800; color: #2B2320; display: block; }
.auth-sub { font-size: 13px; color: #A79C95; margin-top: 8px; display: block; }
.auth-card { width: 100%; max-width: 340px; background: #fff; border-radius: 18px; padding: 28px 20px 20px; margin-top: 28px; box-shadow: 0 6px 24px rgba(0,0,0,.05); display: flex; flex-direction: column; align-items: center; }
/* 头部:圆形头像选择区(图片填满圆并裁剪),说明文字放圆外下方 */
.auth-avatar-wrap { display: flex; flex-direction: column; align-items: center; }
.auth-avatar { width: 92px; height: 92px; border-radius: 50%; background: #F3E9E3; display: flex; align-items: center; justify-content: center; overflow: hidden; border: 1px dashed #D9C4B8; flex: 0 0 auto; }
.auth-ava { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.auth-ava-plus { font-size: 30px; color: #C9B7AC; line-height: 1; }
.auth-ava-label { font-size: 11px; color: #A79C95; margin-top: 8px; }
/* 昵称输入 */
.auth-field { width: 100%; margin-top: 22px; }
.auth-input { width: 100%; height: 48px; border-radius: 12px; background: #F8F3EF; font-size: 14px; color: #2B2320; padding: 0 14px; box-sizing: border-box; }
.auth-ph { color: #C9B7AC; }
/* 获取手机号按钮 */
.auth-btn { width: 100%; height: 48px; border-radius: 12px; background: #F8F3EF; color: #2B2320; font-size: 14px; margin-top: 14px; display: flex; align-items: center; justify-content: center; }
.auth-actions { display: flex; gap: 12px; margin-top: 24px; width: 100%; }
.auth-cancel, .auth-ok { flex: 1; height: 46px; border-radius: 12px; font-size: 14px; font-weight: 600; display: flex; align-items: center; justify-content: center; }
.auth-cancel { background: #F1E6E0; color: #6B615B; }
.auth-ok { background: #F4593C; color: #fff; }
.auth-tip { font-size: 11px; color: #A79C95; margin-top: 20px; text-align: center; line-height: 1.5; }
</style>