feat(miniapp): improve session navigation and auth guard

This commit is contained in:
john
2026-07-11 08:51:30 +08:00
parent 9e5a59b7d9
commit 0fd34a5644
7 changed files with 158 additions and 8 deletions
+48 -4
View File
@@ -2,9 +2,14 @@ const {
checkAuth,
createAgentRun,
getAgentRun,
loadSession
loadSession,
logout
} = require('../../utils/api');
const { AGENT_RUN_MAX_POLLS, AGENT_RUN_POLL_MS } = require('../../utils/config');
const {
AGENT_RUN_MAX_POLLS,
AGENT_RUN_POLL_MS,
SELECTED_SESSION_KEY
} = require('../../utils/config');
const { createUserMessage, normalizeMessages } = require('../../utils/messages');
function delay(ms) {
@@ -17,6 +22,7 @@ Page({
messages: [],
draft: '',
loading: false,
error: '',
scrollIntoView: ''
},
@@ -25,6 +31,14 @@ Page({
const status = await checkAuth();
if (!status?.authenticated) {
wx.redirectTo({ url: '/pages/login/index' });
return;
}
const selectedSessionId = wx.getStorageSync(SELECTED_SESSION_KEY);
if (selectedSessionId) {
wx.removeStorageSync(SELECTED_SESSION_KEY);
if (selectedSessionId !== this.data.sessionId) {
await this.openSession(selectedSessionId);
}
}
} catch {
wx.redirectTo({ url: '/pages/login/index' });
@@ -49,7 +63,7 @@ Page({
...this.data.messages,
{ id: userMessage.id, role: 'user', text }
];
this.setData({ messages: nextMessages, draft: '', loading: true });
this.setData({ messages: nextMessages, draft: '', loading: true, error: '' });
this.scrollToBottom();
try {
@@ -61,6 +75,7 @@ Page({
const run = await createAgentRun(payload);
await this.waitForRun(run?.id || run?.runId);
} catch (error) {
this.setData({ error: error?.message || '发送失败' });
wx.showToast({ title: error?.message || '发送失败', icon: 'none' });
} finally {
this.setData({ loading: false });
@@ -92,6 +107,35 @@ Page({
async refreshSession(sessionId) {
const detail = await loadSession(sessionId);
const messages = normalizeMessages(detail?.messages || detail?.data?.messages || []);
this.setData({ messages });
this.setData({ sessionId, messages, error: '' });
},
async openSession(sessionId) {
this.setData({ loading: true, error: '' });
try {
await this.refreshSession(sessionId);
} catch (error) {
this.setData({ error: error?.message || '会话加载失败' });
} finally {
this.setData({ loading: false });
this.scrollToBottom();
}
},
startNewChat() {
wx.removeStorageSync(SELECTED_SESSION_KEY);
this.setData({ sessionId: '', messages: [], draft: '', error: '' });
},
async handleLogout() {
this.setData({ loading: true, error: '' });
try {
await logout();
wx.redirectTo({ url: '/pages/login/index' });
} catch (error) {
this.setData({ error: error?.message || '退出失败' });
} finally {
this.setData({ loading: false });
}
}
});
+14
View File
@@ -1,5 +1,19 @@
<view class="page chat-page">
<view class="chat-header">
<view class="chat-title">
<text>{{sessionId ? '继续会话' : '新会话'}}</text>
<text wx:if="{{error}}" class="header-error">{{error}}</text>
</view>
<view class="header-actions">
<button class="header-button" bindtap="startNewChat">新聊天</button>
<button class="header-button" bindtap="handleLogout">退出</button>
</view>
</view>
<scroll-view class="messages" scroll-y scroll-into-view="{{scrollIntoView}}">
<view wx:if="{{messages.length === 0 && !loading}}" class="empty-state">
<text>发一条消息开始使用 TKMind。</text>
</view>
<view wx:for="{{messages}}" wx:key="id" id="msg-{{index}}" class="message {{item.role}}">
<text>{{item.text}}</text>
</view>
+51
View File
@@ -5,12 +5,63 @@
padding: 0;
}
.chat-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
padding: 20rpx 24rpx;
background: #ffffff;
border-bottom: 1rpx solid #dbe3eb;
}
.chat-title {
display: flex;
flex-direction: column;
gap: 4rpx;
min-width: 0;
font-size: 30rpx;
font-weight: 800;
}
.header-error {
max-width: 420rpx;
color: #b42318;
font-size: 22rpx;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.header-actions {
display: flex;
gap: 12rpx;
}
.header-button {
width: 112rpx;
height: 58rpx;
line-height: 58rpx;
border-radius: 10rpx;
background: #eef6f5;
color: #0f766e;
font-size: 24rpx;
}
.messages {
flex: 1;
box-sizing: border-box;
padding: 28rpx;
}
.empty-state {
margin: 160rpx auto 0;
color: #64748b;
font-size: 28rpx;
text-align: center;
}
.message {
max-width: 84%;
margin-bottom: 20rpx;
+12
View File
@@ -1,4 +1,5 @@
const {
checkAuth,
loginWithPassword,
loginWithWechatCode
} = require('../../utils/api');
@@ -13,6 +14,17 @@ Page({
passwordLoading: false
},
async onLoad() {
try {
const status = await checkAuth();
if (status?.authenticated) {
wx.switchTab({ url: '/pages/chat/index' });
}
} catch {
// Stay on the login page when the existing session cannot be verified.
}
},
handleUsername(event) {
this.setData({ username: event.detail.value, error: '' });
},
+17 -2
View File
@@ -1,4 +1,5 @@
const { listSessions } = require('../../utils/api');
const { checkAuth, listSessions } = require('../../utils/api');
const { SELECTED_SESSION_KEY } = require('../../utils/config');
function formatTime(value) {
const timestamp = Number(value || 0);
@@ -15,7 +16,20 @@ Page({
},
onShow() {
this.loadSessions();
this.ensureAndLoad();
},
async ensureAndLoad() {
try {
const status = await checkAuth();
if (!status?.authenticated) {
wx.redirectTo({ url: '/pages/login/index' });
return;
}
await this.loadSessions();
} catch {
wx.redirectTo({ url: '/pages/login/index' });
}
},
async loadSessions() {
@@ -40,6 +54,7 @@ Page({
openSession(event) {
const id = event.currentTarget.dataset.id;
if (!id) return;
wx.setStorageSync(SELECTED_SESSION_KEY, id);
wx.switchTab({ url: '/pages/chat/index' });
}
});
+15 -2
View File
@@ -1,4 +1,4 @@
const { listMindSpacePages } = require('../../utils/api');
const { checkAuth, listMindSpacePages } = require('../../utils/api');
Page({
data: {
@@ -8,7 +8,20 @@ Page({
},
onShow() {
this.loadPages();
this.ensureAndLoad();
},
async ensureAndLoad() {
try {
const status = await checkAuth();
if (!status?.authenticated) {
wx.redirectTo({ url: '/pages/login/index' });
return;
}
await this.loadPages();
} catch {
wx.redirectTo({ url: '/pages/login/index' });
}
},
async loadPages() {
+1
View File
@@ -1,6 +1,7 @@
module.exports = {
API_BASE_URL: 'https://h5.tkmind.cn',
MINIAPP_LOGIN_PATH: '/auth/wechat-miniapp/login',
SELECTED_SESSION_KEY: 'tkmind_selected_session_id',
AGENT_RUN_POLL_MS: 1200,
AGENT_RUN_MAX_POLLS: 120
};