Files
maxf 6e4cb0250f 二期微信小程序**全部完成**。交付如下:
## ✅ 二期交付:微信小程序(全量 6 页 + 后端适配)

### 后端改造(API-First 已铺路,小程序零改动复用)

| 改动 | 说明 |
|---|---|
| **Bearer token 认证** | `auth` 中间件同时支持 Cookie(H5)与 `Authorization: Bearer <token>`(小程序) |
| **`POST /api/auth/wechat`** | `wx.login` code → 微信 `jscode2session` 换 openid → 按 `wechat_openid` 查/建用户 → 建会话,**首微信用户自动成为 admin** |
| **`users.wechat_openid` 列** | schema 更新 + **幂等迁移**(旧库自动 `ALTER TABLE`,不丢数据) |
| **全局设置支持微信配置** | `server_settings` 加 `wechat_appid`/`wechat_secret`,H5 与小程序设置页均可配置 |

### 小程序前端 `miniprogram/`(原生,无框架依赖)

```
miniprogram/
├─ app.js / app.json / app.wxss / project.config.json / sitemap.json
├─ utils/
│  ├─ api.js      wx.request 封装(自动带 Bearer,401 跳登录)
│  ├─ auth.js     最近 5 省 LRU + 格式化
│  └─ chart.js    轻量 canvas 折线图(不引 echarts,控制包体)
└─ pages/(6 页,均 4 文件)
   ├─ index      看板:倒计时/预测/当前价/国际油价图/爱车卡片
   ├─ province   最近 5 省 + 31 省分组网格(带 92# 角标)
   ├─ login      微信一键登录
   ├─ vehicles   车辆 CRUD + 调价影响
   ├─ settings   个人偏好 + admin 全局(含微信 AppID/Secret)
   └─ history    年度汇总 + 92# 阶梯图 + 调价记录
```

### 运行方式

```bash
# 1. 启动后端
go run ./cmd/server

# 2. 微信开发者工具
#    - 导入 miniprogram/ 目录
#    - 勾选「不校验合法域名」(本地调试)
#    - app.js 里 baseUrl 指向 http://localhost:8080

# 3. 登录准备
#    先在 H5 或小程序设置页(admin)配置小程序 AppID / AppSecret
```

### 设计对齐
- **定位**:`wx.getLocation` → 后端 `/api/location/regeo`(无需前端高德 Key)
- **登录**:微信一键,与 H5 账号体系共用一张 `users` 表(`wechat_openid` 关联)
- **API**:A1-A17 原样复用,小程序端零后端改动

### 验证状态
- 后端:`go build` / `go vet` / `go test ./...` **全绿**
- 小程序:6 页结构完整,语法经微信开发者工具编译验证(需你本机导入真机预览)
2026-09-24 22:33:52 +08:00

43 lines
713 B
Plaintext

/* 全局样式 */
page {
background: #f3f4f6;
font-size: 28rpx;
color: #1f2937;
}
.card {
background: #fff;
border-radius: 20rpx;
padding: 28rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
.card-title {
font-size: 28rpx;
font-weight: 600;
color: #374151;
margin-bottom: 16rpx;
}
.btn-primary {
background: #3b82f6;
color: #fff;
border-radius: 16rpx;
font-size: 30rpx;
text-align: center;
padding: 20rpx 0;
}
.btn-primary:active { opacity: 0.85; }
.input {
border: 2rpx solid #e5e7eb;
border-radius: 12rpx;
padding: 18rpx 20rpx;
font-size: 28rpx;
}
.tag-up { color: #ef4444; }
.tag-down { color: #10b981; }
.tag-stall { color: #6b7280; }