skill-bill 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/SKILL.md +127 -0
  2. package/dist/.gen-inputs.json +14 -0
  3. package/dist/cli/cmd_read.js +133 -142
  4. package/dist/cli/cmd_read.js.map +1 -1
  5. package/dist/cli/registry.d.ts +15 -0
  6. package/dist/cli/registry.d.ts.map +1 -0
  7. package/dist/cli/registry.js +22 -0
  8. package/dist/cli/registry.js.map +1 -0
  9. package/dist/fetch/db.d.ts +4 -0
  10. package/dist/fetch/db.d.ts.map +1 -1
  11. package/dist/fetch/db.js +12 -0
  12. package/dist/fetch/db.js.map +1 -1
  13. package/dist/fetch/index.d.ts +1 -1
  14. package/dist/fetch/index.d.ts.map +1 -1
  15. package/dist/fetch/index.js +1 -1
  16. package/dist/fetch/index.js.map +1 -1
  17. package/dist/help/index.d.ts +1 -0
  18. package/dist/help/index.d.ts.map +1 -1
  19. package/dist/help/index.js +1 -0
  20. package/dist/help/index.js.map +1 -1
  21. package/dist/help/lookup.d.ts.map +1 -1
  22. package/dist/help/lookup.js +3 -2
  23. package/dist/help/lookup.js.map +1 -1
  24. package/dist/help/writeWire.d.ts +16 -0
  25. package/dist/help/writeWire.d.ts.map +1 -0
  26. package/dist/help/writeWire.js +109 -0
  27. package/dist/help/writeWire.js.map +1 -0
  28. package/dist/output.d.ts +21 -0
  29. package/dist/output.d.ts.map +1 -0
  30. package/dist/output.js +48 -0
  31. package/dist/output.js.map +1 -0
  32. package/dist/policy/category.d.ts +5 -0
  33. package/dist/policy/category.d.ts.map +1 -1
  34. package/dist/policy/category.js +9 -2
  35. package/dist/policy/category.js.map +1 -1
  36. package/dist/policy/index.d.ts +1 -1
  37. package/dist/policy/index.d.ts.map +1 -1
  38. package/dist/policy/index.js +1 -1
  39. package/dist/policy/index.js.map +1 -1
  40. package/dist/policy/record.d.ts +12 -0
  41. package/dist/policy/record.d.ts.map +1 -1
  42. package/dist/policy/record.js +27 -0
  43. package/dist/policy/record.js.map +1 -1
  44. package/dist/policy/wakewords.d.ts +4 -0
  45. package/dist/policy/wakewords.d.ts.map +1 -1
  46. package/dist/policy/wakewords.js +12 -7
  47. package/dist/policy/wakewords.js.map +1 -1
  48. package/dist/query/commands.d.ts +19 -0
  49. package/dist/query/commands.d.ts.map +1 -0
  50. package/dist/query/commands.js +40 -0
  51. package/dist/query/commands.js.map +1 -0
  52. package/dist/query/detail.d.ts +26 -0
  53. package/dist/query/detail.d.ts.map +1 -0
  54. package/dist/query/detail.js +115 -0
  55. package/dist/query/detail.js.map +1 -0
  56. package/dist/query/index.d.ts +13 -0
  57. package/dist/query/index.d.ts.map +1 -0
  58. package/dist/query/index.js +12 -0
  59. package/dist/query/index.js.map +1 -0
  60. package/dist/query/list.d.ts +78 -0
  61. package/dist/query/list.d.ts.map +1 -0
  62. package/dist/query/list.js +110 -0
  63. package/dist/query/list.js.map +1 -0
  64. package/dist/query/read.d.ts +21 -0
  65. package/dist/query/read.d.ts.map +1 -0
  66. package/dist/query/read.js +301 -0
  67. package/dist/query/read.js.map +1 -0
  68. package/dist/record/collect.d.ts +18 -0
  69. package/dist/record/collect.d.ts.map +1 -0
  70. package/dist/record/collect.js +15 -0
  71. package/dist/record/collect.js.map +1 -0
  72. package/dist/record/collectBody.d.ts +29 -0
  73. package/dist/record/collectBody.d.ts.map +1 -0
  74. package/dist/record/collectBody.js +196 -0
  75. package/dist/record/collectBody.js.map +1 -0
  76. package/dist/record/commands.d.ts +11 -0
  77. package/dist/record/commands.d.ts.map +1 -0
  78. package/dist/record/commands.js +22 -0
  79. package/dist/record/commands.js.map +1 -0
  80. package/dist/record/index.d.ts +14 -0
  81. package/dist/record/index.d.ts.map +1 -0
  82. package/dist/record/index.js +12 -0
  83. package/dist/record/index.js.map +1 -0
  84. package/dist/record/receipt.d.ts +16 -0
  85. package/dist/record/receipt.d.ts.map +1 -0
  86. package/dist/record/receipt.js +11 -0
  87. package/dist/record/receipt.js.map +1 -0
  88. package/dist/record/receiptBody.d.ts +5 -0
  89. package/dist/record/receiptBody.d.ts.map +1 -0
  90. package/dist/record/receiptBody.js +112 -0
  91. package/dist/record/receiptBody.js.map +1 -0
  92. package/dist/record/scene-batch.d.ts +3 -0
  93. package/dist/record/scene-batch.d.ts.map +1 -0
  94. package/dist/record/scene-batch.js +211 -0
  95. package/dist/record/scene-batch.js.map +1 -0
  96. package/dist/record/scene-borrow.d.ts +3 -0
  97. package/dist/record/scene-borrow.d.ts.map +1 -0
  98. package/dist/record/scene-borrow.js +285 -0
  99. package/dist/record/scene-borrow.js.map +1 -0
  100. package/dist/record/scene-collect.d.ts +3 -0
  101. package/dist/record/scene-collect.d.ts.map +1 -0
  102. package/dist/record/scene-collect.js +294 -0
  103. package/dist/record/scene-collect.js.map +1 -0
  104. package/dist/record/scene-expense.d.ts +3 -0
  105. package/dist/record/scene-expense.d.ts.map +1 -0
  106. package/dist/record/scene-expense.js +21 -0
  107. package/dist/record/scene-expense.js.map +1 -0
  108. package/dist/record/scene-income.d.ts +3 -0
  109. package/dist/record/scene-income.d.ts.map +1 -0
  110. package/dist/record/scene-income.js +219 -0
  111. package/dist/record/scene-income.js.map +1 -0
  112. package/dist/record/scene-installment.d.ts +3 -0
  113. package/dist/record/scene-installment.d.ts.map +1 -0
  114. package/dist/record/scene-installment.js +248 -0
  115. package/dist/record/scene-installment.js.map +1 -0
  116. package/dist/record/scene-lend.d.ts +3 -0
  117. package/dist/record/scene-lend.d.ts.map +1 -0
  118. package/dist/record/scene-lend.js +273 -0
  119. package/dist/record/scene-lend.js.map +1 -0
  120. package/dist/record/scene-photo.d.ts +3 -0
  121. package/dist/record/scene-photo.d.ts.map +1 -0
  122. package/dist/record/scene-photo.js +262 -0
  123. package/dist/record/scene-photo.js.map +1 -0
  124. package/dist/record/scene-plain.d.ts +3 -0
  125. package/dist/record/scene-plain.d.ts.map +1 -0
  126. package/dist/record/scene-plain.js +221 -0
  127. package/dist/record/scene-plain.js.map +1 -0
  128. package/dist/record/scene-refund.d.ts +3 -0
  129. package/dist/record/scene-refund.d.ts.map +1 -0
  130. package/dist/record/scene-refund.js +281 -0
  131. package/dist/record/scene-refund.js.map +1 -0
  132. package/dist/record/scene-reimburse-done.d.ts +3 -0
  133. package/dist/record/scene-reimburse-done.d.ts.map +1 -0
  134. package/dist/record/scene-reimburse-done.js +288 -0
  135. package/dist/record/scene-reimburse-done.js.map +1 -0
  136. package/dist/record/scene-reimburse.d.ts +3 -0
  137. package/dist/record/scene-reimburse.d.ts.map +1 -0
  138. package/dist/record/scene-reimburse.js +224 -0
  139. package/dist/record/scene-reimburse.js.map +1 -0
  140. package/dist/record/scene-repay.d.ts +3 -0
  141. package/dist/record/scene-repay.d.ts.map +1 -0
  142. package/dist/record/scene-repay.js +294 -0
  143. package/dist/record/scene-repay.js.map +1 -0
  144. package/dist/record/scene-restore.d.ts +3 -0
  145. package/dist/record/scene-restore.d.ts.map +1 -0
  146. package/dist/record/scene-restore.js +250 -0
  147. package/dist/record/scene-restore.js.map +1 -0
  148. package/dist/record/scene-undo.d.ts +3 -0
  149. package/dist/record/scene-undo.d.ts.map +1 -0
  150. package/dist/record/scene-undo.js +262 -0
  151. package/dist/record/scene-undo.js.map +1 -0
  152. package/dist/record/scene-update.d.ts +3 -0
  153. package/dist/record/scene-update.d.ts.map +1 -0
  154. package/dist/record/scene-update.js +249 -0
  155. package/dist/record/scene-update.js.map +1 -0
  156. package/dist/record/scene.d.ts +79 -0
  157. package/dist/record/scene.d.ts.map +1 -0
  158. package/dist/record/scene.js +39 -0
  159. package/dist/record/scene.js.map +1 -0
  160. package/dist/record/slots.d.ts +16 -0
  161. package/dist/record/slots.d.ts.map +1 -0
  162. package/dist/record/slots.js +72 -0
  163. package/dist/record/slots.js.map +1 -0
  164. package/dist/record/write.d.ts +9 -0
  165. package/dist/record/write.d.ts.map +1 -0
  166. package/dist/record/write.js +186 -0
  167. package/dist/record/write.js.map +1 -0
  168. package/dist/render/envelope.d.ts.map +1 -1
  169. package/dist/render/envelope.js +21 -8
  170. package/dist/render/envelope.js.map +1 -1
  171. package/dist/render/errors.d.ts +1 -1
  172. package/dist/render/errors.d.ts.map +1 -1
  173. package/dist/render/errors.js.map +1 -1
  174. package/dist/render/helpFile.d.ts +85 -0
  175. package/dist/render/helpFile.d.ts.map +1 -0
  176. package/dist/render/helpFile.js +110 -0
  177. package/dist/render/helpFile.js.map +1 -0
  178. package/dist/render/helpPaths.d.ts +19 -0
  179. package/dist/render/helpPaths.d.ts.map +1 -0
  180. package/dist/render/helpPaths.js +19 -0
  181. package/dist/render/helpPaths.js.map +1 -0
  182. package/dist/render/html.js +1 -1
  183. package/dist/render/html.js.map +1 -1
  184. package/dist/render/index.d.ts +5 -1
  185. package/dist/render/index.d.ts.map +1 -1
  186. package/dist/render/index.js +4 -1
  187. package/dist/render/index.js.map +1 -1
  188. package/dist/render/templates.js +1 -1
  189. package/dist/render/templates.js.map +1 -1
  190. package/dist/render/views.d.ts +0 -22
  191. package/dist/render/views.d.ts.map +1 -1
  192. package/dist/render/views.js +1 -16
  193. package/dist/render/views.js.map +1 -1
  194. package/dist/shared/blockedSlots.d.ts +39 -0
  195. package/dist/shared/blockedSlots.d.ts.map +1 -0
  196. package/dist/shared/blockedSlots.js +105 -0
  197. package/dist/shared/blockedSlots.js.map +1 -0
  198. package/dist/shared/candidatePick.d.ts +50 -0
  199. package/dist/shared/candidatePick.d.ts.map +1 -0
  200. package/dist/shared/candidatePick.js +115 -0
  201. package/dist/shared/candidatePick.js.map +1 -0
  202. package/dist/shared/collectFrame.d.ts +15 -0
  203. package/dist/shared/collectFrame.d.ts.map +1 -0
  204. package/dist/shared/collectFrame.js +34 -0
  205. package/dist/shared/collectFrame.js.map +1 -0
  206. package/dist/shared/commandSpec.d.ts +72 -0
  207. package/dist/shared/commandSpec.d.ts.map +1 -0
  208. package/dist/shared/commandSpec.js +2 -0
  209. package/dist/shared/commandSpec.js.map +1 -0
  210. package/dist/shared/copyArea.d.ts +51 -0
  211. package/dist/shared/copyArea.d.ts.map +1 -0
  212. package/dist/shared/copyArea.js +122 -0
  213. package/dist/shared/copyArea.js.map +1 -0
  214. package/dist/shared/diffTable.d.ts +26 -0
  215. package/dist/shared/diffTable.d.ts.map +1 -0
  216. package/dist/shared/diffTable.js +77 -0
  217. package/dist/shared/diffTable.js.map +1 -0
  218. package/dist/shared/docPage.d.ts +17 -0
  219. package/dist/shared/docPage.d.ts.map +1 -0
  220. package/dist/shared/docPage.js +111 -0
  221. package/dist/shared/docPage.js.map +1 -0
  222. package/dist/shared/duplicateNote.d.ts +40 -0
  223. package/dist/shared/duplicateNote.d.ts.map +1 -0
  224. package/dist/shared/duplicateNote.js +96 -0
  225. package/dist/shared/duplicateNote.js.map +1 -0
  226. package/dist/shared/emptyNote.d.ts +15 -0
  227. package/dist/shared/emptyNote.d.ts.map +1 -0
  228. package/dist/shared/emptyNote.js +27 -0
  229. package/dist/shared/emptyNote.js.map +1 -0
  230. package/dist/shared/errorReceipt.d.ts +21 -0
  231. package/dist/shared/errorReceipt.d.ts.map +1 -0
  232. package/dist/shared/errorReceipt.js +37 -0
  233. package/dist/shared/errorReceipt.js.map +1 -0
  234. package/dist/shared/flowSteps.d.ts +25 -0
  235. package/dist/shared/flowSteps.d.ts.map +1 -0
  236. package/dist/shared/flowSteps.js +49 -0
  237. package/dist/shared/flowSteps.js.map +1 -0
  238. package/dist/shared/installmentPreview.d.ts +23 -0
  239. package/dist/shared/installmentPreview.d.ts.map +1 -0
  240. package/dist/shared/installmentPreview.js +144 -0
  241. package/dist/shared/installmentPreview.js.map +1 -0
  242. package/dist/shared/outsideScan.d.ts +34 -0
  243. package/dist/shared/outsideScan.d.ts.map +1 -0
  244. package/dist/shared/outsideScan.js +116 -0
  245. package/dist/shared/outsideScan.js.map +1 -0
  246. package/dist/shared/pageIdentity.d.ts +30 -0
  247. package/dist/shared/pageIdentity.d.ts.map +1 -0
  248. package/dist/shared/pageIdentity.js +33 -0
  249. package/dist/shared/pageIdentity.js.map +1 -0
  250. package/dist/shared/pageShell.d.ts +24 -0
  251. package/dist/shared/pageShell.d.ts.map +1 -0
  252. package/dist/shared/pageShell.js +42 -0
  253. package/dist/shared/pageShell.js.map +1 -0
  254. package/dist/shared/photoEscape.d.ts +53 -0
  255. package/dist/shared/photoEscape.d.ts.map +1 -0
  256. package/dist/shared/photoEscape.js +139 -0
  257. package/dist/shared/photoEscape.js.map +1 -0
  258. package/dist/shared/prefillNote.d.ts +21 -0
  259. package/dist/shared/prefillNote.d.ts.map +1 -0
  260. package/dist/shared/prefillNote.js +78 -0
  261. package/dist/shared/prefillNote.js.map +1 -0
  262. package/dist/shared/receiptParts.d.ts +12 -0
  263. package/dist/shared/receiptParts.d.ts.map +1 -0
  264. package/dist/shared/receiptParts.js +38 -0
  265. package/dist/shared/receiptParts.js.map +1 -0
  266. package/dist/shared/recentPicks.d.ts +26 -0
  267. package/dist/shared/recentPicks.d.ts.map +1 -0
  268. package/dist/shared/recentPicks.js +41 -0
  269. package/dist/shared/recentPicks.js.map +1 -0
  270. package/dist/shared/recordPicker.d.ts +32 -0
  271. package/dist/shared/recordPicker.d.ts.map +1 -0
  272. package/dist/shared/recordPicker.js +201 -0
  273. package/dist/shared/recordPicker.js.map +1 -0
  274. package/dist/shared/rowEditorTable.d.ts +31 -0
  275. package/dist/shared/rowEditorTable.d.ts.map +1 -0
  276. package/dist/shared/rowEditorTable.js +103 -0
  277. package/dist/shared/rowEditorTable.js.map +1 -0
  278. package/dist/shared/summaryRow.d.ts +44 -0
  279. package/dist/shared/summaryRow.d.ts.map +1 -0
  280. package/dist/shared/summaryRow.js +82 -0
  281. package/dist/shared/summaryRow.js.map +1 -0
  282. package/dist/shared/typeBadge.d.ts +21 -0
  283. package/dist/shared/typeBadge.d.ts.map +1 -0
  284. package/dist/shared/typeBadge.js +68 -0
  285. package/dist/shared/typeBadge.js.map +1 -0
  286. package/dist/shared/userWording.d.ts +54 -0
  287. package/dist/shared/userWording.d.ts.map +1 -0
  288. package/dist/shared/userWording.js +131 -0
  289. package/dist/shared/userWording.js.map +1 -0
  290. package/dist/shared/writeParts.d.ts +62 -0
  291. package/dist/shared/writeParts.d.ts.map +1 -0
  292. package/dist/shared/writeParts.js +39 -0
  293. package/dist/shared/writeParts.js.map +1 -0
  294. package/dist/triggers/wake-assets.d.ts +37 -0
  295. package/dist/triggers/wake-assets.d.ts.map +1 -0
  296. package/dist/triggers/wake-assets.js +951 -0
  297. package/dist/triggers/wake-assets.js.map +1 -0
  298. package/package.json +10 -4
@@ -0,0 +1,201 @@
1
+ /** 候选记录列表(**唯一定义地**):改记录/撤销/恢复三条唤醒词共用的「挑哪一条记录」那一格,连它的取数。
2
+ *
3
+ * 谁在用(本票实数,三个调用点,指名):
4
+ * ① `src/record/scene-update.ts`——缺 `id` 时列候选(未撤销的那些),挑完再说改哪个字段;
5
+ * ② `src/record/scene-undo.ts`——缺 `id` 时列候选(未撤销的那些;撤销=软删打标,打完还能恢复);
6
+ * ③ `src/record/scene-restore.ts`——缺 `id` 时列候选(**只列已打标撤销的那些**)。
7
+ * 三条词挑的是同一件事(「哪一条记录」),不同的只是过筛条件:换 `mode`,不换件。
8
+ *
9
+ * 本件对外的名字**五个**(照 `docs/agents/structure.md` 的「一个文件对外给的东西不多于五个」):
10
+ * `pickModeOf`(`params.op` 落哪种过筛条件)、`pickerBlock`(那一格整块)、`readRowById`(按编号读一条,
11
+ * 连「是否已打标撤销」一次给全)、`snapshotTable`(一条记录的只读回显)、`diffRowsFor`(本次打算改的字段对照)。
12
+ * 其余五样收在件内、不外给:`PickMode`/`CandidateRead`(两个内部别名)、`isDeleted`(软删口径的唯一定义地)、
13
+ * `readCandidates`(`pickerBlock` 的取数那一半)、`snapshotRows`(`snapshotTable` 的行)。
14
+ * 计数口径:只数真被本目录之外的件引用的名字,零跨目录引用的导出不算。
15
+ *
16
+ * 取数用**写入侧已有的读能力**(`src/fetch/` 的 `openBillDb`/`fetchAll`/`closeBillDb`):
17
+ * 不新增查询命令、不等查询图 #403——列候选是写入流程内部的一步(挑完仍走同一条写命令)。
18
+ * 「撤销候选=未删记录/恢复候选=已软删记录」这条口径住本件,页面不再各判一次:
19
+ * `fetchAll` 传 `includeDeleted: true` 才看得到软删行(`fetchAll` 的缺省口径把软删排除在外)。
20
+ *
21
+ * 一件不自造:单选与记录表走 `./candidatePick.js`(它自己包 base 的 `renderDataTable` 与
22
+ * `renderParamForm` 的单选项),空态走 `./emptyNote.js`,金额文本走 `./summaryRow.js` 的 `money2`,
23
+ * 原值与新值的比对走 `./diffTable.js` 的 `diffOf`——三条词与改记录的回执都用同一份判定。
24
+ *
25
+ * 读不通不许静默:库路径没设、库打不开这些情形由本件报出来(`ok:false` + `reason`),
26
+ * 页面据此出空态并把原因写在话里,不拿一张空表冒充「库里没有记录」。
27
+ */
28
+ import { renderCaliberLine, renderDataTable } from 'base-paint/blocks';
29
+ import { closeBillDb, fetchAll, openBillDb } from '../fetch/index.js';
30
+ import { resolveDbPath } from '../fetch/paths.js';
31
+ import { candidatePick } from './candidatePick.js';
32
+ import { diffOf } from './diffTable.js';
33
+ import { emptyNote } from './emptyNote.js';
34
+ import { money2 } from './summaryRow.js';
35
+ import { fieldLabelOf } from './userWording.js';
36
+ /** 候选列表一页最多列几条(按时间倒序取最近的这些条)。 */
37
+ const PICK_LIMIT = 20;
38
+ const LABELS = {
39
+ update: {
40
+ label: '要改的那条记录',
41
+ what: '没撤销过的记录',
42
+ hint: '从下面列出的记录里挑一条,再说清要改哪一项、改成什么,跟助手说一遍。',
43
+ },
44
+ undo: {
45
+ label: '要撤销的那条记录',
46
+ what: '没撤销过的记录',
47
+ hint: '从下面列出的记录里挑一条:撤销只是打个标记,记录还在,随时可以恢复。',
48
+ },
49
+ restore: {
50
+ label: '要恢复的那条记录',
51
+ what: '已经撤销过的记录',
52
+ hint: '只列已经撤销过的那些:恢复=把撤销标记清掉,这一笔回到查询与统计里。',
53
+ },
54
+ };
55
+ /** `params.op` 落哪一种过筛条件(缺省=改字段那一条)。 */
56
+ export function pickModeOf(op) {
57
+ return op === 'undo' ? 'undo' : op === 'restore' ? 'restore' : 'update';
58
+ }
59
+ /** 一条记录是不是打过软删标记。**软删口径的唯一定义地**:取数(`keeps`)与读一条(`readRowById`)都走它。 */
60
+ function isDeleted(row) {
61
+ return row.deleted_at !== null && String(row.deleted_at).trim() !== '';
62
+ }
63
+ /** 这一条算不算这种 `mode` 的候选:恢复只认已打标的,其余两种只认没打标的。 */
64
+ function keeps(mode, row) {
65
+ return mode === 'restore' ? isDeleted(row) : !isDeleted(row);
66
+ }
67
+ /** 时间倒序(同一时刻按编号倒序):最近的一条摆在最前面。 */
68
+ function byTimeDesc(a, b) {
69
+ if (a.time === b.time)
70
+ return b.id - a.id;
71
+ return a.time < b.time ? 1 : -1;
72
+ }
73
+ /** 一条候选为什么是它(**必填**:写不出依据的候选,用户没法判断该不该选)。
74
+ * 本轮整改把工程话换成用户说法(原文见 `docs/skills/skill-bill/t407-文字审查.md` 第 25、30 条):
75
+ * `未撤销(deleted_at 为空)· 就躺在库里等你处置` → 「还没撤销过 可以改」;
76
+ * `已于 X 打标撤销(软删:行还在库里,置 NULL 即可恢复)` → 「已于 X 撤销过(记录还在,点恢复就回来)」。 */
77
+ function whyOf(mode, row) {
78
+ if (mode === 'restore') {
79
+ return '已于 ' + String(row.deleted_at) + ' 撤销过(记录还在,点恢复就回来)';
80
+ }
81
+ return mode === 'update' ? '还没撤销过 可以改' : '还没撤销过 可以撤';
82
+ }
83
+ /** 候选行那一列的摘要:分类 + 备注。
84
+ * **不带账户**(本轮整改):账户就在同页那张只读回显表的「账户」那一项里逐项列着,
85
+ * 候选表再抄一遍,同一句话在页上就印了两遍(机审判成重复句)。 */
86
+ function pickLabelOf(row) {
87
+ return row.category + (row.note.trim() === '' ? '' : ' ' + row.note.trim());
88
+ }
89
+ /** 单选选项那一格用的全量摘要:分类 + 账户 + 备注(分隔符用全角空格,行内不再拿 `·` 当版式)。 */
90
+ function labelOf(row) {
91
+ const note = row.note.trim() === '' ? '' : ' ' + row.note.trim();
92
+ return row.category + (row.account.trim() === '' ? '' : ' ' + row.account) + note;
93
+ }
94
+ /** 读候选:开库读一遍、按 `mode` 过筛、按时间倒序取最近 `PICK_LIMIT` 条,读完关库。读不通照实报。 */
95
+ function readCandidates(mode) {
96
+ let handle = null;
97
+ try {
98
+ handle = openBillDb(resolveDbPath());
99
+ const kept = fetchAll(handle, { includeDeleted: true }).filter((r) => keeps(mode, r));
100
+ const sorted = [...kept].sort(byTimeDesc);
101
+ const items = sorted.slice(0, PICK_LIMIT).map((r) => ({
102
+ id: r.id,
103
+ label: pickLabelOf(r),
104
+ amount: money2(r.amount),
105
+ time: r.time,
106
+ why: whyOf(mode, r),
107
+ }));
108
+ return { ok: true, reason: '', items, total: sorted.length };
109
+ }
110
+ catch (e) {
111
+ return { ok: false, reason: e instanceof Error ? e.message : String(e), items: [], total: 0 };
112
+ }
113
+ finally {
114
+ if (handle !== null)
115
+ closeBillDb(handle);
116
+ }
117
+ }
118
+ /** 候选格整块:单选 + 候选记录表 +「为什么是它」+ 一行口径;候选为空或读不通都出空态(不拿最近一笔兜底)。 */
119
+ export function pickerBlock(input) {
120
+ const meta = LABELS[input.mode];
121
+ const read = readCandidates(input.mode);
122
+ if (!read.ok) {
123
+ return emptyNote({
124
+ title: '候选读不出来',
125
+ text: '这一格要的候选没能从库里读出来:' + read.reason + '。',
126
+ next: '请先在能读库的环境里跑(库路径指向那个库),再说一遍。',
127
+ });
128
+ }
129
+ if (read.items.length === 0) {
130
+ return emptyNote({
131
+ title: '没有可选的' + meta.label,
132
+ text: '这一格要的是' + meta.what + ',库里一条都没有。',
133
+ next: '不拿最近一笔顶替。请先说清是哪一笔(或先记一笔),再说一遍。',
134
+ });
135
+ }
136
+ return candidatePick({
137
+ name: 'id',
138
+ label: meta.label,
139
+ candidates: read.items,
140
+ selectedId: input.selectedId ?? null,
141
+ hint: input.hint ?? meta.hint,
142
+ }) + renderCaliberLine('库里共 ' + read.total + ' 条' + (input.mode === 'restore' ? '已打标撤销' : '未撤销')
143
+ + '的记录,本页列最近 ' + read.items.length + ' 条(按时间倒序)。');
144
+ }
145
+ /** 按编号读一条记录(**含已打标撤销的**,`getById` 读不到软删行,故本件走 `fetchAll` 自己筛)。
146
+ * `deleted` 与 `row` 一次给全:软删口径只有 `isDeleted` 一处定义,调用方不再各判一次。 */
147
+ export function readRowById(id) {
148
+ let handle = null;
149
+ try {
150
+ handle = openBillDb(resolveDbPath());
151
+ const hit = fetchAll(handle, { includeDeleted: true }).find((r) => r.id === id) ?? null;
152
+ return { ok: true, reason: '', row: hit, deleted: hit !== null && isDeleted(hit) };
153
+ }
154
+ catch (e) {
155
+ return { ok: false, reason: e instanceof Error ? e.message : String(e), row: null, deleted: false };
156
+ }
157
+ finally {
158
+ if (handle !== null)
159
+ closeBillDb(handle);
160
+ }
161
+ }
162
+ /** 一条记录的只读回显(改记录的原记录那一格、撤销/恢复选定后那一格都用它)。
163
+ * 行名走 `./userWording.js` 的 `fieldLabelOf`(库列名不上屏);撤销标记那一格照实说有没有打标。 */
164
+ function snapshotRows(row) {
165
+ return [
166
+ { k: fieldLabelOf('id'), v: String(row.id) },
167
+ { k: fieldLabelOf('category'), v: row.category },
168
+ { k: fieldLabelOf('amount'), v: row.amount.toFixed(2) },
169
+ { k: fieldLabelOf('time'), v: row.time },
170
+ { k: fieldLabelOf('account'), v: row.account },
171
+ { k: fieldLabelOf('ledger'), v: row.ledger },
172
+ { k: fieldLabelOf('currency'), v: row.currency },
173
+ { k: fieldLabelOf('note'), v: row.note },
174
+ {
175
+ k: fieldLabelOf('deleted_at'),
176
+ v: isDeleted(row) ? '已撤销(' + String(row.deleted_at) + ')' : '没撤销过',
177
+ },
178
+ ];
179
+ }
180
+ /** 一条记录的只读表(列名与 `renderDataTable` 的两列同形)。
181
+ * 表头说明里那个 `·` 改全角空格(本轮整改:表头说明是版式位,行内不再拿 `·` 当版式)。 */
182
+ export function snapshotTable(row, caption) {
183
+ return renderDataTable({
184
+ columns: [{ key: 'k', label: '哪一项' }, { key: 'v', label: '记成什么' }],
185
+ rows: snapshotRows(row),
186
+ caption: caption ?? '这一条记录(记录编号 ' + row.id + ' 只读回显)',
187
+ });
188
+ }
189
+ /** 库内那一行是不是给了这一格(`undefined`/`null`/空白串都不算给,0 算给)。 */
190
+ function given(v) {
191
+ if (v === undefined || v === null)
192
+ return false;
193
+ return !(typeof v === 'string' && v.trim() === '');
194
+ }
195
+ /** 本次打算改的那几个字段的对照:**只比给了的那些字段**。
196
+ * 没给的字段不许进这张表——拿「未设置」当新值比,会把「这一格没动」算成「把这一格清空了」。 */
197
+ export function diffRowsFor(input) {
198
+ const fields = input.fields.filter((f) => given(input.params[f]));
199
+ return diffOf({ fields, before: { ...input.row }, after: input.params });
200
+ }
201
+ //# sourceMappingURL=recordPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recordPicker.js","sourceRoot":"","sources":["../../src/shared/recordPicker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** 一列:参数名/中文名/提示/这一列是不是必需。 */
2
+ export interface RowEditorField {
3
+ readonly name: string;
4
+ readonly label: string;
5
+ readonly hint?: string;
6
+ /** 必需的那一列空着,这一行就算缺项(红标与「不给复制」都看它)。 */
7
+ readonly required?: boolean;
8
+ }
9
+ /** 逐行可编辑表的入参。 */
10
+ export interface RowEditorInput {
11
+ /** 这张表的标识(每行控件的 `name` 是它 + 行号 + 列名,免得两行同名打架)。 */
12
+ readonly name: string;
13
+ readonly fields: readonly RowEditorField[];
14
+ /** 行:一张表一行,每一格按列名取值;缺的格写成空串,或干脆不给这一列。 */
15
+ readonly rows: readonly Readonly<Record<string, string>>[];
16
+ /** 哪一列求和(给了才出合计行;解析不出数的格不算进合计)。 */
17
+ readonly totalOf?: string;
18
+ /** 合计那一行前面那句(缺省「合计」)。 */
19
+ readonly totalLabel?: string;
20
+ }
21
+ /** 逐行缺项:第几行缺哪几列。**判定只此一处**(红标与「给不给复制」都引它)。零行返回空数组。 */
22
+ export declare function rowEditorMissing(input: {
23
+ readonly fields: readonly RowEditorField[];
24
+ readonly rows: readonly Readonly<Record<string, string>>[];
25
+ }): readonly {
26
+ readonly row: number;
27
+ readonly fields: readonly string[];
28
+ }[];
29
+ /** 逐行可编辑表整块:逐行一张小表单 + 缺项逐行标红 + 合计行;零行只出空态。 */
30
+ export declare function rowEditorTable(input: RowEditorInput): string;
31
+ //# sourceMappingURL=rowEditorTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rowEditorTable.d.ts","sourceRoot":"","sources":["../../src/shared/rowEditorTable.ts"],"names":[],"mappings":"AA2BA,8BAA8B;AAC9B,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,iBAAiB;AACjB,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;IAC3D,mCAAmC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAOD,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;CAC5D,GAAG,SAAS;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,CAS1E;AA+CD,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAsB5D"}
@@ -0,0 +1,103 @@
1
+ /** 逐行可编辑表(缺口块之一,**唯一定义地**):多行可编辑,缺项逐行标红。
2
+ *
3
+ * 谁在用(本票:**不接任何页面**,理由见下;件头按将来实数写,写得出第二个用法):
4
+ * ① 批量与修正族(批量录入)——一屏多行,每行是一笔的金额与分类,缺的那格当场标红;
5
+ * ② 特殊收支族(记分期 8 期以上的分摊预览、以及流程三段式第二段「填金额与对象」的多笔一次填)——
6
+ * 同一件,换的是行数与列名。
7
+ * 两个用法的分界:①每一行都是一条独立记录(批量录入),②每一行是同一条记录的一期/一栏(分期分摊)。
8
+ *
9
+ * 本票为什么先冻不接:本票的红线是**不改任何页面的可见行为**,接进去就会改(批量录入现在出通用页)。
10
+ * 故本件这一票只做「件 + 断言」,接入落在批量与修正族那一件场景件里(`src/record/scene-batch.ts`)。
11
+ *
12
+ * 老侧出处:`templates/写入/batch_confirm.html:111-149`(逐行可编辑)、`:139-143` 与 `:168-170`
13
+ * (缺金额只标红、不拦复制——施工图第四节点名要修,本件的规矩是「缺项逐行报出来,行缺了就不给复制」)。
14
+ *
15
+ * 本件的活儿比它第一眼看起来多一件:**一行的哪些格缺了,是算出来的**(`rowEditorMissing`),
16
+ * 不是渲染时顺手判的。理由:后续那张页要拿同一份判定去决定「给不给复制指令」,
17
+ * 两处各判一次必然走岔——判定只此一处,标红只是它的影子。
18
+ *
19
+ * 一件不自造:一行一张小表单走 `renderParamForm`(每格带 `name` 与 `required`,标签与控件配对)、
20
+ * 缺项那枚红标走 `renderStatusBadge`、合计行与口径走 `renderCaliberLine`、
21
+ * 零行空态走共用位的 `./emptyNote.js`(它包 base 的 `renderEmptyBlock`)。
22
+ */
23
+ import { renderStatusBadge } from 'base-paint';
24
+ import { renderCaliberLine, renderParamForm } from 'base-paint/blocks';
25
+ import { emptyNote } from './emptyNote.js';
26
+ /** 一个格给没给:`undefined`/空白串都算没给(0 算给了)。 */
27
+ function cellGiven(v) {
28
+ return typeof v === 'string' && v.trim() !== '';
29
+ }
30
+ /** 逐行缺项:第几行缺哪几列。**判定只此一处**(红标与「给不给复制」都引它)。零行返回空数组。 */
31
+ export function rowEditorMissing(input) {
32
+ const out = [];
33
+ input.rows.forEach((row, i) => {
34
+ const missing = input.fields
35
+ .filter((f) => f.required === true && !cellGiven(row[f.name]))
36
+ .map((f) => f.name);
37
+ if (missing.length > 0)
38
+ out.push({ row: i + 1, fields: missing });
39
+ });
40
+ return out;
41
+ }
42
+ /** 一行的表单字段:控件名带行号,标签带「第 N 行」。 */
43
+ function rowFields(input) {
44
+ return input.fields.map((f) => {
45
+ const value = input.row[f.name];
46
+ return {
47
+ name: input.name + '-' + (input.index + 1) + '-' + f.name,
48
+ label: '第 ' + (input.index + 1) + ' 行 ' + f.label,
49
+ ...(f.hint === undefined ? {} : { hint: f.hint }),
50
+ ...(f.required === true ? { required: true } : {}),
51
+ ...(value === undefined || value === '' ? {} : { value }),
52
+ };
53
+ });
54
+ }
55
+ /** 哪一列缺了,中文名列名(红标那句话说给人听,用中文名不用参数名)。 */
56
+ function labelsOf(fields, names) {
57
+ return names.map((n) => fields.find((f) => f.name === n)?.label ?? n).join('、');
58
+ }
59
+ /** 合计那一句:只把解析得出数的格算进来;一列全解析不出就说清「算不出」。 */
60
+ function totalLine(input) {
61
+ const name = input.totalOf;
62
+ if (name === undefined || name === '') {
63
+ return '合计 ' + input.rows.length + ' 行(未指定求和列,本表不出金额合计)。';
64
+ }
65
+ let sum = 0;
66
+ let counted = 0;
67
+ for (const row of input.rows) {
68
+ const raw = row[name];
69
+ if (raw === undefined || raw.trim() === '')
70
+ continue;
71
+ const n = Number(raw.trim());
72
+ if (!Number.isFinite(n))
73
+ continue;
74
+ sum += n;
75
+ counted += 1;
76
+ }
77
+ const label = input.totalLabel ?? '合计';
78
+ return label + ' ' + input.rows.length + ' 行,其中 ' + counted + ' 行有金额,合计 ' + sum.toFixed(2)
79
+ + (counted === input.rows.length ? '' : '(没算进去的格是数字写不清的)');
80
+ }
81
+ /** 逐行可编辑表整块:逐行一张小表单 + 缺项逐行标红 + 合计行;零行只出空态。 */
82
+ export function rowEditorTable(input) {
83
+ if (input.rows.length === 0) {
84
+ return emptyNote({
85
+ title: '这张表一行都没有',
86
+ text: '要逐行填的那张表是空的,没有东西可改。',
87
+ next: '请把每一行的' + input.fields.map((f) => f.label).join('/') + '告诉助手,再说一遍。',
88
+ });
89
+ }
90
+ const missing = rowEditorMissing(input);
91
+ const reds = missing.map((m) => renderStatusBadge({
92
+ status: 'danger',
93
+ text: '第 ' + m.row + ' 行缺:' + labelsOf(input.fields, m.fields),
94
+ }));
95
+ const rows = input.rows.map((row, i) => renderParamForm({
96
+ description: '第 ' + (i + 1) + ' 行',
97
+ fields: rowFields({ name: input.name, fields: input.fields, row, index: i }),
98
+ }));
99
+ return rows.join('') + reds.join('') + renderCaliberLine(missing.length === 0
100
+ ? '每行的必需格都齐了:' + totalLine(input)
101
+ : '有 ' + missing.length + ' 行缺必需格(缺的那几行先不给复制,补齐再说):' + totalLine(input));
102
+ }
103
+ //# sourceMappingURL=rowEditorTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rowEditorTable.js","sourceRoot":"","sources":["../../src/shared/rowEditorTable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { KpiCardInput } from 'base-paint/blocks';
2
+ /** 一型的方向口径(**唯一定义地**,四处引用都走这里):
3
+ * `sign`=这一型要的金额符号(`Math.sign` 的值),`require`=「这一型为什么得是这个符号」那一句,
4
+ * `word`=这一型的方向词(摘要行与组合徽章共用)。 */
5
+ export interface DirectionRule {
6
+ readonly word: string;
7
+ readonly sign: number;
8
+ readonly require: string;
9
+ }
10
+ /** 一型的方向口径(没有这一型=返回 `undefined`,调用方照实当「本型不管方向」)。 */
11
+ export declare function directionOf(kind: string): DirectionRule | undefined;
12
+ /** 金额一律两位小数(**唯一定义地**,比对与展示同一份口径)。`null`/非有限数/`0`=未给;`+` 只给正数。
13
+ * 上级裁定第 3 条:金额 `0.00` 配「零(本仓不记零)」自相矛盾,值栏一律写「未给」。 */
14
+ export declare function money2(amount: number | null): string;
15
+ /** 方向那句话(**唯一定义地**):符号即方向——支出负数、收入正数。零与「还没给」都照实说,不拿 0 顶。
16
+ * 用户说法照 `docs/skills/skill-bill/t407-文字审查.md` 第 43、44 条的判法:`未判(金额还没给)` 改「还没给」,
17
+ * `零(本仓不记零)` 改「还没给」(值栏摆 0.00 配「本仓不记零」自相矛盾,本级不再这么写)。 */
18
+ export declare function moneyDirection(amount: number | null): string;
19
+ /** 摘要行的事实:六格取值。`amount` 用 `null` 表示「还没给」;数字 `0` 视同没给(本仓不记零,裁定第 3 条)。 */
20
+ export interface SummaryFacts {
21
+ readonly amount: number | null;
22
+ readonly category: string;
23
+ readonly account: string;
24
+ readonly ledger: string;
25
+ readonly time: string;
26
+ }
27
+ /** 结论摘要行的几格(**唯一定义地**):采集页直接出网格,回执页把它们并进自己那张网格里。
28
+ * `amount` 未给时那一格写「未给」,不拿 0 顶替。时间缺省那句取口径层的缺省时刻,本件不抄第二份。
29
+ *
30
+ * 三处口径句改成用户说法(本轮整改,原文见 `docs/skills/skill-bill/t407-文字审查.md` 第 40、42、43、60 条):
31
+ * `L1/L2/L3 三级` 改成「分类要选到最细那一级」(内部层级名不上屏);`缺省=…` 改成「不填就记到…」;
32
+ * 采集页那一格给的是**真值**(这一笔打算记成什么),所以不再配「不填就记成…」——那是没给时才成立的话。 */
33
+ export declare function summaryCards(facts: SummaryFacts): readonly KpiCardInput[];
34
+ /** 结论摘要行:那张网格 + 方向两枚胶囊 + 分类一行口径。
35
+ * R3 改形状:原先一句 `支出的金额记成负数、收入记成正数;分类要选到最细那一级。`
36
+ * 拿顿号分方向、用分号缀分类,三件事挤一句(采集 10 页同句)。现拆三枚独立形状:
37
+ * 方向两条各一枚胶囊(分行并列),分类单独一行口径;不再用连接符串版式。
38
+ *
39
+ * 第 3 轮返工:记借入-采集页要把「必需项置顶、可选项折叠」重排进自己那张网格(首屏 8 张同形卡太挤),
40
+ * 故允许只取后面两件(`cards: false`)——那两枚胶囊与那一行口径的字面量仍只此一份,调用方不另抄一遍。 */
41
+ export declare function summaryRow(facts: SummaryFacts, options?: {
42
+ readonly cards?: boolean;
43
+ }): string;
44
+ //# sourceMappingURL=summaryRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summaryRow.d.ts","sourceRoot":"","sources":["../../src/shared/summaryRow.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD;;kCAEkC;AAClC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAOD,oDAAoD;AACpD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAEnE;AAED;qDACqD;AACrD,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGpD;AAED;;wDAEwD;AACxD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK5D;AAED,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;6DAK6D;AAC7D,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,CAazE;AAED;;;;;;gEAMgE;AAChE,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAI9F"}
@@ -0,0 +1,82 @@
1
+ /** 结论摘要行(每张页都有的一行,**唯一定义地**):金额、方向、三级分类、账户、账本、时间一眼看完。
2
+ *
3
+ * 谁在用(两个调用点,指名):
4
+ * ① `src/record/collect.ts`——过程型采集页:事实取自本次 `--params`(缺的槽位写「未给」);
5
+ * ② `src/record/receipt.ts`——结果型回执整页:事实取自写库后库内那一行(`BillRow`)。
6
+ * 第二个消费者:`src/query/`(随兄弟图 #403 的查询域一起到位,出来的是同一套采集页/回执页/复制区)。
7
+ * 本票实测:本包 `src/` 下真引用本件的是上面两个调用点,再无第三处。
8
+ *
9
+ * 四件事一处定义(别处不许再写第二条):
10
+ * - **符号即方向**:`moneyDirection` 与 `directionOf`(取值源是件内的 `DIRECTION` 表)——摘要行的方向格、
11
+ * 采集页与回执页的类型徽章那句「…要负数/要正数」、阻断条的方向判定与文案,都走这一份;
12
+ * - **金额两位小数文本**:`money2`——摘要行、重复检测的比对与列示、阻断条的方向文案共引这一份;
13
+ * - **缺省时刻**:引 `src/policy/category.ts` 的 `DEFAULT_TIME_SUFFIX`(真源在口径层,本件不留第二份);
14
+ * - **缺省值**(账本「生活」/币种「人民币」):引 `src/policy/category.ts` 的 `DEFAULTS`,本件不另立一份。
15
+ * 分两件出去(`moneyDirection` 与 `summaryRow`)是因为方向那句话在采集页的徽章与阻断条上都要引同一份措辞。
16
+ *
17
+ * 口径出处:`docs/skills/skill-bill/t407-页面块清单-16词.md` 第一节第 3 行(`renderKpiCard` + `renderKpiGrid`
18
+ * + `renderCaliberLine`)。三级分类的一级取自 `src/policy/category.ts` 的 `l1Of`(分类口径的唯一真相源),
19
+ * 本件不另写一份 L1 名单。
20
+ */
21
+ import { renderCaliberLine, renderChips, renderKpiGrid } from 'base-paint/blocks';
22
+ import { DEFAULT_TIME_SUFFIX, l1Of } from '../policy/category.js';
23
+ const DIRECTION = {
24
+ expense: { word: '支出', sign: -1, require: '记支出要负数' },
25
+ income: { word: '收入', sign: 1, require: '记收入要正数' },
26
+ };
27
+ /** 一型的方向口径(没有这一型=返回 `undefined`,调用方照实当「本型不管方向」)。 */
28
+ export function directionOf(kind) {
29
+ return DIRECTION[kind];
30
+ }
31
+ /** 金额一律两位小数(**唯一定义地**,比对与展示同一份口径)。`null`/非有限数/`0`=未给;`+` 只给正数。
32
+ * 上级裁定第 3 条:金额 `0.00` 配「零(本仓不记零)」自相矛盾,值栏一律写「未给」。 */
33
+ export function money2(amount) {
34
+ if (amount === null || !Number.isFinite(amount) || amount === 0)
35
+ return '未给';
36
+ return (amount > 0 ? '+' : '') + amount.toFixed(2);
37
+ }
38
+ /** 方向那句话(**唯一定义地**):符号即方向——支出负数、收入正数。零与「还没给」都照实说,不拿 0 顶。
39
+ * 用户说法照 `docs/skills/skill-bill/t407-文字审查.md` 第 43、44 条的判法:`未判(金额还没给)` 改「还没给」,
40
+ * `零(本仓不记零)` 改「还没给」(值栏摆 0.00 配「本仓不记零」自相矛盾,本级不再这么写)。 */
41
+ export function moneyDirection(amount) {
42
+ if (amount === null || !Number.isFinite(amount))
43
+ return '还没给';
44
+ if (amount < 0)
45
+ return '支出(取负数)';
46
+ if (amount > 0)
47
+ return '收入(取正数)';
48
+ return '还没给';
49
+ }
50
+ /** 结论摘要行的几格(**唯一定义地**):采集页直接出网格,回执页把它们并进自己那张网格里。
51
+ * `amount` 未给时那一格写「未给」,不拿 0 顶替。时间缺省那句取口径层的缺省时刻,本件不抄第二份。
52
+ *
53
+ * 三处口径句改成用户说法(本轮整改,原文见 `docs/skills/skill-bill/t407-文字审查.md` 第 40、42、43、60 条):
54
+ * `L1/L2/L3 三级` 改成「分类要选到最细那一级」(内部层级名不上屏);`缺省=…` 改成「不填就记到…」;
55
+ * 采集页那一格给的是**真值**(这一笔打算记成什么),所以不再配「不填就记成…」——那是没给时才成立的话。 */
56
+ export function summaryCards(facts) {
57
+ const l1 = facts.category.trim() === '' ? '' : l1Of(facts.category);
58
+ return [
59
+ { label: '金额', value: money2(facts.amount), detail: moneyDirection(facts.amount) },
60
+ {
61
+ label: '分类',
62
+ value: facts.category.trim() === '' ? '未给' : facts.category,
63
+ detail: facts.category.trim() === '' ? '分类要选到最细那一级' : '归在「' + l1 + '」下面',
64
+ },
65
+ { label: '账户', value: facts.account.trim() === '' ? '未给' : facts.account, detail: '不填就记到默认账户' },
66
+ { label: '账本', value: facts.ledger.trim() === '' ? '未给' : facts.ledger, detail: '不填就记到默认账本' },
67
+ { label: '时间', value: facts.time.trim() === '' ? '未给' : facts.time, detail: '不填就记成今天 ' + DEFAULT_TIME_SUFFIX },
68
+ ];
69
+ }
70
+ /** 结论摘要行:那张网格 + 方向两枚胶囊 + 分类一行口径。
71
+ * R3 改形状:原先一句 `支出的金额记成负数、收入记成正数;分类要选到最细那一级。`
72
+ * 拿顿号分方向、用分号缀分类,三件事挤一句(采集 10 页同句)。现拆三枚独立形状:
73
+ * 方向两条各一枚胶囊(分行并列),分类单独一行口径;不再用连接符串版式。
74
+ *
75
+ * 第 3 轮返工:记借入-采集页要把「必需项置顶、可选项折叠」重排进自己那张网格(首屏 8 张同形卡太挤),
76
+ * 故允许只取后面两件(`cards: false`)——那两枚胶囊与那一行口径的字面量仍只此一份,调用方不另抄一遍。 */
77
+ export function summaryRow(facts, options) {
78
+ const notes = renderChips({ items: [{ text: '支出记负数' }, { text: '收入记正数' }] })
79
+ + renderCaliberLine('分类要选到最细那一级。');
80
+ return (options?.cards === false ? '' : renderKpiGrid(summaryCards(facts))) + notes;
81
+ }
82
+ //# sourceMappingURL=summaryRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summaryRow.js","sourceRoot":"","sources":["../../src/shared/summaryRow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { StatusKind } from 'base-paint';
2
+ export { wakeWordOf } from './userWording.js';
3
+ export { nextStepOf } from './userWording.js';
4
+ /** 类型徽章的入参。 */
5
+ export interface TypeBadgeInput {
6
+ /** `params.kind`(空串=没有 preset 的「记一笔」)。 */
7
+ readonly kind: string;
8
+ /** 四档之一:`ok`=已写库、`warn`=待补、`danger`=已阻断、`empty`=无数据。 */
9
+ readonly status: StatusKind;
10
+ /** 这一页现在是什么状态(内部状态串;用户说法走 `./userWording.js` 的 `statusNoteOf`)。 */
11
+ readonly state: string;
12
+ /** 「下一步动作」那枚形状那句话(取值走 `./userWording.js` 的 `nextStepOf`)。 */
13
+ readonly next: string;
14
+ }
15
+ /** 类型徽章:唤醒词标签 + 这一页的口径 + 页面状态徽章 + 下一步动作。四枚形状各自独立,不拼分隔符串。
16
+ * R3 分行:下一步动作那句按整句拆行(句号断开),一句一行口径;单句页仍只出一行,形状不变。
17
+ * 第 3 轮返工(上级裁定第 3 条):头两枚原先挤在**一枚**胶囊里、中间用全角空格串
18
+ * (`记支出 支出 金额取负数`——一件事排了三件,且「记支出/支出」两段重字),
19
+ * 现在拆成两枚:`记支出` 一枚、`金额取负数` 一枚;口径那句不再重复唤醒词里已有的方向词。 */
20
+ export declare function typeBadge(input: TypeBadgeInput): string;
21
+ //# sourceMappingURL=typeBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeBadge.d.ts","sourceRoot":"","sources":["../../src/shared/typeBadge.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,eAAe;AACf,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAWD;;;;qDAIqD;AACrD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAgBvD"}
@@ -0,0 +1,68 @@
1
+ /** 类型徽章(写入域每张页都有的第二件,**唯一定义地**):报「这一页是哪条唤醒词、什么口径、什么状态、下一步做什么」。
2
+ *
3
+ * 谁在用(两个调用点,指名):
4
+ * ① `src/record/` 的 16 件场景件——过程型采集页:第三枚形状报「还没写库」(红档);
5
+ * ② 同一批场景件的回执页——第三枚形状报「这一笔已保存」(绿档)。
6
+ * 本票实测:本包 `src/` 下真引用本件的就是上面这批调用点,再无第三处。
7
+ *
8
+ * **不再拼分隔符串**(本轮的根因二):改前是一行字符串
9
+ * `记支出 · 支出(金额取负数) · bill.record.add · 待补槽位 · 未写库(已阻断)`——
10
+ * 一句话里塞了方向口径/命令身份/槽位状态/写库状态四件不同的东西。现在拆成**若干枚独立形状**,
11
+ * 每枚承载一件事:
12
+ * ① 唤醒词标签(`renderChips` 一枚胶囊);
13
+ * ② 这一页的口径(同一枚胶囊里的第二段;型认得出就说方向,认不出就说「分类按三级挂靠」);
14
+ * ③ 页面状态徽章(`renderStatusBadge`,四档之一);
15
+ * ④ 下一步动作(`renderCaliberLine` 一行);
16
+ * 命令身份(`bill.record.add`/`bill.record.update`)**从用户可见处去掉**(照上级裁定第 1 条):
17
+ * 它只活在复制给助手的载荷里,页面正文与徽章一个字都不印。
18
+ * 用户说法取自 `./userWording.js`(**唯一映射地**):本件不另写第二份对照表。
19
+ *
20
+ * 一型的那句方向口径(「记支出要负数」/「记收入要正数」)**不在这里另写一份**:取值走
21
+ * `./summaryRow.ts` 的 `directionOf`(符号即方向只有那一处定义),本件只把它摆进第二枚形状。
22
+ *
23
+ * 对外四件(铁律五「不多于五个」):`typeBadge`/`TypeBadgeInput`/`wakeWordOf`/`nextStepOf`(后两者转出给场景件与页标题用,
24
+ * 定义仍在 `./userWording.js` 那一处)。
25
+ *
26
+ * 口径出处:`docs/skills/skill-bill/t407-页面块清单-16词.md` 第一节第 2 行 + 第二节「类型徽章」列
27
+ * + `docs/skills/skill-bill/t407-文字审查.md` 第三节第 1 条(该改的判法与最小改法)。
28
+ */
29
+ import { renderStatusBadge } from 'base-paint';
30
+ import { renderCaliberLine, renderChips } from 'base-paint/blocks';
31
+ import { directionOf } from './summaryRow.js';
32
+ import { statusNoteOf, wakeWordOf } from './userWording.js';
33
+ export { wakeWordOf } from './userWording.js';
34
+ export { nextStepOf } from './userWording.js';
35
+ /** 第二枚形状那句话:型认得出就说方向,认不出就说分类挂靠(不再印「本仓尚未定额的型」这种内部话)。
36
+ * 第 3 轮返工:型认得出时不再把方向词抄一遍(唤醒词那枚已经写了「记支出」),只留「金额取负数」这一半。 */
37
+ function caliberOf(kind) {
38
+ const d = directionOf(kind);
39
+ if (d !== undefined)
40
+ return '金额取' + (d.sign < 0 ? '负' : '正') + '数';
41
+ if (kind.trim() === '')
42
+ return '方向按金额符号判';
43
+ return '分类按三级挂靠';
44
+ }
45
+ /** 类型徽章:唤醒词标签 + 这一页的口径 + 页面状态徽章 + 下一步动作。四枚形状各自独立,不拼分隔符串。
46
+ * R3 分行:下一步动作那句按整句拆行(句号断开),一句一行口径;单句页仍只出一行,形状不变。
47
+ * 第 3 轮返工(上级裁定第 3 条):头两枚原先挤在**一枚**胶囊里、中间用全角空格串
48
+ * (`记支出 支出 金额取负数`——一件事排了三件,且「记支出/支出」两段重字),
49
+ * 现在拆成两枚:`记支出` 一枚、`金额取负数` 一枚;口径那句不再重复唤醒词里已有的方向词。 */
50
+ export function typeBadge(input) {
51
+ const kind = typeof input.kind === 'string' ? input.kind : '';
52
+ const state = statusNoteOf(input.state);
53
+ const caliber = caliberOf(kind);
54
+ const parts = [
55
+ renderChips({
56
+ items: caliber === '' ? [{ text: wakeWordOf(kind) }] : [{ text: wakeWordOf(kind) }, { text: caliber }],
57
+ }),
58
+ renderStatusBadge(state === '' ? { status: input.status } : { status: input.status, text: state }),
59
+ ];
60
+ const next = input.next.trim();
61
+ if (next !== '') {
62
+ const lines = next.split('。').map((s) => s.trim()).filter((s) => s !== '');
63
+ for (const line of lines)
64
+ parts.push(renderCaliberLine(line + '。'));
65
+ }
66
+ return parts.join('');
67
+ }
68
+ //# sourceMappingURL=typeBadge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeBadge.js","sourceRoot":"","sources":["../../src/shared/typeBadge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAc9C;2DAC2D;AAC3D,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACnE,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,UAAU,CAAC;IAC1C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;qDAIqD;AACrD,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG;QACZ,WAAW,CAAC;YACV,KAAK,EAAE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;SACvG,CAAC;QACF,iBAAiB,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;KACnG,CAAC;IACF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3E,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}