feat: 小程序连接服务器IP + 同步未提交开发

- miniprogram/config.js: BASE_URL 指向 http://192.227.237.8:3001
- 动作标题中文化(nameZh),搜索匹配 name/nameZh/nameEn
- 新增计划/收藏/我的 等页面与组件
- 后端 Docker 化(Dockerfile/.dockerignore)与翻译脚本
- .gitignore 补充 .DS_Store
This commit is contained in:
2026-07-22 14:05:30 +08:00
parent b3b58e66fb
commit 70ff806042
79 changed files with 2397 additions and 146070 deletions

View File

@@ -1,20 +1,115 @@
// 极简登录占位:仅演示 wx.login 拿到 code不请求真实后端服务端
// 生产环境中,应将 code 发送到后端换取 openid / session。
function getOpenid() {
return new Promise((resolve) => {
// 登录与用户态管理:封装 wx.login + 后端 /api/auth/login
// 后端在首次登录时自动创建用户;开发模式(未配置 WX_APPID接受任意 code
// 并返回稳定的虚拟 openid。
const { request, setUnauthorizedHandler } = require('./request');
const TOKEN_KEY = 'access_token';
const OPENID_KEY = 'openid';
const PROFILE_KEY = 'user_profile';
// 模块加载时注册 401 自动重新登录:成功后返回新的 token失败返回 null。
setUnauthorizedHandler(async () => {
try {
await login();
return wx.getStorageSync(TOKEN_KEY);
} catch (e) {
return null;
}
});
function login() {
return new Promise((resolve, reject) => {
wx.login({
success(res) {
if (res.code) {
// 真实实现wx.request({ url: BASE_URL + '/api/auth/login', data: { code } })
console.log('[auth] wx.login code =', res.code);
}
resolve(res.code || '');
const code = res.code || 'tourist';
request({ url: '/api/auth/login', method: 'POST', data: { code } })
.then((r) => {
const token = r.token;
const user = r.user || {};
wx.setStorageSync(TOKEN_KEY, token);
if (user.openid) wx.setStorageSync(OPENID_KEY, user.openid);
const profile = { nickname: user.nickname || '', avatar: user.avatar || '' };
wx.setStorageSync(PROFILE_KEY, profile);
resolve(profile);
})
.catch((err) => {
wx.showToast({ title: '登录失败,请重试', icon: 'none' });
reject(err);
});
},
fail() {
resolve('');
// 拿不到 code 时降级为游客 code
request({ url: '/api/auth/login', method: 'POST', data: { code: 'tourist' } })
.then((r) => {
const token = r.token;
const user = r.user || {};
wx.setStorageSync(TOKEN_KEY, token);
if (user.openid) wx.setStorageSync(OPENID_KEY, user.openid);
const profile = { nickname: user.nickname || '', avatar: user.avatar || '' };
wx.setStorageSync(PROFILE_KEY, profile);
resolve(profile);
})
.catch((err) => {
wx.showToast({ title: '登录失败,请重试', icon: 'none' });
reject(err);
});
}
});
});
}
module.exports = { getOpenid };
// 确保已登录:有 token 直接返回,否则执行登录。
async function ensureLogin() {
const token = getToken();
if (token) return token;
await login();
return getToken();
}
function getToken() {
return wx.getStorageSync(TOKEN_KEY) || '';
}
function getProfile() {
return wx.getStorageSync(PROFILE_KEY) || { nickname: '', avatar: '' };
}
function isLogin() {
return !!getToken();
}
function logout() {
wx.removeStorageSync(TOKEN_KEY);
wx.removeStorageSync(OPENID_KEY);
wx.removeStorageSync(PROFILE_KEY);
}
function setProfile(p) {
const profile = Object.assign({}, getProfile(), p || {});
wx.setStorageSync(PROFILE_KEY, profile);
return profile;
}
// 更新昵称 / 头像,写回后端并刷新本地状态。
function updateProfile(dto) {
return request({ url: '/api/users/me', method: 'PATCH', data: dto })
.then((user) => {
const profile = {
nickname: (user && user.nickname) || getProfile().nickname,
avatar: (user && user.avatar) || getProfile().avatar
};
wx.setStorageSync(PROFILE_KEY, profile);
return profile;
});
}
module.exports = {
login,
ensureLogin,
getToken,
getProfile,
isLogin,
logout,
setProfile,
updateProfile
};