Files
CloudHome-Ops/CHANGELOG.md

846 lines
67 KiB
Markdown
Raw Permalink Normal View History

# 云定制主页管理 (ops-console) — 变更清单
> 包名:`com.ainetv.aishicloud` · version:`1.0.0+1`
> 标记说明:✅ 已实现 / 已修复 | ⏳ 待后端 / 待验收 | ❌ 已知问题
> 所有 release APK 均通过 `flutter analyze --no-fatal-infos`(0 error)
---
## v6-fix68(2026-08-29)
> **修复两个功能性 Bug:① 设备列表「删除」按钮点击后返回「删除失败:未知异常,请联系管理员」;② 设备详情页修改房号后不生效,页面仍显示 Web 端原有房号。**
>
> **根因(通过静态分析 Web 前端 JS 逆向真实后端契约得出)**:
> 1. **删除失败**:后端 `POST /device/info/delete` 接收的是**裸 JSON 数组** `[id]`(Web 端 `Ao(t)` → `$.adornData(t,!1)` → `JSON.stringify([id,...])`)。旧实现误传 `{'ids':[id]}`(对象),后端无法解析请求体 → 抛出「未知异常」。
> 2. **房号不保存**:后端设备表房号字段名为 **`roomId`**(Web 端设备编辑表单 `prop:'roomId'`、列表列 `prop:'roomId'` 标签「房间号」)。旧实现发送 `roomNo`,后端忽略该未知字段 → 更新“成功”但房号未变,刷新后回显旧值。
>
> **改动**:
> 1. `lib/services/device_service.dart`:
> - `deleteDevice(id)`:body 由 `{'ids':[id]}` 改为裸数组 `[id]`,并在 id 可转为数字时按数字发送(复刻 Web 端 `Yo()` 的 `Number(id)` 行为),兼容数字/字符串两种主键;
> - `activateDeviceByScan`:扫码激活的房号字段 `roomNo` → `roomId`(update 与 save 两条路径)。
> 2. `lib/screens/home/device_detail_page.dart`:`DeviceGroupRoomSheet` 提交时房号字段 `roomNo` → `roomId`。
> 3. `lib/models/device.dart`:`toJson` 房号输出键 `roomNo` → `roomId`(`fromJson` 读取别名列表保留 `roomNo`/`roomId` 等,确保后端返回 `roomId` 时正确回显)。
> 4. 版本号 `v6-fix67` → `v6-fix68`。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备服务 | `deleteDevice(id)`:删除 body 由 `{'ids':[id]}` 改为裸数组 `[id]`,对齐 Web 端契约 |
| ✅ 已修复 | 设备/扫码 | 房号字段统一为后端 `roomId`(update/save/详情编辑/toJson) |
| ✅ 已更新 | 版本号 | 登录页版本号 `v6-fix67` → `v6-fix68` |
---
## v6-fix67(2026-08-31)
> **设备列表页「向左滑动显示删除按钮」手势。**
>
> 用户需求:在「设备 MAC 排序」列表中,为每个 MAC 项增加**向左滑动**手势,滑动后**露出删除按钮**(位于行右侧);**点击删除按钮**才弹出确认对话框,确认后执行删除、取消则保留。
>
> **实现选择**:原先用 `Dismissible` 会在滑动过阈值松手时**自动**弹确认框(红色背景仅为装饰、不可点击),不满足「先露按钮再点击」的交互。故改用 `flutter_slidable`(新增依赖 `flutter_slidable: ^3.1.0`):**向左滑**揭示一个**可点击**的红色「删除」动作(位于行右侧),点击动作才触发确认框。
>
> **改动**:
> 1. `pubspec.yaml`:新增 `flutter_slidable: ^3.1.0`(`flutter pub get` 已解析)。
> 2. `lib/services/device_service.dart`:新增 `deleteDevice(String id)`——`POST /device/info/delete`,body 为 `{ids: [id]}`,与分组/用户等模块的删除接口约定保持一致。
> 3. `lib/screens/home/device_list_page.dart`:
> - 列表 `itemBuilder` 每行用 `Slidable`(`endActionPane` + `ScrollMotion`)包裹,**向左滑动**揭示右侧红色 `SlidableAction`(删除图标 + `t.delete` 文案);`dismissible: null` 表示不自动滑出删除,仅作按钮;
> - `SlidableAction.onPressed` 调用 `_confirmDeleteDevice(device)`:弹出 `AlertDialog`(标题 `确认删除` / 内容 `是否确认删除该设备(设备名/MAC)?`,取消/删除按钮);
> - 取消 → 返回 `false`,行回弹保留;
> - 确认 → 先入 `_pendingDeleteIds` 本地隐藏该行(避免抖动),再调 `deleteDevice`,成功 `ref.invalidate(devicesByStoreProvider)` + SnackBar `已删除`,真正由列表刷新移除;异常则移出 `_pendingDeleteIds` 恢复显示并 SnackBar `删除失败:{原因}`;
> - 新增 `_rowKey(Device)`:`${id 或空}__${mac}` 作为行唯一键,避免 `device.id` 为空时 `ValueKey('')` 重复触发 Flutter 断言崩溃;`_filter` 与 `_pendingDeleteIds` 均改用该键。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 新增 | 依赖 | `pubspec.yaml`:`flutter_slidable: ^3.1.0` |
| ✅ 新增 | 设备服务 | `lib/services/device_service.dart`:`deleteDevice(id)` 调 `POST /device/info/delete`,body `{ids:[id]}` |
| ✅ 新增 | 设备列表 | `lib/screens/home/device_list_page.dart`:每行 `Slidable`(startToEnd)露可点击红色「删除」按钮,点击才弹确认框;确认删除、取消保留;删除中本地隐藏防抖、失败回滚并提示;`_rowKey` 防空 id 重复键 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix66` → `v6-fix67` |
---
## v6-fix66(2026-08-31)
> **修复 Web 端删除设备后,手机端设备列表仍显示已删除设备的问题。**
>
> 用户反馈:在 Web 后台删除设备后,手机端设备列表未同步,仍显示该设备 MAC(如截图中 `08:C3:ED:64:16:72`)。
>
> **根因**:设备列表页使用 `devicesByStoreProvider`(Riverpod `FutureProvider.family`)做缓存,但只在「下拉刷新」「切换门店筛选」「扫码成功」三种场景下刷新;日常从 Web 端删除设备后,手机端没有自动刷新机制,因此一直展示旧缓存。
>
> **改动**:
> 1. `lib/screens/home/device_list_page.dart`:
> - `_DeviceListPageState` 混入 `WidgetsBindingObserver`,在 `initState`/`dispose` 中注册/注销生命周期监听;
> - 新增 `didChangeAppLifecycleState` 处理:应用从后台切回前台(`AppLifecycleState.resumed`)且跳过首次启动事件后,调用 `ref.invalidate(devicesByStoreProvider)` 失效所有门店筛选参数下的设备列表缓存,并静默刷新门店列表,使手机端与 Web 端数据同步;
> - 合并原有的 `dispose` 方法,确保 `removeObserver` 与控制器释放同时执行。
> 2. `lib/screens/device/device_scan_page.dart`:
> - 将扫码查询命中后端设备、扫码绑定/激活成功后的 `ref.invalidate(devicesProvider)` 改为 `ref.invalidate(devicesByStoreProvider)`,失效门店筛选 Provider 族(而不是只失效无参数的 `devicesProvider`),确保返回列表页时看到最新数据。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备列表 | `lib/screens/home/device_list_page.dart`:App 切回前台自动刷新设备/门店列表,同步 Web 端变更 |
| ✅ 已修复 | 扫码页 | `lib/screens/device/device_scan_page.dart`:扫码成功后失效 `devicesByStoreProvider` 全族,而非仅 `devicesProvider` |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix65` → `v6-fix66` |
---
## v6-fix65(2026-08-31)
> **扫码绑定流程改造:扫码成功后改为页面内表单(门店分组下拉 + 房号输入),替代原先分步弹窗。**
>
> 新增需求:扫码成功后,在页面上直接提供「门店分组」选择控件(从分组列表选取)与「房号」输入框,并明确两者的校验规则,保证移动端布局清晰、操作便捷。
>
> **改动**:`lib/screens/device/device_scan_page.dart`
> 1. 移除原 `_confirmBind` / `_pickGroup` / `_pickStore` 三步弹窗流程。
> 2. 新增 `_enterBindForm`:扫码识别 MAC 后进入表单态,停止相机预览;
> - 激活模式(无预填门店):加载门店下拉,选择门店后级联加载其分组;
> - 绑定模式(路由已预填门店):门店固定展示,直接加载分组。
> 3. 新增 `_buildBindForm`(SingleChildScrollView + Card):已识别设备 MAC 卡片、门店分组 `DropdownButtonFormField`、房号 `TextFormField`、提交/重新扫码按钮;分组加载中显示进度。
> 4. 校验规则(`_validateForm`):
> - 门店:仅激活模式必填(未选门店 → `请先选择门店`);
> - 门店分组:必填(未选 → `请选择门店分组`;门店无分组 → `该门店暂无设备分组…`);
> - 房号:必填且格式校验(非空、长度 1~20、仅含中英文/数字/连字符/斜杠,否则 `房号格式不正确…`);错误信息以输入框 `errorText` 实时回显。
> 5. `_submitBind` 校验通过调 `DeviceService().activateDeviceByScan(mac, storeId, groupId, roomNo)`(后端 `roomNo` 字段已支持),写本地绑定缓存并 `ref.invalidate(devicesProvider)`,成功 SnackBar 后 `context.pop()`;登录失效仍兜底跳 `/login`。
> 6. i18n(`lib/utils/i10n.dart`)新增 `scanGroupLabel` / `scanStoreRequired` / `scanGroupRequired` / `scanRoomRequired` / `scanRoomInvalid` / `scanRoomHint` / `scanRescan` / `scanFormInvalid` / `scanFormDeviceTitle`。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 新增 | 扫码页 | 页面内绑定表单:门店分组下拉 + 房号输入 + 实时校验 + 提交/重扫 |
| ✅ 已删除 | 扫码页 | 移除 `_confirmBind` / `_pickGroup` / `_pickStore` 分步弹窗 |
| ✅ 新增 | i18n | 表单相关中文/英文文案 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix64` → `v6-fix65` |
---
## v6-fix60(2026-08-31)
> **修复设备详情页在线状态与 Web 端不一致的问题。**
>
> 用户截图:手机端设备详情页状态显示「离线」,但 Web 端同一设备显示「在线」。根因:移动端详情接口 `/device/info/info/{id}` 返回的 `status`/`lastSyncTime` 滞后于列表接口 `/device/info/list`;Web 端设备列表正是走列表接口并显示最新在线状态。修复后详情页先用 ID 取详情,再用设备 MAC 从列表接口拉一次最新状态并合并,保证手机端详情与 Web 端列表/心跳一致。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备模型 | `lib/models/device.dart`:新增 `copyWith` 方法;`lastSyncTime` 解析兼容 `heartbeatTime` / `heartbeat_time` / `lastHeartbeat` / `last_heartbeat` 等 Web 端心跳命名字段 |
| ✅ 已修复 | 设备服务 | `lib/services/device_service.dart`:`fetchDeviceById` 在拿到详情后调用 `_mergeWithFreshListStatus`:用 `detail.mac` 调 `/device/info/list?mac=...&limit=1` 取最新状态,合并 `status` 与 `lastSyncTime`;列表接口失败时不阻断详情展示 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix59` → `v6-fix60` |
---
## v6-fix61(2026-08-31)
> **修复「设备列表页扫码时弹出『查询失败:登录已过期,请重新登录』」的体验问题。**
>
> 用户操作:设备列表页右上角二维码(`mode=query` 扫码查设备,注意:真正「激活」是底部 + 按钮 `mode=bind&activate=1`)→ 触发 `_queryDevice` 读取 `devicesProvider.future`(`GET /device/info/list`)。当 token 过期:拦截器 `ApiInterceptor.onResponse` 识别到 `code==401` 或「token失效/登录」类文案 → 调 `_refreshAndRetry` 自动刷新令牌并重试一次;若刷新令牌也失败(refreshToken 失效),则 `_notifyUnauthorized()` 清空令牌并触发 `authProvider.onUnauthorized` 回调 → 路由 `redirect` 自动跳 `/login`。问题是拦截器最终 `handler.reject(ApiException(401,'登录已过期,请重新登录'))`,该异常冒泡到扫码页 catch,被 `formatApiError` 取出后以「查询失败:登录已过期,请重新登录」SnackBar 暴露给用户,与登录页跳转形成竞态/困惑。
>
> **修复**:新增 `isUnauthorizedError()` 统一识别登录失效异常;扫码页 4 个 catch(查设备 / 激活绑定 / 加载门店 / 加载分组)在捕获到登录失效时**不再展示底层错误**,直接 `context.go('/login')` 兜底跳转(与拦截器已触发的路由重定向一致)。自动刷新令牌能力保持不变(首次 401 静默重试成功则用户无感)。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 网络层 | `lib/services/api_client.dart`:新增顶层 `isUnauthorizedError(Object?)`,识别 `ApiException(401)` 或含「登录已过期」文案的登录失效异常,供 UI 层复用 |
| ✅ 已修复 | 扫码页 | `lib/screens/device/device_scan_page.dart`:`_queryDevice` / `_confirmBind` / `_pickGroup` / `_pickStore` 四处 catch 增加登录失效判断——命中时跳过错误 SnackBar、改为 `context.go('/login')` 引导重新登录 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix60` → `v6-fix61` |
---
## v6-fix64(2026-08-31)
> **设备列表页登录过期错误提示与扫码页保持一致。**
>
> 之前设备列表页加载失败时,若遇登录过期,会整屏显示「设备列表加载失败:登录已过期,请重新登录」+ 重试按钮(仍用 `error.toString()` 暴露底层异常)。拦截器已通过 `_notifyUnauthorized` 清空令牌并触发路由 `redirect` 跳 `/login`,此处应与之对齐——不再展示底层错误。
>
> **改动**:`lib/screens/home/device_list_page.dart` 的 `devicesAsync.when(error:)` 分支:
> - 命中登录失效(`isUnauthorizedError`)时不再展示错误文案与重试按钮,改为显示加载占位(路由重定向会接管跳登录页);
> - 其他错误改用 `formatApiError(error)` 输出友好文案(不再直接 `error.toString()`)。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备列表 | `lib/screens/home/device_list_page.dart`:error 分支登录失效走加载占位、其余错误用 `formatApiError`;补充 `api_client.dart` 导入 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix63` → `v6-fix64` |
---
## v6-fix63(2026-08-31)
> **精简设置页与设备列表页:移除运行环境切换、显示运行指标开关及设备列表右上角二维码按钮。**
>
> 用户反馈(截图):设置页中「运行环境」与「显示运行指标」两项不需要继续展示,要求直接删掉;设备列表页右上角二维码按钮也直接删掉。
>
> **改动**:
> 1. `lib/screens/profile/settings_page.dart`:
> - 移除「运行环境」`_AppSettingsTile`(含测试/预发切换、`AppConfig.switchEnvironment`、`ApiClient.instance.reconfigure` 等逻辑)。
> - 移除「显示运行指标」`SwitchListTile` 及其对应的 `showRuntimeMetricsProvider` watch。
> - 删除不再使用的导入:`metrics_visibility_provider`、`app_config`、`environment`、`staging_config`、`test_config`;删除不再使用的 `_envLabel` 私有方法。
> 2. `lib/screens/home/device_list_page.dart`:移除 AppBar `actions` 中的二维码 `IconButton`(`mode=query` 扫码入口)。底部的「扫码激活」浮动按钮(`mode=bind&activate=1`)保留。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已删除 | 设置页 | `lib/screens/profile/settings_page.dart`:删除运行环境切换与显示运行指标开关 |
| ✅ 已删除 | 设备列表 | `lib/screens/home/device_list_page.dart`:删除 AppBar 右上角二维码按钮 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix62` → `v6-fix63` |
---
## v6-fix62(2026-08-31)
> **修复扫码查询「未找到设备。请确认二维码内容」且无法识别/连接设备的问题,并优化错误提示。**
>
> 用户反馈:设备列表页右上角扫码后弹「未找到设备。请确认二维码内容」,未能跳转到对应设备。
>
> **根因**:`device_scan_page.dart` 的 `_queryDevice`(query 模式)把扫码 **原始字符串** 直接与 `device.mac`/`deviceSn`/`id` 做小写等值比较,但 bind 模式会用 `_extractMacFromPayload` 解析 JSON(`{"mac":"..."}`)或归一化 MAC。因此当二维码是 `{"mac":"..."}` JSON,或 MAC 分隔符/大小写与设备记录不一致时,query 模式永远匹配不到 → 误报「未找到设备」。此外原提示「请确认二维码内容」过于笼统,对「二维码有效但设备尚未联网/同步」的场景没有引导。
>
> **修复**:
> 1. query 模式新增 `_parseScanPayload`:解析纯 MAC(兼容 `:`/`-`/无分隔)与 JSON(`mac`/`id`/`sn`/`deviceUid` 等字段),并用 `_normalizeMac` 归一化比较,忽略分隔符与大小写差异。
> 2. 本地列表未命中时,按 MAC/SN 调后端 `/device/info/list` 兜底查询,覆盖「设备已激活但本地缓存尚未刷新」的场景,命中即刷新列表并跳转详情。
> 3. 错误提示分级:二维码无法识别(非 MAC 也非含标识的 JSON)→ 「无法识别该二维码,请扫描设备二维码或确认二维码内容」;二维码有效但设备暂未就绪 → 「未找到对应设备(MAC)。设备可能尚未联网或同步,请确认设备已开机联网、已激活并归属当前账号后重试」。
> 4. 登录失效(401)仍按既有逻辑兜底跳登录页。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 扫码页 | `lib/screens/device/device_scan_page.dart`:`_queryDevice` 改为先解析 payload(MAC/JSON)、归一化 MAC 比较;本地未命中时按 MAC/SN 后端兜底查询并跳转详情 |
| ✅ 已修复 | 扫码页 | 新增静态 `_parseScanPayload`(兼容纯 MAC 与 JSON)与 `_normalizeMac`(去分隔符转大写) |
| ✅ 已优化 | 文案 | `lib/utils/i10n.dart`:新增 `scanPayloadUnrecognized`(无法识别二维码)、`scanDeviceNotReady(identifier)`(设备暂未就绪引导);原 `scanDeviceNotFound` 不再使用 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix61` → `v6-fix62` |
---
## v6-fix59(2026-08-28)
> **修复环境切换后再操作导致的 `LateInitializationError`**。
>
> 用户截图显示报错:`LateInitializationError: Field '_dio' has already been initialized.`,调用栈指向 `ApiClient.reconfigure` → `SettingsPage` 的环境切换 SegmentedButton。根因:`ApiClient._dio` 声明为 `late final`,`reconfigure()` 重复调用时会二次赋值,触发 LateInitializationError。修复后支持重复重建,并在环境/语言切换入口增加 try-catch,避免崩溃。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 网络层 | `lib/services/api_client.dart`:`ApiClient._dio` 由 `late final` 改为 `late`,`reconfigure()` 改为返回 `({bool success, String? error})`,允许安全重复调用,内部捕获异常避免抛崩溃 |
| ✅ 已修复 | 设置页 | `lib/screens/profile/settings_page.dart`:环境切换 closure 加 try-catch,先切换环境、重建 Dio、提示「环境已切换,请重新登录」、再 logout;失败时 SnackBar 提示错误。语言切换也加 try-catch 防御 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix58` → `v6-fix59` |
---
## v6-fix58(2026-08-28)
> **移除登录页副标题「设备与系统运维监控」**。
>
> 用户截图显示登录页标题「云定制主页管理」下方仍显示「设备与系统运维监控」一行副标题文字,需移除该文本及相关 UI 元素,保持页面其余布局与功能不受影响。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已移除 | 登录页 | `lib/screens/login/login_page.dart` 删除副标题 `Text('设备与系统运维监控')` 及其上方的 `SizedBox(height: 8)`,登录页不再展示该文字 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix57` → `v6-fix58` |
---
## v6-fix57(2026-08-28)
> **优化 App「用户管理-新增用户」交互(依据 MuMu 录屏 `云定制主页管理(5).mp4`)**。
>
> 打开新增/编辑用户弹窗时,系统默认先弹出「所属组织」选择界面,打断了先填表单(用户名/账号/密码等)的正常流程。调整为:组织选择置于表单填写完成之后,仅用户主动点击「所属组织」字段才展开。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 用户管理-新增/编辑用户 | `lib/screens/admin/sys/user_page.dart` 的 `_UserForm` 中 `OrgPickerFormField` 显式设置 `autoOpen: false`,打开用户表单不再自动弹出组织选择器,需先填完必填信息、再主动点击字段触发 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix56` → `v6-fix57` |
---
## v6-fix56(2026-08-28)
> **修复 App 组织选择器交互问题(依据 MuMu 录屏 `云定制主页管理(2)(3)(4).mp4`)**。
>
> 视频2/3:门店管理 → 点击编辑门店时,「所属组织」选择器在表单打开后自动弹出,影响操作流程。视频4:用户管理 → 编辑用户 → 点击「所属组织」选择器后,列表未将当前已选组织置顶,导致需滑动查找。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 门店编辑页 | `lib/screens/store/store_list_page.dart` 中 `_StoreFormSheet` 的 `OrgPickerFormField` 显式设置 `autoOpen: false`,进入编辑/新增表单时不再自动弹出组织选择器,仅用户主动点击字段时才展开 |
| ✅ 已修复 | 组织选择器 | `lib/widgets/org_picker_sheet.dart` 中 `OrgPickerSheet` 在过滤后把当前 `_selectedId` 对应的选项置顶到列表最前面,保持其他项原有层级顺序;搜索关键字为空时当前选择一目了然,搜索时仍按名称过滤 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix55` → `v6-fix56` |
---
## v6-fix55(2026-08-27)
> **用户纠正**:v6-fix54 后加的"服务器地址"自由输入设置实为一条可切生产的入口(可手填生产 IP `58.220.83.58`),违反"仅测试环境"要求。本版本移除该入口,改为"强制防生产切换"机制:默认锁定测试环境,永不可进入生产模式。
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已重构 | 环境配置 | 新增 `lib/config/` 模块:`AppEnvironment` 枚举(仅 `test` / `staging`,**刻意不含 production**)、`TestConfig` / `StagingConfig`(测试/预发敏感配置分文件独立管理,禁止交叉引用生产)、`AppConfig`(按当前环境解析 origin/前缀/验证码开关,默认 `test`)、`ProductionGuard`(启动期强制守卫) |
| ✅ 已移除 | 可切生产入口 | 删除 `lib/utils/server_config.dart` 及设置页"服务器地址"自由输入对话框(原可手填任意 URL 含生产 IP);所有网络层(`api_client` / `auth_service` / `marquee_web_source`)改为读 `AppConfig` 当前环境地址 |
| ✅ 已新增 | 启动守卫 | `main.dart` 启动即调用 `ProductionGuard.enforce()`:① 编译期 `assert(!kAllowProduction)`(kAllowProduction 恒 false);② 运行期解析当前后端 host,若命中生产拒绝名单(`58.220.83.58` / `api.ainetv.com` / `ainetv.com` 或含 `prod`)则 `print` 清晰报错并 `exit(1)` 阻断启动,确保系统无法进入生产模式 |
| ✅ 已新增 | 环境切换函数 | `AppConfig.switchEnvironment(AppEnvironment)` 仅接受预定义 `test` / `staging` 枚举值,显式排除 production,禁止解析/接收任何生产标识字符串;设置页仅提供 TEST/预发 分段切换(无生产按钮) |
| ✅ 已清理 | 常量 | `AppConstants` 移除 `apiBaseUrl` / `apiFullBaseUrl` / `enableCaptcha`(原注释含生产 IP 兜底),仅保留 `apiPrefix`;默认配置不再含任何生产默认值/兜底/隐式生产配置 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix54` → `v6-fix55` |
| ✅ 已验证 | 构建 | `flutter analyze` 0 error;release APK `ops-console-release-v6-fix55.apk`(33.9MB)验鲜:`v6-fix55`=1、`ProductionGuard`=1、默认 origin=`ips-test.ainetv.com`(测试);prod-ip 仅出现在守卫拒绝名单中,非活动端点 |
---
## v6-fix54(2026-08-27)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | Web-公司介绍内容展示 | 修复 Web 端「公司介绍」选中大类后右侧小类列表偶发"暂无数据"的问题:`parseIntroSubcategoryList` / `parseIntroDetailList` 现在兼容后端 `{data: [...]}` 与 `{data: {list, totalCount}}` 两种返回;`loadChildren` 始终以当前 `treeData` 重新定位节点,避免 `selectedNode` 引用过期,并在 API 解析为空时使用树节点已携带的完整小类数据兜底展示 |
| ✅ 已优化 | App-公司介绍数据加载 | 门店内容页加载大类由 `getIntroCategoryAll()` + 客户端过滤改为 `getIntroCategoryList(page, limit, orgId)` 服务端过滤,与 Web 端 `store-company-intro.vue` 的查询方式完全一致,避免门店权限/分页差异导致数据不一致 |
| ✅ 已优化 | App-公司介绍保存字段 | 小类表单的 `categoryType` 预填父大类值;保存时若为空则不下发,与 Web 端保存行为对齐,避免空字符串覆盖后端默认值 |
| ✅ 已调整 | 文案 | 移除代码注释中的"门店维度"表述;管理首页入口、页面标题继续使用 `companyIntro`/`introCategoryTitle`(即"公司介绍") |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix53` → `v6-fix54` |
---
## v6-fix53(2026-08-27)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已重构 | 公司介绍-门店维度 | 将公司介绍入口从「大类→小类→详情」三级结构改为「门店维度」展示:新增 `IntroStoreListPage` 门店列表页(参考门店管理页布局,支持关键字搜索门店),点击进入 `IntroStoreContentPage` 门店介绍内容页 |
| ✅ 已新增 | 公司介绍-门店介绍内容页 | 按门店加载其下所有公司介绍大类,并按 `categoryType` 内容类型分组呈现;每个分类可展开查看小类,每个小类可展开查看详情内容;数据层级:门店 → 内容类型 → 分类 → 小类 → 详情 |
| ✅ 已保留 | 公司介绍-原管理入口 | 原 `/admin/intro/category` 三级管理页保留,可作为后台管理备用入口;管理首页公司介绍入口改为 `/admin/intro/store-list` |
| ✅ 已新增 | 公司介绍-快捷操作 | 门店内容页支持直接在分类/小类/详情层级新增、编辑、删除;新增分类时自动带入当前门店 `orgId` |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix52` → `v6-fix53` |
---
## v6-fix52(2026-08-27)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 公司介绍-小类/详情图片上传 | 根因:原 `UploadService.uploadImage` 走 `POST /sys/upload` 通用本地上传,与公司介绍模块(Web 端 `company-intro-subcategory-form.vue`)使用的腾讯云 COS STS 直传通道不一致,导致上传失败、无法保存。现改为对齐 Web 流程:计算文件 MD5 → `GET /theme/resource/check-md5` 秒传判断 → `GET /theme/upload/credential` 取 STS 临时凭证(bucket/region/key)→ 用临时密钥直传 COS(putObject,原生 q-sign-algorithm=sha1 签名)→ `POST /theme/resource/register` 注册 `tv_resource` → 返回 `https://ipscdn.ainetv.cn/{key}` 访问地址 |
| ✅ 已新增 | 依赖 | 新增 `crypto`(文件 MD5);腾讯云 COS 直传客户端 vendoring 到 `lib/services/cos/cos_client.dart`(基于 flutter_cos_client MIT 实现,修复签名有效期硬性 `now+3600` 与后端 STS `ExpiredTime` 冲突导致的 403,去掉未维护 0.0.1 包依赖与 mime 依赖) |
| ✅ 已完善 | 错误提示 | 上传各阶段(凭证不完整 / 云存储失败 / 资源注册失败)统一抛出 `ApiException`,message 为可展示中文;前端 `catch` 已能显示 `e.message`,便于定位失败原因 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix51` → `v6-fix52` |
---
## v6-fix51(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 公司介绍-新增/修改 | 修复「保存失败」提示不展示后端真实原因的问题:拦截器返回的是 `DioException`(其 `.error` 才是 `ApiException`),原代码 `e is ApiException ? e.message : t.saveFailed` 永远走兜底,导致只能看到「保存失败」。三个表单(大类/小类/详情)的保存与删除异常提示统一改为 `formatApiError(e)`,可正确显示后端 msg |
| ✅ 已修复 | 公司介绍-新增大类 | 与 Web 前端字段对齐:`saveIntroCategory` 请求体移除后端未使用的 `orgName`;`orgId` 在发送前 `int.tryParse` 转为数字,与 Web 一致 |
| ✅ 已新增 | 公司介绍-表单校验 | 新增大类/小类/详情保存前进行客户端校验:名称/标题/类型编码/媒体地址非空;类型编码禁止中文。减少无效请求,错误反馈更即时 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix50` → `v6-fix51` |
---
## v6-fix50(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 网络层(写操作失败) | `api_client.dart` 的 `ApiInterceptor.onRequest` 不再给 POST/PUT 等业务 body 注入防缓存字段 `t`:此前该字段会作为未知列进入后端 insert,导致「列表能读、新增/修改 500 失败」。防缓存 `t` 仅在 GET 的 query 上保留;写入请求 body 保持纯净 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix49` → `v6-fix50` |
---
## v6-fix49(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已更新 | 后端环境切换 | `constants.dart` 的 `apiBaseUrl` 由生产环境 `http://58.220.83.58` 切回测试环境 `https://ips-test.ainetv.com`(HTTPS);生产地址保留为注释便于后续切回 |
| ✅ 已恢复 | 登录验证码 | `enableCaptcha` 由 `false` 恢复为 `true`:测试环境 `ips-test.ainetv.com` 提供 `/api/captcha.jpg`(HTTP 200),登录页重新显示验证码输入与刷新 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix48` → `v6-fix49` |
---
## v6-fix48(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已更新 | 桌面/启动图标 | 将 `com_ainetv_ainetvcloud_xxxhdpi_640dpi_云管理.png` 生成并覆盖 Android 5 个密度(mdpi/hdpi/xhdpi/xxhdpi/xxxhdpi)的 `mipmap-*/ic_launcher.png` |
| ✅ 已更新 | 登录页 logo | 登录页继续使用 `assets/images/logo.png`,已同步替换为同一源图 |
| ✅ 已更新 | 应用内图标 | 设置页「关于」弹窗新增 `applicationIcon`,统一使用 `assets/images/logo.png`(64×64 显示) |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix47` → `v6-fix48` |
---
## v6-fix47(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 登录页 logo | 将顶部蓝色 `Icons.monitor_heart` 图标替换为橙色云朵 logo 图片(`assets/images/logo.png`,文字已改为「云管理」,保留底部「ainetv」) |
| ✅ 已修复 | 验证码加载失败 | 新增 `AppConstants.enableCaptcha` 开关(默认 `false`):关闭时隐藏验证码输入区与刷新按钮,登录时 captcha/uuid 传空串,避免生产环境 `58.220.83.58` 未暴露 `/api/captcha.jpg` 导致登录页报错 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix46` → `v6-fix47` |
---
## v6-fix46(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已更新 | 后端环境切换 | `constants.dart` 的 `apiBaseUrl` 由测试环境 `https://ips-test.ainetv.com` 切换为生产环境 `http://58.220.83.58`(明文 HTTP,Android 已开启 `usesCleartextTraffic`);测试地址保留为注释便于回退 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix45` → `v6-fix46` |
---
## v6-fix45(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 手机端全屏显示 | 修复应用在手机上未全屏、四周出现黑边的问题:`main.dart` 启动时设置 `SystemUiMode.edgeToEdge` + 透明状态栏/导航栏;`styles.xml` / `styles-night.xml` 的 `NormalTheme` 启用 `windowDrawsSystemBarBackgrounds` 并将状态栏/导航栏颜色设为透明 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix44` → `v6-fix45` |
---
## v6-fix44(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已移除 | 公司介绍门店筛选 | 移除大类列表顶部「筛选门店」FilterChip 区域(含「全部门店」多选),保留大类/小类/详情三级列表、表单、树形组织选择器、图片/视频上传等其余所有功能与布局 |
| ✅ 已清理 | 冗余代码 | 移除 `_stores`、`_selectedOrgIds`、`_loadStores`、`_applyOrgFilter`、`_buildOrgFilter` 及 `StoreInfo` import;`_storeName` 改为仅从组织树回查;`initState` 改调用 `_loadOrgNodes()` 以支持列表门店名称回显 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix43` → `v6-fix44` |
---
## v6-fix43(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已调整 | 公司介绍门店选择器节点规则 | 树形组织选择器仅展示渠道(type=1)、代理商(type=2)与门店(type=3);渠道与代理商节点 `selectable=false`(禁用、灰色、不可点击);门店节点保持可选;平台(type=0)与设备分组(type=4)不展示 |
| ✅ 已新增 | 节点选择工具方法 | 新增 `_isOrgVisible`、`_isOrgSelectable`,统一控制选择器内组织节点的展示与可选状态 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix42` → `v6-fix43` |
---
## v6-fix42(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已改造 | 公司介绍大类门店选择器 | 「所属门店」从 DropdownButtonFormField 改为与用户管理一致的树形组织选择器:底部弹窗、实时搜索组织名称、按组织层级缩进展示;复用 `OrgPickerSheet`/`OrgPickerFormField` 组件,数据走 `OrgService.getOrgTree()`(组织树 / flatList / select 三阶回退) |
| ✅ 已实现 | 编辑态自动高亮当前节点 | 编辑大类时 `OrgPickerFormField` 自动展开选择器,并默认选中高亮当前 `orgId` 对应的门店组织节点;若当前 category 所属组织不在用户可见树中,自动补入选项保证可展示 |
| ✅ 已新增 | 组织树工具方法 | `intro_category_page` 新增 `_loadOrgNodes`、`_buildOrgPickerOptions`、`_buildHierarchicalOptions`、`_buildHierarchyFromFlatList`、`_findOrgByName`、`_findOrgById`,与用户管理 `_UserForm` 对齐 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix41` → `v6-fix42` |
---
## v6-fix41(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 公司介绍大类门店选择 | 修复新增/编辑大类时「所属门店」下拉为空、无法选择门店的问题:`intro_category_page._loadStores` 改用 `OrgService.getMyStores()`(组织树 / myStores / listByLoginOrg 三阶回退),覆盖当前用户可见的所有门店;表单打开前强制重新加载门店;编辑时若当前 category 所属门店不在返回列表中,自动补入下拉项;空字符串 `orgId` 统一视为未选择,避免收起态显示空白 |
| ✅ 已新增 | 国际化 | `i10n.dart` 新增 `introStoreLoadFailed`(门店列表加载失败提示)中英 key |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix40` → `v6-fix41` |
---
## v6-fix38(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 公司介绍多门店筛选 | 大类列表页门店筛选由单选下拉改为 `FilterChip` 多选,空选表示「全部门店」,数据按所选门店列表实时过滤 |
| ✅ 已实现 | 公司介绍图片上传 | 新增 `UploadService`,小类/详情表单支持从相册选择图片、本地即时预览、Dio `FormData` 上传至 `/sys/upload`,成功后自动回填 `mediaUrl` 并切换为网络预览;保留手动输入 `mediaUrl` 兜底 |
| ✅ 已对齐 | 大类表单 | 新增「所属门店」必填下拉(`orgId`/`orgName` 联动);移除 Web 后台不存在的 `status` 字段,保存字段与 Web 端一致 |
| ✅ 已对齐 | 小类/详情表单 | 新增「排序号」字段;`status` 语义与 Web 后台统一为 `1=启用/0=禁用`;小类新增可选「类型编码」字段(默认预填父大类编码) |
| ✅ 已改造 | 保存接口分流 | `IntroService` 三个 `save*` 方法按 `id` 是否存在分流到 `/save`(新增)或 `/update`(编辑),与 Web 端 `companyIntro.ts` 保持一致 |
| ✅ 已修正 | 状态语义 | `IntroCategory`/`IntroSubcategory`/`IntroDetail` 的 `_parseStatus` 改为后端原值映射(1=启用,0=禁用),列表 Chip 文案同步改为「启用/禁用」 |
| ✅ 已新增 | 国际化 | `i10n.dart` 补充 `introFilterStores`、`introSelectStoresHint`、`introCategoryStore`、`introStoreRequired`、`introSortOrder`、`introEnabled`、`introDisabled`、`introUploadImage`、`introPickImage`、`introImagePreview`、`introUploading`、`introUploadFailed`、`introInsertImage` 中英 key |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix37` → `v6-fix38` |
---
## v6-fix40(2026-08-26)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 滚动字幕速度档位化 | 数字速度输入改为 5 档下拉选择(慢/较慢/中/较快/快),新增 `MarqueeConfig`/`MarqueeSpeed` 配置结构,提供 `setSpeed`/`setGrade`/`copyWith` 接口与 `speedFromValue`/`gradeFromValue` 历史值吸附;落库仍为 Web 端原值(200/100/50/30/10),卡片与表单直观显示当前生效档位 |
| ✅ 已实现 | 滚动字幕级别选择 | 新增 3 级「级别」下拉(危险/紧急/普通),落库原值(50/40/20),与 Web 端 `scrolling-text.data.ts` 一致;列表与表单显示当前生效级别 |
| ✅ 已实现 | 滚动字幕字体颜色色板 | 「字体颜色 / 背景颜色」由手动输入 `#HEX` 改为对齐 Web 端 `el-color-picker` 的色板弹窗:饱和度/明度 2D 选择区 + 色相条 + HEX 输入框 + 实时预览 + 取消/确定,回填 `#RRGGBB` |
| ✅ 已修复 | 公司介绍大类门店名称 | 新增/编辑大类「所属门店」选中后正确展示门店组织名称:下拉增加 `selectedItemBuilder` 确保收起态渲染所选名称;列表卡片在接口未返回 `orgName` 时按 `orgId` 从本地门店列表回查名称 |
| ✅ 已对齐 | 公司介绍小类图片上传 | 小类素材固定为图片(与 Web 端一致,Web 端无媒体类型选择、始终 `mediaType=image`);新增图片类型(jpg/png/gif/webp)与大小(≤300KB,建议 1920×1080)校验,超限给出提示;上传仍走后端 `/sys/upload`,返回 URL 存入 `mediaUrl` |
| ✅ 已对齐 | 公司介绍详情图片/视频上传 | 详情表单保留「媒体类型」图片/视频切换;图片(≤3MB)/ 视频(≤30MB)类型与大小校验与 Web 端一致,视频走 `pickVideo`;上传路径与返回结构同小类 |
---
## v6-fix39(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 滚动字幕起止时间筛选 | 「滚动字幕」新增/编辑表单的开始时间、结束时间改为纯 `HH:mm:ss` 滚轮选择器,不再复用日期时间选择器,与 Web 端 `/theme/scrollingText` 时间字段格式(`HH:mm:ss`)一致 |
| ✅ 已实现 | 时间滚轮控件 | 新增 `_TimePickerSheet` 底部弹窗:三列 `ListWheelScrollView` 滚轮(时 00-23 / 分 00-59 / 秒 00-59),`FixedExtentScrollController` 定位初始值;底部「取消/确定」按钮,确定后回传 `DateTime(2000,1,1,h,m,s)`,与 `MarqueeMessage._parseDateTime` 基准日期一致 |
| ✅ 已对齐 | 默认值与范围 | 新增字幕默认起止时间为 `00:00:00` ~ `23:59:59`;编辑时回显既有 `startTime`/`endTime`;秒级精度,可选范围覆盖全天,与 Web 后台一致 |
| ✅ 已新增 | 国际化 | `i10n.dart` 补充 `marqueeHour`/`marqueeMinute`/`marqueeSecond`(时/分/秒)中英 key,供滚轮单位标签使用 |
| ✅ 已清理 | 死代码 | 移除 `_TimePickerSheet._buildWheel` 中的未使用 `selected` 局部变量,消除 analyze 警告 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix38` → `v6-fix39` |
---
## v6-fix37(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 组织选择器高亮 | `OrgPickerSheet` 列表项选中时立即通过本地 `_selectedId` 状态更新高亮背景、文字颜色与对勾图标,并延迟 120ms 后关闭弹窗,解决选中条目需要额外按一次才显示高亮的问题 |
| ✅ 已优化 | 选择器视觉反馈 | 已选项文字加粗并显示 `Icons.check_circle`,高亮底色透明度由 `0.24` 提升至 `0.38`,选中状态与视觉反馈同步呈现 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix36` → `v6-fix37` |
---
## v6-fix36(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 滚动字幕数据源 | 移动端 `MarqueeWebSource` 默认接口由 `/marquee/list` 改为与 Web 后台一致的 `/theme/scrollingText/list`;解析 `totalCount` 与 Web 端字段映射(`orgId`→`storeId`、`orgName`→`storeName`、`scrollSpeed`→`speed`、`bgColor`→`backgroundColor`),解决移动端列表为空的问题 |
| ✅ 已修复 | 滚动字幕新增/编辑 | 修复 `_MarqueeFormState` 中 `late String? _storeId` 未在 `initState` 初始化导致的 `LateInitializationError`;新增「状态」下拉框(启用/禁用),表单提交携带真实状态 |
| ✅ 已改造 | 滚动字幕服务 | `MarqueeService` 新增/编辑/删除/启用/禁用均优先调用 Web 后台 `/theme/scrollingText/save|update|delete`;失败时回退本地 `SharedPreferences` 缓存,保证编辑内容实时同步到桌面与移动端 |
| ✅ 已优化 | 列表卡片 | `_StatusChip` 改为「启用/禁用」;`_MarqueeCard` 操作按钮改为「启用/禁用」;时间范围展示改为 `HH:mm:ss`,与 Web 后台一致 |
| ✅ 已新增 | 国际化 | `i10n.dart` 补充 `marqueeStatus`、`marqueeStatusEnabled`、`marqueeStatusDisabled` 中英 key |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix35` → `v6-fix36` |
---
## v6-fix35(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已重构 | 设备详情页 | `DeviceDetailPage` 按需求分四个清晰区块:**基本信息**(设备名称、设备 ID、型号、状态)、**配置参数**(所属门店、分组、房号、位置、操作系统、设备序列号、设备 UID、创建时间)、**网络信息**(IP 地址、MAC 地址)、**运行时数据**(在线时长、最后活跃时间);原信息卡拆分为带标题的分区块卡片,缺口字段(设备名称 / 设备 ID / 最后活跃时间)已补齐并回显 |
| ✅ 已更新 | 详情头部 | 标题由「型号」改为「设备名称」(型号降级为副标题),与列表点击进入的语义一致 |
| ✅ 已新增 | 国际化 | `i10n.dart` 补充 `basicInfo`、`configParams`、`networkInfo`、`runtimeData`、`deviceName`、`deviceId`、`modelLabel`、`deviceSnLabel`、`deviceUidLabel`、`createTime`、`lastActiveTime` 等中英 key |
| ✅ 已保留 | 刷新同步 | 详情页下拉刷新(`RefreshIndicator`)刷新 `deviceDetailProvider`,与设备列表数据保持一致;「下发指令 / 修改分组」按钮保持可用 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix34` → `v6-fix35` |
---
## v6-fix34(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | Web 端滚动字幕数据源 | 新增 `lib/services/marquee_web_source.dart` 作为独立的 Web 端内容获取层;支持配置请求地址(`baseUrl` / `path`)与请求方式(`GET/POST` 等),内置超时、网络异常、HTTP 错误、业务码错误、解析失败等完善错误处理;将结果封装为结构化 `MarqueeFetchResult` |
| ✅ 已扩展 | 滚动字幕数据模型 | `MarqueeMessage` 新增 `fontColor`、`backgroundColor`、`fontSize`、`level`、`startTime`、`endTime` 字段,兼容 Web 端返回的 JSON 格式 |
| ✅ 已改造 | 字幕服务 | `MarqueeService.getMarqueeList` 优先调用 `MarqueeWebSource` 获取 Web 端数据,失败时自动回退到本地 `SharedPreferences` 缓存 |
| ✅ 已更新 | UI | `MarqueeManagementPage` 列表卡片展示字体颜色、背景颜色、字体大小、级别、生效时间;新增/编辑表单支持设置颜色、字号、级别与起止时间 |
| ✅ 已新增 | 国际化 | `i10n.dart` 补充字体颜色、背景颜色、字体大小、级别、开始/结束时间等中英 key |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix33` → `v6-fix34` |
---
## v6-fix33(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 滚动字幕管理 | 在「管理」界面新增「滚动字幕管理」入口;新增 `MarqueeManagementPage`(`/admin/marquee`),支持按门店查看、新增、编辑、删除、查询滚动字幕,并区分不同门店内容 |
| ✅ 已实现 | 下发/取消下发 | 字幕状态区分为「待下发」与「已下发」;点击「下发」调用设备指令 `marqueeUpdate` 按组织下发到门店设备,成功后状态更新为已下发;点击「取消下发」重置为待下发 |
| ✅ 已新增 | 数据模型 | `lib/models/marquee_message.dart` 定义滚动字幕实体;`lib/services/marquee_service.dart` 基于 `SharedPreferences` 实现字幕记录本地持久化与 CRUD,并提供下发/取消下发能力 |
| ✅ 已新增 | 国际化 | `lib/utils/i10n.dart` 补充滚动字幕管理相关 key(管理标题、新增/编辑、字幕内容、滚动速度、目标门店、状态、下发/取消下发等) |
| ✅ 已更新 | 管理首页 | `lib/screens/admin/admin_hub_page.dart` 增加「滚动字幕管理」入口卡片 |
| ✅ 已更新 | 路由 | `lib/app/router.dart` 增加 `/admin/marquee` 路由 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix32` → `v6-fix33` |
---
## v6-fix32(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 激活码管理 | 英文语言下激活码管理页所有中文硬编码文案(门店选择、当前激活码、复制、最大激活数、已激活数、创建时间、生成/重置激活码、生成弹窗等)全部替换为 `AppLocalizations` 国际化 key,切换英文后完整显示英文 |
| ✅ 已新增 | 国际化 | `i10n.dart` 补充 `copy`、`generate`、`generateActivationCodeHint`、`activationCodeCopied`、`activationCodeLoadFailed`、`generateFailedWithError` 等 key |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix31` → `v6-fix32` |
---
## v6-fix31(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 管理模块返回键 | 管理中心「设备管理」「激活码」入口原 `context.push('/device')` / `context.push('/device/activation')` 会把页面压入**设备分支**导航栈,导致底部导航错乱 + 左上角返回箭头显示/点击异常 → 在 `/admin` 分支下新增别名路由 `/admin/device`(复用 `DeviceListPage`)与 `/admin/activation`(复用 `DeviceActivationPage`),入口改为 `context.push('/admin/device')` / `context.push('/admin/activation')`;仍保留 `push` 方式,用户可通过系统返回键回到管理首页 |
| ✅ 已修复 | 设备列表 AppBar | `DeviceListPage` 的 `AppBar` 增加 `automaticallyImplyLeading: false`,从管理模块进入时不显示左上角返回箭头(`DeviceActivationPage` 此前已设置,保持生效) |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix30` → `v6-fix31` |
⚠️ 备注:
- 原 `/device` 与 `/device/activation` 路由**保留不变**(设备 tab 入口及设备详情内的跳转仍使用),本次仅新增 `/admin` 分支下的别名路由,无命名冲突。
---
## v6-fix30(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 角色选择器 | 用户编辑页「角色」字段由下拉框改为可视化 `ChoiceChip` 横向选择器;编辑时根据 `roleId`/`roleName`/`roleNames` 自动选中对应角色,选中项显示主题色高亮背景 + `Icons.check` 勾选图标;角色来自动态 `widget.roles`,新增用户默认选中当前登录用户角色 |
| ✅ 已删除 | 全局返回悬浮按钮 | 移除 `AppBottomNav` 中的全局返回 FAB(`_BackFabLocation`、`floatingActionButton`、`floatingActionButtonLocation`)及 `navigator_key.dart` 导入;同步删除不再被引用的 `lib/app/navigator_key.dart`,并移除 `router.dart` 中的 `navigatorKey` 引用 |
| ✅ 已删除 | AppBar 返回键 | 扫描 `lib/screens/**` 所有 `AppBar`,移除自定义 `leading` 返回按钮(`device_scan`/`device_group`/`store_config` 三页),并对全部子页面 `AppBar` 设置 `automaticallyImplyLeading: false`,统一移除左上角返回箭头,保留标题与 actions |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix29` → `v6-fix30` |
---
## v6-fix29(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 组织选择器 | 新建/编辑门店与用户编辑页「所属组织」字段进入页面时自动弹出选择弹窗,已选项自动高亮勾选;弹窗支持关键字实时搜索与层级缩进展示 |
| ✅ 已实现 | 返回悬浮按钮 | 在 `AppBottomNav` 外层 Scaffold 增加全局返回悬浮按钮(左上角、仅非首页显示),点击优先 `pop`,不可 pop 时回到首页 |
| ✅ 已删除 | AppBar 返回键 | 移除「设备管理」「用户管理」「激活码」「公司介绍」四页 AppBar 左上角返回箭头,统一由全局返回悬浮按钮负责返回 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix28` → `v6-fix29` |
---
## v6-fix28(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 默认选中 | 门店编辑页与用户管理编辑页进入时,已关联的组织/角色选项自动高亮选中;用户编辑时若后端未返回 ID,通过名称反向匹配并增加只读占位项兜底展示 |
| ✅ 已修复 | 门店组织搜索 | 编辑门店「所属组织」搜索框输入关键字后,新增实时搜索结果列表;点击结果立即更新 `_parentId`、清空搜索关键字并刷新页面,修复「选中无反应」问题 |
| ✅ 已优化 | 门店默认上级 | 编辑门店加载组织树后,若 `store.parentId` 为空,自动在组织树中按 `store.orgId` 查找门店节点并取其 `parentId` 作为默认上级;新增/仍为空时回退到当前用户所在组织 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix27` → `v6-fix28` |
---
## v6-fix27(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已优化 | 门店表单 | 编辑门店「所属组织」下拉选项仅渲染渠道(type=1)与代理商(type=2),门店类型(type=3)已过滤隐藏;新增组织名称关键字搜索框,输入时实时模糊匹配并动态筛选渠道/代理组织 |
| ✅ 已优化 | 默认选中 | 新增门店时,若未指定上级组织则默认选中当前登录用户所在组织;新增用户时,默认选中当前登录用户的所属组织与角色 |
| ✅ 已更新 | 用户模型 | `User` 模型新增 `orgId`/`roleId` 字段;`AuthService` 从登录响应与 `/sys/user/info` 中解析并持久化,供表单默认选中使用 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix26` → `v6-fix27` |
---
## v6-fix26(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 用户编辑 | 编辑用户时角色字段为空 → 当列表接口未返回角色信息时,自动调用 `/sys/user/info/{id}` 拉取详情;同时通过 `roleName`/`roleNames` 反向匹配 `roleId`,确保角色下拉与用户信息页/Web 端展示一致 |
| ✅ 已优化 | 用户表单 | 新增/编辑用户「所属组织」下拉改为按组织层级自上而下排序:优先调用 `/sys/org/tree`,递归扁平化为带深度的选项并逐层缩进展示;失败时回退到 `/sys/org/flatList` 并按 `parentId` 重建层级顺序 |
| ✅ 已修复 | 返回按钮 | 管理模块入口进入的「设备列表」「激活码管理」「用户管理」页面左上角返回箭头点击无响应 → 统一将返回目标改为 `/admin`,确保回到管理首页 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix25` → `v6-fix26` |
---
## v6-fix25(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已优化 | 门店表单 | 编辑门店「所属组织」下拉列表改为按组织层级自上而下排序:调用 `/sys/org/tree` 获取树形结构,递归扁平化为带深度的选项;列表中从最上级组织开始,逐层缩进展示其下级组织,层级关系清晰 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix24` → `v6-fix25` |
---
## v6-fix24(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 修改分组 | 分组下拉仍展示全部门店分组 → 修复 `/device/group/list` 接口参数:Web 端按 `orgId` 过滤门店,移动端此前只传 `storeId` 导致后端忽略门店过滤;现同时携带 `orgId` 与 `storeId`,确保仅返回当前设备所属门店的分组 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix23` → `v6-fix24` |
---
## v6-fix23(2026-08-25)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 修改分组 | 分组下拉报 `DioException [unknown]: null` → 改为调用已有接口 `/device/group/list?storeId=xxx`;同时增强 `Device.storeId` 解析,支持从 `store`/`storeInfo`/`org` 嵌套对象取门店 ID |
| ✅ 已修复 | 新建门店保存 | 保存仍提示「未知异常」→ `_save()` 先按 Web 端字段契约(`type='STORE'`/`status='ACTIVE'`)提交;失败时自动 fallback 到旧后端兼容格式(`type=3`/`status=1`),并展示后端返回的具体错误信息 |
| ✅ 已修复 | 首页快捷操作 | 「系统管理」文字/图标有误 → 改为「用户管理」并替换为 `Icons.people` |
| ✅ 已修复 | 返回按钮 | 管理模块下的「设备管理」「激活码」「用户管理」「公司介绍」页面左上角返回键无响应 → 统一为各页 AppBar 增加显式 `leading` 返回按钮(可 pop 时 pop,不可 pop 时回退到 `/admin`) |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix22` → `v6-fix23` |
---
## v6-fix22(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已调整 | 首页快捷操作 | 移除「媒资管理」入口;当前项目阶段暂不需要,后续如需可在 `HomePage._buildDashboard` 中恢复 `_QuickAction(mediaManagement)` |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix21` → `v6-fix22` |
---
## v6-fix21(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 修改分组 | 分组下拉改为仅展示当前设备所属门店的分组:调用门店专用接口 `/sys/store/deviceGroups/{storeId}`,避免 `/device/group/list` 跨门店返回全部分组;无门店上下文时保留「未分组」与当前分组可选 |
| ✅ 已修复 | 返回按钮 | 扫码激活页左上角返回键无响应 → AppBar 增加显式 `leading` 返回按钮;全面排查并把设备列表/扫码页/首页管理入口/管理中心的 `context.go` 改为 `context.push`,确保子页面返回正常;为设备分组页、门店配置页也增加显式返回兜底 |
| ✅ 已修复 | 门店保存 | 门店保存仍失败(提示「未知异常,请联系管理员」)→ 对齐 Web 端 `/sys/org/saveOrUpdate` 字段契约:`type` 改为字符串 `'STORE'`,新增 `orderNo`/`status`,补充 `storeProfile` 对象(storeName/phone/address/province/city);移除后端不识别字段;所属组织下拉仅展示代理商(type=2),与 Web 端一致 |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix20` → `v6-fix21` |
⚠️ 备注:
- 门店保存字段已按 Web 端「新增门店」payload 重构;若后端仍返回具体业务错误,弹窗会展示后端 msg,请把完整错误文字发我进一步对齐。
- 返回按钮修复后,从底部导航进入的 tab 根页(首页/设备/门店/管理/我的)仍保持 `go` 替换,子页面统一使用 `push`,返回行为与原生一致。
---
## v6-fix20(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 新增门店 | 门店新增保存时报错 → `_save()` 增加「所在地区」「所属组织」必选项校验;保存失败/成功/校验失败均通过页面中央 `AlertDialog` 提示,避免 SnackBar 被遮挡 |
| ✅ 已优化 | 门店表单 | 新增 i18n 词条 `regionRequired`/`parentOrgRequired`;硬编码中文接入国际化 |
| ✅ 已修复 | 激活码管理 | 「重置激活码」结果提示改为页面居中 `AlertDialog` 展示(成功/失败均居中),替代原左下角 SnackBar |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix19` → `v6-fix20` |
---
## v6-fix19(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备列表 | 顶部搜索框 hint 被截断(`搜索设备名称 / 房号`)→ 顶部改为两行布局:第一行放门店下拉 + 型号筛选,第二行放全宽搜索框,确保 hint 完整显示;i18n `searchDeviceHint` 中英文词条已确认同步 |
| ✅ 已修复 | 设备详情 | 遥测字段(位置 / 操作系统 / 运行时长)全显示空或 `0分` → `Device.fromJson` 新增详情响应兜底解包(`{device|info|data|result|record}` 嵌套),并对 location/os/uptime 等做常见字段别名兼容;详情卡空值统一回退为「无」,保证 UI 不崩溃 |
| ✅ 已修复 | 修改分组 | 弹窗原显示「所属分组 ID」文本输入框 → 改为 `DropdownButtonFormField` 分组名称下拉,进入时按设备所属门店拉取 `/device/group/list`,下拉显示分组名称(`groupName`)、提交 `groupId`,与 Web 端一致;无门店上下文时安全降级 |
| ✅ 已修复 | 公司介绍 | 左上角返回箭头点击无响应 → AppBar 增加显式 `leading` 返回按钮:`context.pop()` 回管理中心,内部层级逐级返回,无法 pop 时兜底跳转 `/admin` |
| ✅ 已修复 | 编译 | 修复 `Device.fromJson` 引用未定义的 `_firstNum` 导致的编译错误(原交付版本潜在隐患);补充 `num? _firstNum(...)` 静态辅助方法 |
| ✅ 已优化 | i18n | 新增 `back`(返回)、`groupListLoading`(加载分组列表…)等词条,弹窗内所有硬编码中文均接入 `lib/utils/i10n.dart` |
| ✅ 已更新 | 版本号 | 登录页底部版本号 `v6-fix18` → `v6-fix19` |
⚠️ 备注:
- 设备详情遥测依赖后端 `/device/info/info/{id}` 实际返回字段;已做 `device/info/data` 等多层包络与 `cpu_usage`/`os`/`uptime` 等常见别名兼容,若后端确无数据则统一显示「无」,不崩溃。
- 修改分组下拉依赖设备携带的门店 ID(`Device.storeId`);若该字段后端未返回,下拉仅含「未分组」与当前分组,仍可正常提交。
⏳ 待验收:4 项修复在真机/模拟器上的实际表现。
---
## v6-fix18(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 新增门店 | 激活码改为由系统依据 web 端已有规则自动生成:移除手动输入框,保存门店后若终端数>0 自动调用 `/device/activationCode/generate`;每次点击「创建激活码」仅生成一个唯一码 |
| ✅ 已实现 | 门店编辑 | 编辑页直接展示「最大终端数 / 已用终端数」及当前正在使用的激活码,并提供「创建激活码」按钮 |
| ✅ 已修复 | 门店详情 | 创建时间字段空白 → `StoreInfo` 兼容 `createTime`/`create_time`/`createdTime`/`createdAt`/`created_at` 等常见别名;列表项与详情弹窗均补充展示创建时间 |
| ✅ 已修复 | 语言切换 | 切换英文后启动报错「token 失效」→ `ApiInterceptor` 刷新 token 成功后同步持久化到 `CredentialStorage`,避免语言切换/进程重启后恢复到旧令牌 |
| ✅ 已修复 | 扫码页 | 英文模式下扫码页全部文案接入 i10n,包括标题、提示、权限、弹窗、成功/失败提示等 |
| ✅ 已优化 | 门店表单 | 省市区、详细地址、联系电话、所属组织、保存/创建按钮等剩余硬编码中文全部接入 i10n |
⏳ 待验收:新增门店自动生成激活码、编辑页终端数/激活码展示、英文切换不报错、扫码页英文文案。
---
## v6-fix17(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备模型 | 新增 `Device.model` 字段,按 `model`/`modelName`/`deviceModel`/`productModel`/`productName`/`board`/`hardware`/`brand`/`deviceType`/`type` 等常见别名解析真实型号 |
| ✅ 已修复 | 设备显示 | `DeviceListTile` / `DeviceDetailPage` 标题统一使用 `device.model`,避免把 MAC 地址误当型号展示 |
| ✅ 已修复 | 型号过滤 | 设备列表「型号」筛选框改为基于 `Device.model` 过滤 |
| ✅ 已优化 | 降级处理 | 型号解析不到时,依次降级为 `name`(若 name 不是 MAC)→ `deviceSn` → `mac`;并新增 `_isMacAddress` 识别 MAC 格式,确保 MAC 不会混入型号展示 |
⏳ 待验收:设备标题是否显示真实型号(非 MAC)。
---
## v6-fix16(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 设备列表 | 顶部搜索区新增「型号」输入框,按 `Device.name` / `deviceSn` 实时过滤设备列表 |
| ✅ 已优化 | 设备列表 | Riverpod `AsyncValue.when` 启用 `skipLoadingOnRefresh`/`skipLoadingOnReload`,刷新/重新加载时保留已有数据,避免全屏转圈覆盖已加载项 |
| ✅ 已优化 | 设备列表 | 门店下拉已加载过门店后不再重新显示转圈;状态 Chip 颜色与 `DeviceStatus` 严格对应(在线绿/离线灰/告警橙) |
⏳ 待验收:型号筛选是否生效、刷新是否不再全屏转圈。
---
## v6-fix15(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备列表 | 标题前增加「型号:」前缀(如「型号:rk3566_r」),并接入 i10n |
| ✅ 已修复 | 扫码页 | 移除 AppBar「相册识别」按钮,进入页面即直接调用摄像头扫描;说明当前代码无「实时截屏/摄像头」选择弹窗,若仍出现请确认版本/入口 |
| ✅ 已实现 | 新增门店 | 表单新增「终端数」与「激活码」字段;保存门店后若终端数>0,自动调用 `/device/activationCode/generate` 创建激活码;`saveOrUpdateOrg` 改为返回 orgId 供后续操作 |
⏳ 待验收:设备标题前缀、扫码页直接摄像头、新增门店激活码/终端数。
---
## v6-fix14(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 设备列表 | web 端有房间号但手机端显示「未设置」→ 扩大 `Device.fromJson` 房号字段兼容:`roomNo`/`room`/`roomNumber`/`room_id`/`roomNum`/`roomName`/`houseNumber`/`roomCode` 等常见别名 |
✅ 已验收:设备列表房号。
---
## v6-fix13(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 门店详情 | 激活码仍显示「—」→ 改从激活码专用接口 `/device/activationCode/info/{orgId}` 拉取 `code` 字段,作为真实激活码展示;接口无值时回退门店列表里的 `activationCode` |
| ✅ 已修复 | 设备列表 | 副标题改为用户指定格式:**门店:XXX门店 MAC:AA:BB:CC:DD:EE:FF 房号:V888**(缺失项显示「未设置」) |
✅ 已验收:门店详情激活码、设备副标题格式。
---
## v6-fix12(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 用户管理 | 角色/组织显示「未分配」→ 兼容后端嵌套对象/数组返回(`roles`/`org`/`dept` 等),并建立完整 orgId→name 映射 |
| ✅ 已修复 | 设备列表 | 副标题改为 `MAC · 所属门店 · 房号`(原为设备型号/MAC) |
| ✅ 已修复 | 门店管理 | 手机端新增/编辑门店**真实上报后端**(`/sys/org/saveOrUpdate`),不再本地占位 |
| ✅ 已修复 | 门店列表 | 按创建时间倒序,最新门店排最前面(与 web 一致) |
⏳ 待验收:用户管理角色/组织回显、新增门店 web 可见、门店列表排序。
---
## v6-fix11(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 国际化 i18n | 设备详情、下发指令、修改分组/房号、激活码管理、用户管理、公司介绍等页面接入英文翻译 |
| ✅ 已修复 | 编译 | 修复 8 个 analyze error(i10n 重复定义、store_list 误用 `t.normal`/`t.disabled`、device_list 漏传参、user_page 漏 `t` 声明) |
---
## v6-fix10(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 门店编辑-界面配置 | 按设备分组展示已启用/添加的主题(接入 `/theme/page/org/{orgId}/photos`,ThemeGroup→ThemePage[]) |
| ✅ 已修复 | 设备列表 | `Device.fromJson` 兼容 `deviceName`/`ipAddress`/`deviceIp` 等字段,正确回显设备名、房号、MAC、IP |
---
## v6-fix9(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已实现 | 国际化 i18n | 底部导航、首页、设备列表、门店列表、管理入口、设置页接入英文翻译(首批) |
---
## v6-fix8(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 门店详情 | 联系电话/激活码/到期时间/创建时间字段空白 → 调 `/sys/org/listByLoginOrg` 回填 |
| ✅ 已修复 | 扫码 | 路由冲突(`scan` 在 `:id` 后导致被当 deviceId)→ 路由前置 |
| ✅ 已修复 | 管理页 | 移除「门店配置」入口(后端 `/device/storeConfig/{orgId}` 500) |
---
## v6-fix7(2026-08-24)
| 类型 | 模块 | 内容 |
|------|------|------|
| ✅ 已修复 | 登录-记住密码 | `CredentialStorage` 由 `Directory.current`(Android 不可写)迁移到 `shared_preferences`,记住账号密码 + 免登录恢复均生效 |
---
## 历史(本会话早段)
| 版本 | 类型 | 模块 | 内容 |
|------|------|------|------|
| v6-fix6 | ✅ 已修复 | 门店编辑 | 移除扫码绑定按钮/启动图Logo区块;主题皮肤改接 web 模板;所属组织下拉过滤+透传 parentId |
| v6-fix5 | ✅ 已修复 | 登录灰块 | `localizationsDelegates` 漏默认 delegate 致 TextField 崩溃 → 补全三个默认 delegate |
| v6-fix3 | ✅ 已实现 | 排障 | `ErrorWidget.builder` 红色错误页 + DIAG 横幅,替代 release 灰屏 |
---
## ⏳ 已知待办 / 未改项
- ❌ **门店配置** 功能:后端 `/device/storeConfig/{orgId}` 持续 500,前端已移除入口并给友好提示,需后端修复后恢复。
- ⏳ 门店编辑「界面配置」目前为**展示型**(显示分组/主题/激活主题),移动端切换激活主题需后端 `/theme/page/active` 支持,可后续迭代。
- ⏳ 切换英文后如仍有个别深层弹窗/页面为中文,多为未接 i18n 的入口,截图可继续补。
---
## 交付物索引
| 版本 | APK 路径 | 大小 |
|------|----------|------|
| v6-fix35 | `E:/Project/ops-console/ops-console-release-v6-fix35.apk` | 33.5MB |
| v6-fix34 | `E:/Project/ops-console/ops-console-release-v6-fix34.apk` | 33.5MB |
| v6-fix33 | `E:/Project/ops-console/ops-console-release-v6-fix33.apk` | 33.5MB |
| v6-fix32 | `E:/Project/ops-console/ops-console-release-v6-fix32.apk` | 33.5MB |
| v6-fix31 | `E:/Project/ops-console/ops-console-release-v6-fix31.apk` | 33.5MB |
| v6-fix30 | `E:/Project/ops-console/ops-console-release-v6-fix30.apk` | 33.5MB |
| v6-fix12 | `E:/Project/ops-console/ops-console-release-v6-fix12.apk` | 33.4MB |
| v6-fix11 | `E:/Project/ops-console/ops-console-release-v6-fix11.apk` | 35.0MB |
| v6-fix7~10 | `E:/Project/ops-console/build/app/outputs/flutter-apk/app-release.apk` | 33.x MB |