feat(branding): Update logo and favicon assets across all platforms

- Replace favicon.svg in admin panel with new brand logo design
- Add logo.png and logo.svg files to dedicated logo directory
- Update miniprogram with new logo assets (logo.png and logo.svg)
- Add README-UPLOAD.md documentation for miniprogram upload functionality
- Update miniprogram app.js and config.js for logo integration
- Update miniprogram user page (pages/user/index.js) to use new branding
- Update server upload route to handle new logo assets
- Add test-upload.js for upload functionality testing
- Update server .env configuration for asset handling
- Standardize brand visual identity across admin, miniprogram, and server platforms
This commit is contained in:
Ethanfly 2026-02-03 17:26:02 +08:00
parent 02937ca33c
commit 2ee017bccf
13 changed files with 483 additions and 40 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -226,8 +226,8 @@
padding: 0 16px;
.logo-icon {
width: 32px;
height: 32px;
width: 48px;
height: 48px;
}
.logo-text {

BIN
logo/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

71
logo/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,91 @@
# 小程序头像上传配置说明
## 问题说明
小程序从本地相册选择头像后,会得到一个临时文件路径(如 `http://tmp/xxx.jpg`),这个路径需要上传到服务器才能永久保存。
## 开发环境配置
### 1. 微信开发者工具设置
在微信开发者工具中,需要开启以下选项:
1. 点击右上角"详情"
2. 在"本地设置"中勾选:
- ✅ **不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书**
- ✅ **不校验合法域名**
这样才能在开发环境中访问 `http://127.0.0.1:3001` 的上传接口。
### 2. 确认服务端运行
确保后端服务正在运行:
```bash
cd server
npm start
```
服务应该运行在 `http://127.0.0.1:3001`
### 3. 测试上传功能
1. 在小程序中点击头像
2. 选择"从相册选择"
3. 选择一张图片
4. 点击"保存"
5. 查看控制台日志,应该显示:
- "检测到临时头像,开始上传: http://tmp/xxx.jpg"
- "头像上传成功: http://127.0.0.1:3001/uploads/xxx.jpg"
## 生产环境配置
### 1. 小程序后台配置
在微信公众平台mp.weixin.qq.com配置合法域名
1. 登录小程序后台
2. 进入"开发" -> "开发管理" -> "开发设置"
3. 在"服务器域名"中配置:
- **uploadFile合法域名**`https://yingsa-server.ethan.team`
- **request合法域名**`https://yingsa-server.ethan.team`
- **socket合法域名**`wss://yingsa-server.ethan.team`
### 2. 服务端HTTPS配置
确保生产环境服务器已配置HTTPS证书。
## 常见问题
### Q1: 上传失败,提示"不在合法域名列表中"
**解决方案**
- 开发环境:在微信开发者工具中开启"不校验合法域名"
- 生产环境:在小程序后台配置合法的上传域名
### Q2: 上传失败,提示"网络错误"
**解决方案**
- 检查后端服务是否正常运行
- 检查 `miniprogram/config.js` 中的 `baseUrl` 配置是否正确
- 查看后端日志是否有错误信息
### Q3: 头像显示不出来
**解决方案**
- 检查上传是否成功(查看控制台日志)
- 检查返回的URL是否正确
- 确认 `server/uploads` 目录存在且有写入权限
- 确认服务端的静态文件服务配置正确
### Q4: 临时文件路径错误
**解决方案**
- 这是正常的,临时文件只在选择时有效
- 必须上传到服务器后才能永久使用
- 代码已经处理了临时文件的上传逻辑
## 调试技巧
### 查看上传日志
在小程序控制台中查看:
```
检测到临时头像,开始上传: [临时路径]
上传头像响应: [服务器响应]
头像上传成功: [最终URL]
```
### 查看服务端日志
在服务端控制台中查看上传请求和文件保存情况。
### 检查文件是否保存
检查 `server/uploads/` 目录下是否有新上传的文件。

View File

@ -64,7 +64,7 @@ App({
},
// 手机号授权登录(第二步:解密手机号完成注册/登录)
phoneLogin(encryptedData, iv, userProfile) {
phoneLogin(encryptedData, iv, gender) {
return new Promise((resolve, reject) => {
const wxInfo = this.globalData.wxLoginInfo;
if (!wxInfo) {
@ -72,19 +72,23 @@ App({
return;
}
const requestData = {
openid: wxInfo.openid,
unionid: wxInfo.unionid,
sessionKey: wxInfo.sessionKey,
encryptedData,
iv,
};
// 如果提供了性别参数,添加到请求中
if (gender === 1 || gender === 2) {
requestData.gender = gender;
}
wx.request({
url: `${this.globalData.baseUrl}/api/user/phone-login`,
method: "POST",
data: {
openid: wxInfo.openid,
unionid: wxInfo.unionid,
sessionKey: wxInfo.sessionKey,
encryptedData,
iv,
nickname: (userProfile && userProfile.nickName) || "",
avatar: (userProfile && userProfile.avatarUrl) || "",
gender: (userProfile && userProfile.gender) || 0,
},
data: requestData,
success: (loginRes) => {
if (loginRes.data.code === 0) {
this.globalData.token = loginRes.data.data.token;

View File

@ -6,9 +6,9 @@
// 开发环境配置
const devConfig = {
// API 基础地址(本地开发)
baseUrl: "http://127.0.0.1:3000",
baseUrl: "http://127.0.0.1:3001",
// WebSocket 地址(本地开发)
wsUrl: "ws://127.0.0.1:3000/ws",
wsUrl: "ws://127.0.0.1:3001/ws",
};
// 生产环境配置

BIN
miniprogram/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -140,7 +140,7 @@ Page({
await this.doPhoneLogin(
e.detail.encryptedData,
e.detail.iv,
needGender ? this.data.registerGender : 0,
needGender ? this.data.registerGender : undefined,
);
// 获取门店信息
@ -186,8 +186,8 @@ Page({
},
async doPhoneLogin(encryptedData, iv, gender) {
const g = gender === 1 || gender === 2 ? gender : 0;
await app.phoneLogin(encryptedData, iv, g ? { gender: g } : null);
const g = gender === 1 || gender === 2 ? gender : undefined;
await app.phoneLogin(encryptedData, iv, g);
this._pendingPhoneLogin = null;
this.setData({ showGenderModal: false });
},
@ -277,10 +277,79 @@ Page({
},
// 选择头像新APIbutton open-type="chooseAvatar"
onChooseAvatarNew(e) {
async onChooseAvatarNew(e) {
const avatarUrl = e.detail.avatarUrl;
this.setData({
"profileForm.avatar": avatarUrl,
console.log('选择头像:', avatarUrl);
// 检查是否是临时文件
if (avatarUrl && (avatarUrl.startsWith("wxfile://") ||
avatarUrl.startsWith("http://tmp") ||
avatarUrl.includes("/tmp/"))) {
console.log('检测到临时头像,立即上传');
wx.showLoading({ title: "上传头像中..." });
try {
const uploadedUrl = await this.uploadAvatar(avatarUrl);
console.log('头像上传成功:', uploadedUrl);
this.setData({
"profileForm.avatar": uploadedUrl,
});
wx.hideLoading();
wx.showToast({ title: "头像上传成功", icon: "success", duration: 1500 });
} catch (uploadErr) {
wx.hideLoading();
console.error("头像上传失败:", uploadErr);
wx.showToast({
title: uploadErr.message || "头像上传失败",
icon: "none",
duration: 2000
});
// 上传失败,不设置头像
}
} else {
// 不是临时文件,直接使用
this.setData({
"profileForm.avatar": avatarUrl,
});
}
},
// 选择头像(使用 wx.chooseMedia
onChooseAvatar() {
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: (res) => {
console.log('选择图片成功:', res);
const tempFilePath = res.tempFiles[0].tempFilePath;
this.setData({
"profileForm.avatar": tempFilePath,
});
},
fail: (err) => {
console.error('选择图片失败:', err);
// 如果 chooseMedia 不支持,降级使用 chooseImage
wx.chooseImage({
count: 1,
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: (res) => {
console.log('选择图片成功(降级):', res);
const tempFilePath = res.tempFilePaths[0];
this.setData({
"profileForm.avatar": tempFilePath,
});
},
fail: (err) => {
console.error('选择图片失败:', err);
wx.showToast({ title: '选择图片失败', icon: 'none' });
}
});
}
});
},
@ -311,14 +380,8 @@ Page({
wx.showLoading({ title: "保存中..." });
try {
// 如果选择了新头像,先上传
let avatarUrl = avatar;
if (
avatar &&
(avatar.startsWith("wxfile://") || avatar.startsWith("http://tmp"))
) {
avatarUrl = await this.uploadAvatar(avatar);
}
// 头像已经在选择时上传,这里直接使用
const avatarUrl = avatar;
// 调用更新资料接口
const payload = {
@ -329,6 +392,7 @@ Page({
payload.gender = gender;
}
console.log("保存资料请求:", payload);
const res = await app.request("/api/user/profile", payload, "PUT");
// 更新本地数据服务端已返回完整URL
@ -357,6 +421,16 @@ Page({
// 上传头像
async uploadAvatar(filePath) {
return new Promise((resolve, reject) => {
console.log('开始上传头像');
console.log('文件路径:', filePath);
console.log('上传URL:', `${app.globalData.baseUrl}/api/upload/avatar`);
console.log('Token:', app.globalData.token);
if (!app.globalData.token) {
reject(new Error('未登录,无法上传头像'));
return;
}
wx.uploadFile({
url: `${app.globalData.baseUrl}/api/upload/avatar`,
filePath: filePath,
@ -366,20 +440,30 @@ Page({
},
success: (res) => {
try {
console.log("上传头像响应状态:", res.statusCode);
console.log("上传头像响应数据:", res.data);
if (res.statusCode !== 200) {
reject(new Error(`上传失败,状态码: ${res.statusCode}`));
return;
}
const data = JSON.parse(res.data);
if (data.code === 0 && data.data && data.data.url) {
console.log("头像上传成功:", data.data.url);
resolve(data.data.url);
} else {
console.error("上传头像失败:", data);
resolve(filePath);
reject(new Error(data.message || "上传头像失败"));
}
} catch (e) {
resolve(filePath);
console.error("解析上传响应失败:", e);
reject(new Error("上传头像失败"));
}
},
fail: (err) => {
console.error("上传头像失败:", err);
resolve(filePath);
console.error("上传头像请求失败:", err);
reject(new Error("上传头像失败,请检查网络"));
},
});
});

View File

@ -14,7 +14,7 @@
NODE_ENV=development
# 服务端口默认3000
PORT=3000
PORT=3001
# ------------------------------------------
# 数据库配置 (MySQL)

View File

@ -60,7 +60,15 @@ router.post('/images', authAdmin, upload.array('files', 10), (req, res) => {
});
// 用户头像上传(需要登录)
router.post('/avatar', authUser, upload.single('file'), (req, res) => {
router.post('/avatar', (req, res, next) => {
console.log('收到头像上传请求');
console.log('Authorization:', req.headers.authorization);
next();
}, authUser, (req, res, next) => {
console.log('认证通过,开始上传');
next();
}, upload.single('file'), (req, res) => {
console.log('文件上传完成:', req.file);
if (!req.file) {
return res.status(400).json(error('请选择要上传的图片'));
}
@ -68,7 +76,20 @@ router.post('/avatar', authUser, upload.single('file'), (req, res) => {
const relativePath = `/uploads/${req.file.filename}`;
// 使用 normalizeAvatarUrl 确保 https 保持 httpshttp 保持 http
const fullUrl = normalizeAvatarUrl(relativePath, req);
console.log('返回URL:', fullUrl);
res.json(success({ url: fullUrl }, '上传成功'));
});
// 错误处理中间件
router.use((err, req, res, next) => {
console.error('上传错误:', err);
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json(error('文件大小超过限制最大5MB'));
}
return res.status(400).json(error('文件上传失败: ' + err.message));
}
return res.status(500).json(error(err.message || '上传失败'));
});
module.exports = router;

36
server/test-upload.js Normal file
View File

@ -0,0 +1,36 @@
// 测试上传接口
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
async function testUpload() {
try {
// 首先测试健康检查
console.log('1. 测试健康检查...');
const healthRes = await axios.get('http://127.0.0.1:3001/health');
console.log('✓ 健康检查通过:', healthRes.data);
// 测试上传路由是否存在不带token
console.log('\n2. 测试上传路由无token...');
try {
await axios.post('http://127.0.0.1:3001/api/upload/avatar');
} catch (err) {
if (err.response) {
console.log('✓ 路由存在,返回状态:', err.response.status);
console.log(' 响应:', err.response.data);
} else {
console.log('✗ 请求失败:', err.message);
}
}
console.log('\n测试完成');
console.log('\n如果看到404错误说明路由没有正确注册。');
console.log('如果看到401错误说明路由存在但需要认证。');
console.log('如果看到400错误说明路由正常工作。');
} catch (error) {
console.error('测试失败:', error.message);
}
}
testUpload();