feat(自主填报): 销量填写拆为独立页并按原型改版

甲方反馈原销量填写功能难看、不好用,按原型图改版。

改造前,销量填写是自主填报页内的一个区块(新增商品 → 底部弹层选品 →
回列表手输数量),与原型差异大。现拆为独立页面。

新页 packageUser/self-report/sales:
- 视觉对齐原型:搜索框 + 筛选、新增商品 + 批量填写、卡片式列表
  (序号徽标/封面/分类/厂家/编码/规格胶囊/数量步进器/删除)、底部提交
- 新增搜索(按名称或编码过滤清单)
- 新增分类筛选(弹层选择,可清空)
- 新增批量填写(弹层一次录入多项数量)

自主填报页(create.vue):
- 销量区块改为入口卡片 + 摘要列表,明细编辑移入新页
- 提交按钮文案「提交销量」改为「提交填报」——该页提交的是整单
  (打卡/门头照/销量/小票/小结/评分/定位),原标题名不副实
- 删除已迁移到新页的商品库选择逻辑(约 270 行)及废弃样式

页面间交接(util/sales-handoff.js):
- 工程无全局状态管理,页面间只能靠 storage 传递
- 仅负责一次性交接,消费后即清;持久化草稿统一由自主填报页的
  整单草稿负责(其 buildDraftPayload 已含 salesItems),避免两份状态互相覆盖
- 换连锁/换门店后清空销量明细与交接数据

修复:
- 数量步进器改用纯文本按钮。原用 u-icon 时点击无效——该组件根节点自带
  @tap,会吃掉落在图标上的点击;同页「删除」按钮因带文字才正常
- 数量输入限制为非负整数。type=number 在部分机型仍可输入 . / e / -,
  故增加输入清洗与失焦兜底;采用「取开头连续数字」而非「删掉所有非数字」,
  后者会把 1.5 抹成 15、1e3 抹成 13,属静默的数据污染

提交结构 salesGroups 未变,后端与审核端无需调整。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-10-11 18:47:03 +08:00
parent 9eaa168d72
commit 876847ccbb
4 changed files with 1511 additions and 651 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+7
View File
@@ -170,6 +170,13 @@
"navigationStyle": "custom"
}
},
{
"path": "self-report/sales",
"style": {
"navigationBarTitleText": "销量填写",
"navigationStyle": "custom"
}
},
{
"path": "self-report/records",
"style": {
+44
View File
@@ -0,0 +1,44 @@
import {getUserInfo} from "@/util/user";
// 「自主填报」(create.vue) 与「销量填写」(sales.vue) 两个页面之间的数据交接。
// 本工程没有全局状态管理,页面间只能靠 storage 传递。
//
// 这里只负责一次性的交接,不是草稿:
// - 进销量页前,create 页写入连锁名与当前明细作为上下文;
// - 销量页返回时写回明细(action='submit' 表示用户点了销量页自己的
// 「提交销量」,'merge' 表示按返回键离开时同步明细);
// - create 页消费后即清。两者都不代表整单已提交,整单提交是
// create 页「提交填报」按钮的职责。
//
// 持久化草稿统一由 create 页的整单草稿负责(含 salesItems/打卡/小票/小结),
// 销量页不再单独存草稿,避免两份状态互相覆盖。
const normalizeUid = () => {
const user = getUserInfo() || {}
const uid = user.uid || user.id || user.userId || 'guest'
return String(uid)
}
const readObject = key => {
const value = uni.getStorageSync(key)
return value && typeof value === 'object' ? value : null
}
export const cloneSalesItems = (items = []) => {
try {
return JSON.parse(JSON.stringify(Array.isArray(items) ? items : []))
} catch (error) {
return []
}
}
export const getSalesHandoffKey = () => `yxe_self_report_sales_handoff_${normalizeUid()}`
export const setSalesHandoff = (payload = {}) => {
uni.setStorageSync(getSalesHandoffKey(), Object.assign({savedAt: Date.now()}, payload))
}
export const getSalesHandoff = () => readObject(getSalesHandoffKey())
export const clearSalesHandoff = () => {
uni.removeStorageSync(getSalesHandoffKey())
}