微信小程序登录修改

This commit is contained in:
2026-01-09 12:17:21 +08:00
parent f948362d1b
commit 8073a95f74
23 changed files with 1417 additions and 58 deletions

View File

@@ -739,7 +739,7 @@ async function initWebSocket() {
// 构建WebSocket URL
// 开发环境ws://localhost:8180 或 ws://192.168.x.x:8180
// 生产环境wss://your-domain.com
const protocol = 'ws:' // 开发环境使用ws生产环境改为wss
const protocol = 'wss:' // 开发环境使用ws生产环境改为wss
// 小程序使用原生WebSocket端点不是SockJS端点
const wsUrl = `${protocol}//${WS_HOST}/urban-lifeline/workcase/ws/chat?token=${encodeURIComponent(token)}`

View File

@@ -227,7 +227,7 @@ async function initWebSocket() {
// 构建WebSocket URL
// 开发环境ws://localhost:8180 或 ws://192.168.x.x:8180
// 生产环境wss://your-domain.com
const protocol = 'ws:' // 开发环境使用ws生产环境改为wss
const protocol = 'wss:' // 开发环境使用ws生产环境改为wss
// 小程序使用原生WebSocket端点不是SockJS端点
const wsUrl = `${protocol}//${WS_HOST}/urban-lifeline/workcase/ws/chat?token=${encodeURIComponent(token)}`

View File

@@ -80,6 +80,15 @@
font-weight: 500;
}
// 退出按钮特殊样式
.logout-btn {
background: rgba(255, 59, 48, 0.1);
.btn-text {
color: #ff3b30;
}
}
// 欢迎区域(机器人+浮动标签)
.hero {
position: relative;
@@ -803,3 +812,94 @@
.modal-btn .btn-text {
font-size: 16px;
}
// 手机号授权弹窗样式
.phone-auth-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1001;
display: flex;
align-items: center;
justify-content: center;
}
.phone-auth-content {
width: 85%;
max-width: 340px;
padding: 30px 24px;
}
.phone-auth-body {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.auth-icon-wrap {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
}
.auth-icon {
font-size: 40px;
}
.auth-desc {
font-size: 14px;
color: #666;
line-height: 1.6;
margin-bottom: 10px;
}
.phone-auth-footer {
flex-direction: column;
gap: 12px;
}
.phone-auth-btn {
width: 100%;
height: 48px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 24px;
margin: 0;
padding: 0;
}
.phone-auth-btn .btn-text {
color: white;
font-size: 16px;
font-weight: 600;
}
.phone-auth-btn::after {
border: none;
}
.skip-auth-btn {
width: 100%;
height: 40px;
background: transparent;
border-radius: 20px;
margin: 0;
padding: 0;
}
.skip-auth-btn .skip-text {
color: #999;
font-size: 14px;
font-weight: 400;
}
.skip-auth-btn::after {
border: none;
}

View File

@@ -4,9 +4,6 @@
<view class="header" :style="{ paddingTop: headerPaddingTop + 'px', height: headerTotalHeight + 'px' }">
<text class="title">泰豪小电</text>
<view class="header-right">
<button class="workcase-btn" @tap="switchMockUser" v-if="isMockMode">
<text class="btn-text">切换</text>
</button>
<button class="workcase-btn" @tap="goToChatRoomList">
<text class="btn-text">聊天室</text>
</button>
@@ -175,14 +172,40 @@
</view>
</view>
</view>
<!-- 手机号授权弹窗 -->
<view class="phone-auth-modal" v-if="showPhoneAuthModal">
<view class="modal-mask"></view>
<view class="modal-content phone-auth-content">
<view class="modal-header">
<text class="modal-title">欢迎使用泰豪小电</text>
</view>
<view class="modal-body phone-auth-body">
<view class="auth-icon-wrap">
<text class="auth-icon">📱</text>
</view>
<text class="auth-desc">为了给您提供更好的服务,需要获取您的手机号用于身份识别和工单通知</text>
</view>
<view class="modal-footer phone-auth-footer">
<button
class="modal-btn confirm phone-auth-btn"
open-type="getPhoneNumber"
@getphonenumber="onGetPhoneNumber"
>
<text class="btn-text">授权手机号登录</text>
</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, nextTick, onMounted } from 'vue'
import { guestAPI, aiChatAPI, workcaseChatAPI } from '@/api'
import { guestAPI, aiChatAPI, workcaseChatAPI, fileAPI } from '@/api'
import { clearLoginInfo } from '@/api/base'
import type { TbWorkcaseDTO } from '@/types'
import type { DifyFileInfo } from '@/types/ai/aiChat'
import { AGENT_ID, FILE_DOWNLOAD_URL } from '@/config'
import { AGENT_ID } from '@/config'
// 前端消息展示类型
interface ChatMessageItem {
type: 'user' | 'bot'
@@ -215,8 +238,12 @@
phone: '',
userId: ''
})
const isMockMode = ref(true) // 开发环境mock模式
const userType = ref(false)
// 是否显示手机号授权弹窗
const showPhoneAuthModal = ref(false)
// 临时保存的微信登录code
const tempWechatCode = ref('')
// AI 对话相关
const chatId = ref<string>('') // 当前会话ID
const currentTaskId = ref<string>('') // 当前任务ID用于停止
@@ -229,41 +256,74 @@
// 初始化用户信息
async function initUserInfo() {
// #ifdef MP-WEIXIN
// 正式环境:从微信获取用户信息
// wx.login({
// success: (loginRes) => {
// // 使用code换取openid等信息
// console.log('微信登录code:', loginRes.code)
// }
// })
// #endif
// 开发环境使用mock数据
if (isMockMode.value) {
userInfo.value = {
wechatId: '17857100377',
username: '访客用户',
phone: '17857100377',
userId: ''
try {
// 1. 先尝试从缓存获取
const cachedWechatId = uni.getStorageSync('wechatId')
const cachedUserInfo = uni.getStorageSync('userInfo')
const cachedToken = uni.getStorageSync('token')
if (cachedWechatId && cachedUserInfo && cachedToken) {
// 有完整缓存,直接使用
const parsedUserInfo = typeof cachedUserInfo === 'string' ? JSON.parse(cachedUserInfo) : cachedUserInfo
userInfo.value = {
wechatId: cachedWechatId,
username: parsedUserInfo.username || parsedUserInfo.realName || '微信用户',
phone: parsedUserInfo.phone || '',
userId: parsedUserInfo.userId || ''
}
// 判断用户类型
if (parsedUserInfo.status === 'guest') {
userType.value = false
} else {
userType.value = true
}
console.log('使用缓存的用户信息:', userInfo.value)
return
}
await doIdentify()
// 2. 没有缓存,自动登录
await autoLogin()
} catch (error) {
console.error('初始化用户信息失败:', error)
// 登录失败,尝试自动登录
await autoLogin()
}
}
// 切换mock用户开发调试用
function switchMockUser() {
uni.showActionSheet({
itemList: ['员工 (17857100375)', '访客 (17857100377)'],
success: (res) => {
if (res.tapIndex === 0) {
userInfo.value = { wechatId: '17857100375', username: '员工用户', phone: '17857100375', userId: '' }
} else {
userInfo.value = { wechatId: '17857100377', username: '访客用户', phone: '17857100377', userId: '' }
// 自动登录
async function autoLogin() {
uni.showLoading({ title: '初始化中...' })
try {
// 使用 wx.login 获取 code
uni.login({
provider: 'weixin',
success: async (loginRes) => {
console.log('微信登录成功code:', loginRes.code)
uni.hideLoading()
// 保存code等待手机号授权后使用
tempWechatCode.value = loginRes.code
// 显示手机号授权弹窗
showPhoneAuthModal.value = true
},
fail: (err) => {
console.error('微信登录失败:', err)
uni.hideLoading()
uni.showToast({ title: '登录失败,请重试', icon: 'none' })
}
doIdentify()
}
})
})
} catch (error: any) {
console.error('自动登录失败:', error)
uni.hideLoading()
uni.showToast({
title: error.message || '登录失败',
icon: 'none'
})
}
}
// 调用identify接口
@@ -304,6 +364,104 @@
}
}
// 获取手机号回调
async function onGetPhoneNumber(e: any) {
console.log('获取手机号回调:', e)
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
console.error('获取手机号失败:', e.detail)
// 如果是权限问题,提示用户
if (e.detail.errno === 102) {
uni.showModal({
title: '提示',
content: '该小程序暂未开通手机号快速验证功能,请联系管理员开通后再试。',
showCancel: false
})
} else {
uni.showToast({
title: '需要授权手机号才能使用',
icon: 'none'
})
}
return
}
// 关闭弹窗
showPhoneAuthModal.value = false
uni.showLoading({ title: '登录中...' })
try {
const code = tempWechatCode.value
const wechatId = code.substring(0, 20) // 使用部分code作为临时ID
// 获取手机号授权返回的数据
const phoneCode = e.detail.code // 手机号授权code推荐使用
const encryptedData = e.detail.encryptedData // 加密数据
const iv = e.detail.iv // 解密向量
console.log('手机号授权数据:', { code, phoneCode, encryptedData: encryptedData?.substring(0, 50) + '...', iv })
// 调用 identify 接口
// 后端可以选择使用 phoneCode 或 encryptedData+iv 来解密手机号
const identifyRes = await guestAPI.identify({
wechatId: wechatId,
code: code, // 微信登录code
phoneCode: phoneCode, // 手机号授权code新版API推荐
encryptedData: encryptedData, // 加密数据旧版API
iv: iv, // 解密向量旧版API
loginType: 'wechat_miniprogram'
})
uni.hideLoading()
if (identifyRes.success && identifyRes.data) {
const loginDomain = identifyRes.data
// 保存登录信息
uni.setStorageSync('token', loginDomain.token || '')
uni.setStorageSync('userInfo', JSON.stringify(loginDomain.user))
uni.setStorageSync('loginDomain', JSON.stringify(loginDomain))
uni.setStorageSync('wechatId', wechatId)
// 更新用户信息
userInfo.value = {
wechatId: wechatId,
username: loginDomain.user?.username || loginDomain.user?.realName || '微信用户',
phone: loginDomain.user?.phone || '',
userId: loginDomain.user?.userId || ''
}
// 判断用户类型
if (loginDomain.user?.status === 'guest') {
userType.value = false
} else {
userType.value = true
}
console.log('手机号授权登录成功:', userInfo.value)
uni.showToast({ title: '登录成功', icon: 'success' })
} else {
uni.showToast({
title: identifyRes.message || '登录失败',
icon: 'none'
})
// 登录失败,重新显示授权弹窗
showPhoneAuthModal.value = true
}
} catch (error: any) {
console.error('手机号授权登录失败:', error)
uni.hideLoading()
uni.showToast({
title: error.message || '登录失败',
icon: 'none'
})
// 登录失败,重新显示授权弹窗
showPhoneAuthModal.value = true
}
}
// 生命周期
onMounted(() => {
// 初始化用户信息
@@ -885,7 +1043,7 @@
// 获取文件下载URL通过文件ID
function getFileDownloadUrl(fileId: string): string {
return `${FILE_DOWNLOAD_URL}${fileId}`
return fileAPI.getDownloadUrl(fileId)
}
// 判断文件ID对应的文件是否为图片
@@ -938,6 +1096,7 @@
})
}
}
</script>
<style lang="scss" scoped>