20260905:本地调试小程序

This commit is contained in:
George 2026-09-05 13:39:38 +08:00
parent 21cd8337da
commit 823ce54419
21 changed files with 569 additions and 7 deletions

View File

@ -2,6 +2,7 @@
.DS_Store .DS_Store
*.mp3 *.mp3
*.mp4 *.mp4
project.private.config.json
# 依赖目录(如果有使用 npm) # 依赖目录(如果有使用 npm)
node_modules/ node_modules/

View File

@ -0,0 +1,214 @@
# 图标资源获取指南
## 需要的图标清单
### 1. TabBar 图标(底部导航栏)
- **device.png** - 设备图标(普通状态)
- 搜索关键词:设备、蓝牙、bluetooth、chip、device
- 颜色:#999999(灰色)
- 尺寸:81px × 81px
- **device-active.png** - 设备图标(激活状态)
- 同上图标,颜色改为:#1890ff(蓝色)
- **mine.png** - 我的图标(普通状态)
- 搜索关键词:用户、user、个人中心、person
- 颜色:#999999(灰色)
- 尺寸:81px × 81px
- **mine-active.png** - 我的图标(激活状态)
- 同上图标,颜色改为:#1890ff(蓝色)
### 2. 功能图标
- **default-avatar.png** - 默认头像
- 搜索关键词:头像、avatar、用户头像、default avatar
- 建议尺寸:200px × 200px
- 颜色:浅灰色填充
- **device-empty.png** - 空状态占位图
- 搜索关键词:空状态、empty、无数据、no data
- 建议尺寸:300px × 300px
- 建议:选择带有设备元素的空状态插画
- **ble-scan.png** - 蓝牙扫描图标
- 搜索关键词:蓝牙扫描、bluetooth scan、搜索、search
- 建议尺寸:120px × 120px
- 颜色:#1890ff(主题蓝色)
## iconfont 使用步骤
### 1. 访问网站
https://www.iconfont.cn/
### 2. 搜索图标
- 使用上面提供的关键词搜索
- 筛选条件:
- 类型:图标
- 风格:线性/面性(根据喜好选择)
- 免费:是
### 3. 选择并添加到项目
- 点击喜欢的图标
- 添加到购物车
- 创建项目:yack-mini-icons
### 4. 下载图标
- 进入项目
- 选择 PNG 格式
- 设置对应尺寸和颜色
- 批量下载
### 5. 重命名文件
下载后按照本项目的命名规范重命名:
```
下载的图标.png → device.png
下载的图标.png → device-active.png(修改颜色后再次下载)
...
```
## 通过 API 获取图标信息
iconfont.cn 提供了搜索 API,可以通过编程方式获取图标信息。
### API 端点
```
POST https://www.iconfont.cn/api/icon/search.json
```
### 请求参数
| 参数 | 类型 | 说明 | 示例 |
|------|------|------|------|
| q | string | 搜索关键词 | 蓝牙 |
| sortType | string | 排序方式 | updated_at |
| page | int | 页码 | 1 |
| pageSize | int | 每页数量 | 54 |
| sType | string | 筛选类型 | 空(全部) |
| fromCollection | int | 来源集合 | -1 |
| fills | string | 填充参数 | 空 |
| t | long | 时间戳 | 1788578495971 |
| ctoken | string | CSRF Token | null |
### 请求示例(curl)
```bash
curl 'https://www.iconfont.cn/api/icon/search.json' \
-H 'accept: application/json, text/javascript, */*; q=0.01' \
-H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7' \
-H 'content-type: application/x-www-form-urlencoded; charset=UTF-8' \
-H 'origin: https://www.iconfont.cn' \
-H 'referer: https://www.iconfont.cn/search/index' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' \
--data-raw 'q=%E8%93%9D%E7%89%99&sortType=updated_at&page=1&pageSize=54&sType=&fromCollection=-1&fills=&t=1788578495971&ctoken=null'
```
### 响应格式
```json
{
"code": 200,
"data": {
"count": 1234,
"page": 1,
"pageSize": 54,
"icons": [
{
"id": "12345",
"name": "蓝牙",
"font_class": "icon-bluetooth",
"show_svg": "<svg>...</svg>",
"author_name": "作者名",
"price": 0,
"tags": ["蓝牙", "bluetooth", "设备"]
}
]
}
}
```
### 使用建议
1. **关键词搜索**:使用中文或英文关键词,如"蓝牙"、"bluetooth"
2. **URL 编码**:中文关键词需要 URL 编码(如 `%E8%93%9D%E7%89%99` 表示"蓝牙")
3. **分页**:通过 `page` 参数获取更多结果
4. **时间戳**:`t` 参数可使用当前时间戳,防止缓存
5. **鉴权**:部分接口需要登录态,使用 Cookie 中的 `EGG_SESS_ICONFONT` 等
### 批量下载脚本示例
```javascript
// Node.js 示例:获取蓝牙相关图标
const axios = require('axios');
async function searchIcons(keyword, page = 1) {
const params = new URLSearchParams();
params.append('q', keyword);
params.append('sortType', 'updated_at');
params.append('page', page);
params.append('pageSize', 54);
params.append('fromCollection', -1);
params.append('t', Date.now());
params.append('ctoken', 'null');
const response = await axios.post(
'https://www.iconfont.cn/api/icon/search.json',
params,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0'
}
}
);
return response.data;
}
// 使用示例
searchIcons('蓝牙').then(data => {
console.log(`找到 ${data.data.count} 个图标`);
data.data.icons.forEach(icon => {
console.log(`- ${icon.name} (ID: ${icon.id})`);
});
});
```
### 注意事项
1. **登录态**:下载 SVG 或 PNG 可能需要登录态
2. **版权**:部分图标需要付费或遵守商用许可
3. **频率限制**:避免高频请求,建议间隔 1-2 秒
4. **Cookie 失效**:长时间使用需重新获取登录态
5. **官方推荐**:推荐使用网页界面或官方工具进行批量下载
## 替代方案
### 如果找不到合适的图标,可以使用:
1. **微信官方图标库**
- WeUI 图标库:https://weui.io/
- 提供了标准的小程序图标
2. **Ant Design 图标库**
- https://ant.design/components/icon-cn/
- 有丰富的图标资源
3. **Font Awesome**
- https://fontawesome.com/
- 国际知名图标库
## 颜色值参考
```css
/* 主题色 */
--primary-color: #1890ff;
/* TabBar 普通状态 */
--tabbar-normal: #999999;
/* TabBar 激活状态 */
--tabbar-active: #1890ff;
/* 辅助灰色 */
--gray-light: #f5f5f5;
```

View File

@ -3,7 +3,8 @@
"pages/login/login", "pages/login/login",
"pages/index/index", "pages/index/index",
"pages/device/scan/scan", "pages/device/scan/scan",
"pages/device/detail/detail" "pages/device/detail/detail",
"pages/mine/mine"
], ],
"window": { "window": {
"navigationBarTitleText": "岩安测控网关", "navigationBarTitleText": "岩安测控网关",
@ -23,6 +24,12 @@
"text": "设备", "text": "设备",
"iconPath": "assets/images/device.png", "iconPath": "assets/images/device.png",
"selectedIconPath": "assets/images/device-active.png" "selectedIconPath": "assets/images/device-active.png"
},
{
"pagePath": "pages/mine/mine",
"text": "我的",
"iconPath": "assets/images/mine.png",
"selectedIconPath": "assets/images/mine-active.png"
} }
] ]
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -100,11 +100,14 @@ Page({
const loginRes = await request.login(username, password, captchaCode, captchaUuid); const loginRes = await request.login(username, password, captchaCode, captchaUuid);
const token = loginRes.token; const token = loginRes.token;
// 先保存 token(重要:后续请求需要认证)
storage.setToken(token);
// 获取用户信息 // 获取用户信息
const infoRes = await request.getInfo(); const infoRes = await request.getInfo();
const user = infoRes.user; const user = infoRes.user;
// 保存登录信息 // 保存用户信息
app.setLoginInfo({ app.setLoginInfo({
token: token, token: token,
userId: user.userId, userId: user.userId,

View File

@ -2,7 +2,7 @@
<view class="login-container"> <view class="login-container">
<!-- Logo --> <!-- Logo -->
<view class="login-header"> <view class="login-header">
<image class="logo" src="/assets/images/logo.png" mode="aspectFit"></image> <image class="logo" src="/assets/images/logo_zh.png" mode="aspectFit"></image>
<text class="title">岩安测控蓝牙网关</text> <text class="title">岩安测控蓝牙网关</text>
<text class="subtitle">采集 BLE 设备数据,透传至云平台</text> <text class="subtitle">采集 BLE 设备数据,透传至云平台</text>
</view> </view>

View File

@ -15,7 +15,7 @@
} }
.logo { .logo {
width: 160rpx; width: 400rpx;
height: 160rpx; height: 160rpx;
margin-bottom: 32rpx; margin-bottom: 32rpx;
} }

View File

@ -0,0 +1,130 @@
/* pages/mine/mine.js */
const storage = require('../../utils/storage.js');
const { mqttClient } = require('../../services/mqtt-client.js');
const { bleService } = require('../../services/ble.js');
Page({
data: {
userInfo: null,
mqttStatus: '未连接',
mqttStatusColor: '#999',
bleStatus: '未初始化',
bleStatusColor: '#999',
pendingCount: 0,
appVersion: '1.0.0'
},
onLoad() {
this.loadUserInfo();
this.checkStatus();
},
onShow() {
// 每次显示页面时刷新状态
this.checkStatus();
},
// 加载用户信息
loadUserInfo() {
const userInfo = storage.getUserInfo();
this.setData({ userInfo });
},
// 检查连接状态
checkStatus() {
// MQTT 状态
const mqttState = mqttClient.getState();
let mqttStatus = '未连接';
let mqttStatusColor = '#999';
if (mqttState.connected) {
mqttStatus = '已连接';
mqttStatusColor = '#52c41a';
} else if (mqttState.connecting) {
mqttStatus = '连接中';
mqttStatusColor = '#1890ff';
}
// 蓝牙状态
const bleState = bleService.getState();
let bleStatus = '未初始化';
let bleStatusColor = '#999';
if (bleState.connected) {
bleStatus = '已连接设备';
bleStatusColor = '#52c41a';
} else if (bleState.adapterAvailable) {
bleStatus = '蓝牙已开启';
bleStatusColor = '#1890ff';
} else if (bleState.adapterAvailable === false) {
bleStatus = '蓝牙未开启';
bleStatusColor = '#ff4d4f';
}
// 离线缓存数量
const pendingData = storage.getPendingData();
const pendingCount = pendingData ? pendingData.length : 0;
this.setData({
mqttStatus,
mqttStatusColor,
bleStatus,
bleStatusColor,
pendingCount
});
},
// 清除缓存
clearCache() {
wx.showModal({
title: '提示',
content: '确定要清除缓存吗?这将清除所有离线数据。',
success: (res) => {
if (res.confirm) {
storage.clearPendingData();
wx.showToast({
title: '清除成功',
icon: 'success'
});
this.checkStatus();
}
}
});
},
// 退出登录
logout() {
wx.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
// 断开 MQTT 连接
if (mqttClient.isConnected()) {
mqttClient.disconnect();
}
// 断开蓝牙连接
if (bleService.isConnected()) {
bleService.disconnect();
}
// 清除所有本地数据
storage.clearAll();
// 跳转到登录页
wx.reLaunch({
url: '/pages/login/login'
});
}
}
});
},
// 关于我们
showAbout() {
wx.showModal({
title: '岩安测控网关',
content: '版本:' + this.data.appVersion + '\n\n轻量化蓝牙网关,采集 BLE 设备数据透传上报至 yack 平台。',
showCancel: false
});
}
});

View File

@ -0,0 +1,3 @@
{
"navigationBarTitleText": "我的"
}

View File

@ -0,0 +1,57 @@
<!--pages/mine/mine.wxml-->
<view class="container">
<!-- 用户信息卡片 -->
<view class="user-card">
<view class="user-info">
<image class="avatar" src="/assets/images/default-avatar.png" mode="aspectFill"></image>
<view class="info">
<text class="username">{{userInfo.userName || '未登录'}}</text>
<text class="user-id">ID: {{userInfo.userId || '-'}}</text>
</view>
</view>
</view>
<!-- 状态信息 -->
<view class="status-section">
<view class="section-title">连接状态</view>
<view class="status-list">
<view class="status-item">
<text class="label">MQTT 状态</text>
<text class="status" style="color: {{mqttStatusColor}}">{{mqttStatus}}</text>
</view>
<view class="status-item">
<text class="label">蓝牙状态</text>
<text class="status" style="color: {{bleStatusColor}}">{{bleStatus}}</text>
</view>
<view class="status-item">
<text class="label">离线缓存</text>
<text class="status">{{pendingCount}} 条</text>
</view>
</view>
</view>
<!-- 功能列表 -->
<view class="function-section">
<view class="section-title">功能</view>
<view class="function-list">
<view class="function-item" bindtap="clearCache">
<text class="function-name">清除缓存</text>
<text class="function-arrow">></text>
</view>
<view class="function-item" bindtap="showAbout">
<text class="function-name">关于</text>
<text class="function-arrow">></text>
</view>
</view>
</view>
<!-- 退出登录按钮 -->
<view class="logout-section">
<button class="logout-btn" bindtap="logout">退出登录</button>
</view>
<!-- 版本信息 -->
<view class="version-info">
<text>版本 {{appVersion}}</text>
</view>
</view>

View File

@ -0,0 +1,139 @@
/* pages/mine/mine.wxss */
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 20px;
}
/* 用户卡片 */
.user-card {
background-color: #1890ff;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
.user-info {
display: flex;
align-items: center;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
background-color: #fff;
margin-right: 30rpx;
}
.info {
flex: 1;
display: flex;
flex-direction: column;
}
.username {
font-size: 36rpx;
color: #fff;
font-weight: bold;
margin-bottom: 10rpx;
}
.user-id {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
}
/* 通用区块样式 */
.status-section,
.function-section {
background-color: #fff;
margin-bottom: 20rpx;
}
.section-title {
padding: 20rpx 30rpx;
font-size: 28rpx;
color: #999;
background-color: #f5f5f5;
}
/* 状态列表 */
.status-list {
padding: 0 30rpx;
}
.status-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.status-item:last-child {
border-bottom: none;
}
.status-item .label {
font-size: 32rpx;
color: #333;
}
.status-item .status {
font-size: 28rpx;
}
/* 功能列表 */
.function-list {
padding: 0 30rpx;
}
.function-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.function-item:last-child {
border-bottom: none;
}
.function-name {
font-size: 32rpx;
color: #333;
}
.function-arrow {
font-size: 28rpx;
color: #999;
}
/* 退出登录 */
.logout-section {
padding: 40rpx 30rpx;
}
.logout-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #fff;
color: #ff4d4f;
font-size: 32rpx;
border: none;
border-radius: 8rpx;
}
.logout-btn::after {
border: none;
}
/* 版本信息 */
.version-info {
text-align: center;
padding: 40rpx 0;
font-size: 24rpx;
color: #999;
}

View File

@ -37,16 +37,24 @@
"ignore": [], "ignore": [],
"disablePlugins": [], "disablePlugins": [],
"outputPath": "" "outputPath": ""
} },
"compileWorklet": false,
"localPlugins": false,
"disableUseStrict": false,
"useCompilerPlugins": false,
"condition": false,
"swc": false,
"disableSWC": true
}, },
"compileType": "miniprogram", "compileType": "miniprogram",
"libVersion": "3.3.4", "libVersion": "3.3.4",
"appid": "wx0000000000000000", "appid": "wxede2cc5d39b2c6ea",
"projectname": "yack-mini", "projectname": "yack-mini",
"condition": {}, "condition": {},
"editorSetting": { "editorSetting": {
"tabIndent": "insertSpaces", "tabIndent": "insertSpaces",
"tabSize": 2 "tabSize": 2
}, },
"miniprogramRoot": "miniprogram/" "miniprogramRoot": "miniprogram/",
"simulatorPluginLibVersion": {}
} }