|
|
@@ -0,0 +1,2179 @@
|
|
|
+#include "pch.h"
|
|
|
+#include "CKucunViews.h"
|
|
|
+#include "../control/ControlEx.h"
|
|
|
+
|
|
|
+namespace
|
|
|
+{
|
|
|
+ // 数值格式化(KcGroupInt / KcFormatMoney / KcFormatInt)已提到 CKucunViews.h 做 inline,
|
|
|
+ // 详情弹框 CKucunDetailWnd 与本文件共用同一份实现。
|
|
|
+
|
|
|
+ // 库存台账:状态由 当前库存 vs 安全库存 推导
|
|
|
+ // 口径(与设计稿 t5 副标题「仅显示预警 / 缺货商品」一致):
|
|
|
+ // 缺货 = 当前库存 < 安全库存
|
|
|
+ // 预警 = 安全库存 <= 当前库存 < 2 x 安全库存
|
|
|
+ // 正常 = 其余
|
|
|
+ int KcCxStatus(int cur, int safe)
|
|
|
+ {
|
|
|
+ if (cur < safe) return 2;
|
|
|
+ if (safe > 0 && cur < safe * 2) return 1;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 胶囊标签底图 / 文字色表已提到 CKucunViews.h(KcTagImage / KcTagText / KcApplyTag)。
|
|
|
+
|
|
|
+ // ---- 占位假数据(数值口径逐项对齐设计稿 t2 / t3 / t4 / t6 / t7 / t8) ----
|
|
|
+ // 入库记录:类型 = 采购入库 / 调拨入库 / 退货入库,状态 = 0 已入库 1 待入库
|
|
|
+ const KcRkItem g_rk[] = {
|
|
|
+ { L"CG20260928001", L"采购入库", L"张丽", 5, 1280.0, 0 },
|
|
|
+ { L"CG20260927003", L"采购入库", L"王强", 3, 2150.0, 0 },
|
|
|
+ { L"CG20260926002", L"调拨入库", L"张丽", 2, 860.0, 0 },
|
|
|
+ { L"CG20260925006", L"退货入库", L"李明", 6, 1530.0, 0 },
|
|
|
+ { L"CG20260924001", L"采购入库", L"王强", 4, 1600.0, 0 },
|
|
|
+ };
|
|
|
+ const int g_rk_n = (int)(sizeof(g_rk) / sizeof(g_rk[0]));
|
|
|
+
|
|
|
+ // 入库统计(按商品维度):入库数量列在设计稿里带单位("60 桶"),由 C++ 拼
|
|
|
+ const KcRkStat g_rk_stat[] = {
|
|
|
+ { L"食用油", L"调料", L"桶", 4, 60, 3300.0 },
|
|
|
+ { L"大米", L"主食", L"斤", 3, 150, 1980.0 },
|
|
|
+ { L"牛肉", L"肉类", L"斤", 2, 80, 1600.0 },
|
|
|
+ { L"面粉", L"主料", L"袋", 2, 40, 1040.0 },
|
|
|
+ { L"生抽", L"调料", L"瓶", 1, 20, 500.0 },
|
|
|
+ };
|
|
|
+ const int g_rk_stat_n = (int)(sizeof(g_rk_stat) / sizeof(g_rk_stat[0]));
|
|
|
+
|
|
|
+ // 出库记录:类型 = 调拨出库 / 报损 / 采购退货,状态 = 0 已出库
|
|
|
+ const KcCkItem g_ck[] = {
|
|
|
+ { L"CK20260928002", L"调拨出库", L"张丽", 4, 420.0, 0 },
|
|
|
+ { L"CK20260927004", L"报损", L"王强", 2, 96.0, 0 },
|
|
|
+ { L"CK20260926001", L"采购退货", L"李明", 3, 540.0, 0 },
|
|
|
+ { L"CK20260925003", L"调拨出库", L"张丽", 5, 680.0, 0 },
|
|
|
+ { L"CK20260924002", L"报损", L"王强", 1, 45.0, 0 },
|
|
|
+ };
|
|
|
+ const int g_ck_n = (int)(sizeof(g_ck) / sizeof(g_ck[0]));
|
|
|
+
|
|
|
+ const KcRkStat g_ck_stat[] = {
|
|
|
+ { L"面粉", L"主料", L"袋", 5, 35, 1450.0 },
|
|
|
+ { L"大米", L"主食", L"斤", 4, 60, 1240.0 },
|
|
|
+ { L"牛肉", L"肉类", L"斤", 3, 22, 980.0 },
|
|
|
+ { L"食用油", L"调料", L"桶", 3, 8, 880.0 },
|
|
|
+ { L"鸡蛋", L"蛋类", L"斤", 3, 18, 630.0 },
|
|
|
+ };
|
|
|
+ const int g_ck_stat_n = (int)(sizeof(g_ck_stat) / sizeof(g_ck_stat[0]));
|
|
|
+
|
|
|
+ // 盘点记录:结果 = 0 盘盈(蓝) 1 盘亏(红) 2 无差异(灰);无草稿 / 进行中态
|
|
|
+ const KcPdItem g_pd[] = {
|
|
|
+ { L"PD20260925001", L"全部商品", L"张丽", 8, 3, 2, -130.0, 1 },
|
|
|
+ { L"PD20260910002", L"主食 / 调料", L"王强", 5, 0, 0, 0.0, 2 },
|
|
|
+ };
|
|
|
+ const int g_pd_n = (int)(sizeof(g_pd) / sizeof(g_pd[0]));
|
|
|
+
|
|
|
+ // 盘点差异(按商品维度):diff 正负 ≠ 结果着色,两列各自算
|
|
|
+ const KcPdStat g_pd_stat[] = {
|
|
|
+ { L"大米", L"主食", L"斤", 2, 5, 65.0, 0 },
|
|
|
+ { L"鸡蛋", L"蛋类", L"斤", 1, 3, 39.0, 0 },
|
|
|
+ { L"土豆", L"蔬菜", L"斤", 1, 3, 6.0, 0 },
|
|
|
+ { L"番茄", L"蔬菜", L"斤", 2, -4, -24.0, 1 },
|
|
|
+ { L"牛肉", L"肉类", L"斤", 1, -6, -216.0, 1 },
|
|
|
+ };
|
|
|
+ const int g_pd_stat_n = (int)(sizeof(g_pd_stat) / sizeof(g_pd_stat[0]));
|
|
|
+
|
|
|
+ // ---- 商品档案(占位):三张编辑页 + 「添加商品」弹层 + 库存查询两张表共用 ----
|
|
|
+ // 名称 / 分类 / 单位 / 拼音码 / 库存 / 入库默认单价 与设计稿 v4 的「添加商品」列表逐项一致;
|
|
|
+ // safe / avgCost / inPrice / salePrice 四个字段取自设计稿 v2 的 t1 台账与 t9 库存金额报表,
|
|
|
+ // 保证「库存查询」「库存金额」「添加商品」三处的数字彼此自洽。
|
|
|
+ const KcGoods g_goods[] = {
|
|
|
+ // name, cat, unit, py, stock, price, safe, fixedCost, avgCost, inPrice, salePrice
|
|
|
+ { L"大米", L"主食", L"斤", L"dm", 320, 3.20, 100, false, 4.20, 4.50, 6.80 },
|
|
|
+ { L"面粉", L"主料", L"袋", L"mf", 45, 45.00, 30, true, 26.00, 26.00, 38.00 },
|
|
|
+ { L"食用油", L"调料", L"桶", L"syy", 12, 86.00, 20, false, 57.50, 55.00, 86.00 },
|
|
|
+ { L"番茄", L"蔬菜", L"斤", L"fq", 8, 4.50, 30, false, 4.00, 3.80, 6.50 },
|
|
|
+ { L"牛肉", L"肉类", L"斤", L"nr", 56, 38.00, 40, true, 28.00, 28.00, 39.90 },
|
|
|
+ { L"鸡蛋", L"蛋类", L"斤", L"jd", 18, 6.00, 25, false, 5.40, 5.20, 7.90 },
|
|
|
+ { L"生抽", L"调料", L"瓶", L"sc", 60, 15.00, 15, true, 8.50, 8.50, 15.00 },
|
|
|
+ { L"土豆", L"蔬菜", L"斤", L"td", 5, 2.80, 40, false, 2.60, 2.60, 4.50 },
|
|
|
+ };
|
|
|
+ const int g_goods_n = (int)(sizeof(g_goods) / sizeof(g_goods[0]));
|
|
|
+
|
|
|
+ // 库存查询 / 添加商品弹层共用的分类下拉项(与 g_goods 的 cat 取值一一对应)
|
|
|
+ const wchar_t* const KC_CATS[6] =
|
|
|
+ {
|
|
|
+ L"主食", L"主料", L"调料", L"蔬菜", L"肉类", L"蛋类"
|
|
|
+ };
|
|
|
+
|
|
|
+ // ---- 单据详情明细(t10 入库 / t12 出库 / t13 盘点为设计稿原文,其余同构占位) ----
|
|
|
+ // 列口径:入库/出库 = 商品 / 单位 / 数量 / 单价 / 金额;盘点 = 商品 / 单位 / 账面 / 实盘 / 差异 / 盈亏
|
|
|
+ const KcDetailRow g_drk1[] = {
|
|
|
+ { L"大米", L"斤", 100, 4.20, 420.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"面粉", L"袋", 5, 26.00, 130.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"食用油", L"桶", 5, 55.00, 275.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"生抽", L"瓶", 20, 8.50, 170.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"牛肉", L"斤", 10, 28.50, 285.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_drk2[] = {
|
|
|
+ { L"大米", L"斤", 200, 3.20, 640.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"牛肉", L"斤", 30, 38.00, 1140.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"食用油", L"桶", 5, 74.00, 370.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_drk3[] = {
|
|
|
+ { L"食用油", L"桶", 10, 60.00, 600.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"面粉", L"袋", 5, 52.00, 260.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_drk4[] = {
|
|
|
+ { L"大米", L"斤", 100, 3.20, 320.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"面粉", L"袋", 10, 32.00, 320.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"食用油", L"桶", 2, 86.00, 172.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"牛肉", L"斤", 10, 38.00, 380.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"鸡蛋", L"斤", 20, 6.00, 120.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"生抽", L"瓶", 10, 21.80, 218.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_drk5[] = {
|
|
|
+ { L"大米", L"斤", 250, 3.20, 800.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"牛肉", L"斤", 10, 38.00, 380.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"鸡蛋", L"斤", 30, 6.00, 180.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"土豆", L"斤", 80, 3.00, 240.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_dck1[] = {
|
|
|
+ { L"面粉", L"袋", 2, 28.00, 56.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"大米", L"斤", 8, 15.50, 124.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"牛肉", L"斤", 3, 40.00, 120.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"鸡蛋", L"斤", 12, 10.00, 120.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_dck2[] = {
|
|
|
+ { L"番茄", L"斤", 12, 4.50, 54.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"鸡蛋", L"斤", 7, 6.00, 42.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_dck3[] = {
|
|
|
+ { L"大米", L"斤", 50, 3.20, 160.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"面粉", L"袋", 4, 45.00, 180.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"食用油", L"桶", 2, 100.00, 200.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_dck4[] = {
|
|
|
+ { L"大米", L"斤", 20, 3.20, 64.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"面粉", L"袋", 2, 45.00, 90.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"食用油", L"桶", 2, 86.00, 172.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"牛肉", L"斤", 5, 38.00, 190.00, 0, 0, 0, 0.0 },
|
|
|
+ { L"生抽", L"瓶", 10, 16.40, 164.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_dck5[] = {
|
|
|
+ { L"番茄", L"斤", 10, 4.50, 45.00, 0, 0, 0, 0.0 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_dpd1[] = {
|
|
|
+ { L"大米", L"斤", 0, 0.0, 0.0, 315, 320, 5, 65.00 },
|
|
|
+ { L"鸡蛋", L"斤", 0, 0.0, 0.0, 15, 18, 3, 39.00 },
|
|
|
+ { L"土豆", L"斤", 0, 0.0, 0.0, 2, 5, 3, 6.00 },
|
|
|
+ { L"番茄", L"斤", 0, 0.0, 0.0, 12, 8, -4, -24.00 },
|
|
|
+ { L"牛肉", L"斤", 0, 0.0, 0.0, 62, 56, -6, -216.00 },
|
|
|
+ };
|
|
|
+ const KcDetailRow g_dpd2[] = {
|
|
|
+ { L"大米", L"斤", 0, 0.0, 0.0, 300, 300, 0, 0.00 },
|
|
|
+ { L"面粉", L"袋", 0, 0.0, 0.0, 40, 40, 0, 0.00 },
|
|
|
+ { L"食用油", L"桶", 0, 0.0, 0.0, 10, 10, 0, 0.00 },
|
|
|
+ { L"生抽", L"瓶", 0, 0.0, 0.0, 50, 50, 0, 0.00 },
|
|
|
+ { L"鸡蛋", L"斤", 0, 0.0, 0.0, 20, 20, 0, 0.00 },
|
|
|
+ };
|
|
|
+
|
|
|
+ // ---- 单据详情头(kind:0 入库 / 1 出库 / 2 盘点) ----
|
|
|
+ struct KcDocRec
|
|
|
+ {
|
|
|
+ int kind; LPCTSTR title, sub, no, f2lab, f2txt; int f2tag;
|
|
|
+ LPCTSTR status; int statTag; LPCTSTR whoLab, who, timeLab, time; int printCnt;
|
|
|
+ const KcDetailRow* rows; int n;
|
|
|
+ };
|
|
|
+ const KcDocRec g_docs[] = {
|
|
|
+ { 0, L"入库单详情", L"出库单 / 盘点单详情同构", L"CG20260928001", L"入库类型", L"采购入库", KC_TAG_GRN,
|
|
|
+ L"已入库", KC_TAG_GRN, L"经手人", L"张丽", L"入库时间", L"2026-09-28 14:32", 0,
|
|
|
+ g_drk1, 5 },
|
|
|
+ { 0, L"入库单详情", L"出库单 / 盘点单详情同构", L"CG20260927003", L"入库类型", L"采购入库", KC_TAG_GRN,
|
|
|
+ L"已入库", KC_TAG_GRN, L"经手人", L"王强", L"入库时间", L"2026-09-27 10:05", 0,
|
|
|
+ g_drk2, 3 },
|
|
|
+ { 0, L"入库单详情", L"出库单 / 盘点单详情同构", L"CG20260926002", L"入库类型", L"调拨入库", KC_TAG_BLUE,
|
|
|
+ L"已入库", KC_TAG_GRN, L"经手人", L"张丽", L"入库时间", L"2026-09-26 09:18", 0,
|
|
|
+ g_drk3, 2 },
|
|
|
+ { 0, L"入库单详情", L"出库单 / 盘点单详情同构", L"CG20260925006", L"入库类型", L"退货入库", KC_TAG_ORG,
|
|
|
+ L"已入库", KC_TAG_GRN, L"经手人", L"李明", L"入库时间", L"2026-09-25 16:02", 0,
|
|
|
+ g_drk4, 6 },
|
|
|
+ { 0, L"入库单详情", L"出库单 / 盘点单详情同构", L"CG20260924001", L"入库类型", L"采购入库", KC_TAG_GRN,
|
|
|
+ L"已入库", KC_TAG_GRN, L"经手人", L"王强", L"入库时间", L"2026-09-24 08:47", 0,
|
|
|
+ g_drk5, 4 },
|
|
|
+ { 1, L"出库单详情", L"入库单 / 盘点单详情同构", L"CK20260928002", L"出库类型", L"调拨出库", KC_TAG_BLUE,
|
|
|
+ L"已出库", KC_TAG_GRN, L"经手人", L"张丽", L"出库时间", L"2026-09-28 11:20", 0,
|
|
|
+ g_dck1, 4 },
|
|
|
+ { 1, L"出库单详情", L"入库单 / 盘点单详情同构", L"CK20260927004", L"出库类型", L"报损", KC_TAG_RED,
|
|
|
+ L"已出库", KC_TAG_GRN, L"经手人", L"王强", L"出库时间", L"2026-09-27 15:33", 0,
|
|
|
+ g_dck2, 2 },
|
|
|
+ { 1, L"出库单详情", L"入库单 / 盘点单详情同构", L"CK20260926001", L"出库类型", L"采购退货", KC_TAG_ORG,
|
|
|
+ L"已出库", KC_TAG_GRN, L"经手人", L"李明", L"出库时间", L"2026-09-26 14:11", 0,
|
|
|
+ g_dck3, 3 },
|
|
|
+ { 1, L"出库单详情", L"入库单 / 盘点单详情同构", L"CK20260925003", L"出库类型", L"调拨出库", KC_TAG_BLUE,
|
|
|
+ L"已出库", KC_TAG_GRN, L"经手人", L"张丽", L"出库时间", L"2026-09-25 10:26", 0,
|
|
|
+ g_dck4, 5 },
|
|
|
+ { 1, L"出库单详情", L"入库单 / 盘点单详情同构", L"CK20260924002", L"出库类型", L"报损", KC_TAG_RED,
|
|
|
+ L"已出库", KC_TAG_GRN, L"经手人", L"王强", L"出库时间", L"2026-09-24 17:50", 0,
|
|
|
+ g_dck5, 1 },
|
|
|
+ { 2, L"盘点单详情", L"含 账面 / 实盘 / 差异 列", L"PD20260925001", L"盘点范围", L"全部商品", -1,
|
|
|
+ L"已完成", KC_TAG_GRN, L"盘点人", L"张丽", L"盘点时间", L"2026-09-25 16:40", 0,
|
|
|
+ g_dpd1, 5 },
|
|
|
+ { 2, L"盘点单详情", L"含 账面 / 实盘 / 差异 列", L"PD20260910002", L"盘点范围", L"主食 / 调料", -1,
|
|
|
+ L"已完成", KC_TAG_GRN, L"盘点人", L"王强", L"盘点时间", L"2026-09-10 15:20", 0,
|
|
|
+ g_dpd2, 5 },
|
|
|
+ };
|
|
|
+ const int g_docs_n = (int)(sizeof(g_docs) / sizeof(g_docs[0]));
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 商品档案访问器 ============================
|
|
|
+const std::vector<KcGoods>& KcAllGoods()
|
|
|
+{
|
|
|
+ static std::vector<KcGoods> s_all;
|
|
|
+ if (s_all.empty())
|
|
|
+ {
|
|
|
+ for (int i = 0; i < g_goods_n; i++) s_all.push_back(g_goods[i]);
|
|
|
+ }
|
|
|
+ return s_all;
|
|
|
+}
|
|
|
+
|
|
|
+bool KcLookupGoods(const std::wstring& name, KcGoods& out)
|
|
|
+{
|
|
|
+ const std::vector<KcGoods>& all = KcAllGoods();
|
|
|
+ for (size_t i = 0; i < all.size(); i++)
|
|
|
+ {
|
|
|
+ if (all[i].name == name) { out = all[i]; return true; }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 单据详情访问器 ============================
|
|
|
+// 把匿名命名空间里的静态表拷成值语义的 KcDocDetail,弹框侧不再依赖任何文件内私有符号
|
|
|
+bool KcLookupDetail(const std::wstring& no, KcDocDetail& out)
|
|
|
+{
|
|
|
+ for (int i = 0; i < g_docs_n; i++)
|
|
|
+ {
|
|
|
+ const KcDocRec& d = g_docs[i];
|
|
|
+ if (no != d.no) continue;
|
|
|
+
|
|
|
+ out.kind = d.kind;
|
|
|
+ out.title = d.title;
|
|
|
+ out.sub = d.sub;
|
|
|
+ out.no = d.no;
|
|
|
+ out.f2lab = d.f2lab;
|
|
|
+ out.f2txt = d.f2txt;
|
|
|
+ out.f2tag = d.f2tag;
|
|
|
+ out.status = d.status;
|
|
|
+ out.statTag = d.statTag;
|
|
|
+ out.whoLab = d.whoLab;
|
|
|
+ out.who = d.who;
|
|
|
+ out.timeLab = d.timeLab;
|
|
|
+ out.time = d.time;
|
|
|
+ out.printCnt = d.printCnt;
|
|
|
+ out.rows.clear();
|
|
|
+ for (int k = 0; k < d.n; k++) out.rows.push_back(d.rows[k]);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ CKucunViewBase ============================
|
|
|
+CControlUI* CKucunViewBase::Find(LPCTSTR name)
|
|
|
+{
|
|
|
+ if (m_pHost == NULL) return NULL;
|
|
|
+ return m_pHost->FindSubControl(name);
|
|
|
+}
|
|
|
+
|
|
|
+CListContainerElementUI* CKucunViewBase::NewRow(LPCTSTR xmlfile)
|
|
|
+{
|
|
|
+ CDialogBuilder builder;
|
|
|
+ CDialogBuilderCallbackEx cb;
|
|
|
+ CControlUI* p = builder.Create(xmlfile, (UINT)0, &cb, m_pManager);
|
|
|
+ return static_cast<CListContainerElementUI*>(p);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::ClearList(CListUI* list)
|
|
|
+{
|
|
|
+ if (list == NULL) return;
|
|
|
+ while (list->GetCount() > 0) list->RemoveAt(0);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SetRowText(CListContainerElementUI* row, LPCTSTR name, LPCTSTR text)
|
|
|
+{
|
|
|
+ if (row == NULL) return;
|
|
|
+ CLabelUI* p = static_cast<CLabelUI*>(row->FindSubControl(name));
|
|
|
+ if (p) p->SetText(text);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SetRowColor(CListContainerElementUI* row, LPCTSTR name, DWORD color)
|
|
|
+{
|
|
|
+ if (row == NULL) return;
|
|
|
+ CLabelUI* p = static_cast<CLabelUI*>(row->FindSubControl(name));
|
|
|
+ if (p) p->SetTextColor(color);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SetRowWidth(CListContainerElementUI* row, LPCTSTR name, int width)
|
|
|
+{
|
|
|
+ if (row == NULL) return;
|
|
|
+ CControlUI* p = row->FindSubControl(name);
|
|
|
+ if (p) p->SetFixedWidth(width);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SetTag(CListContainerElementUI* row, LPCTSTR name, LPCTSTR text, int style)
|
|
|
+{
|
|
|
+ if (row == NULL) return;
|
|
|
+ CLabelUI* p = static_cast<CLabelUI*>(row->FindSubControl(name));
|
|
|
+ if (p == NULL) return;
|
|
|
+ KcApplyTag(p, std::wstring(text), style);
|
|
|
+}
|
|
|
+
|
|
|
+// 统计卡整数:数值 26px 粗体 + 单位 13px 灰(showhtml 里 {f 13} 切字号、{c #8A9099} 转灰)
|
|
|
+void CKucunViewBase::SetStatInt(LPCTSTR name, int val, LPCWSTR unit)
|
|
|
+{
|
|
|
+ CLabelUI* p = FindC<CLabelUI>(name);
|
|
|
+ if (p == NULL) return;
|
|
|
+ std::wstring s = KcGroupInt(std::to_wstring(val));
|
|
|
+ s += L"{x 4}{c #8A9099}{f 13}"; s += unit; s += L"{/f}{/c}";
|
|
|
+ p->SetText(s.c_str());
|
|
|
+ p->SetFont(p->GetFont()); // 重置 autocalcwidth 缓存
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SetStatMoney(LPCTSTR name, double val, LPCWSTR unit)
|
|
|
+{
|
|
|
+ CLabelUI* p = FindC<CLabelUI>(name);
|
|
|
+ if (p == NULL) return;
|
|
|
+ std::wstring s = KcFormatInt(val);
|
|
|
+ s += L"{x 4}{c #8A9099}{f 13}"; s += unit; s += L"{/f}{/c}";
|
|
|
+ p->SetText(s.c_str());
|
|
|
+ p->SetFont(p->GetFont());
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SetListCount(LPCTSTR labelName, int n)
|
|
|
+{
|
|
|
+ CLabelUI* p = FindC<CLabelUI>(labelName);
|
|
|
+ if (p == NULL) return;
|
|
|
+ std::wstring s = L"共 "; s += std::to_wstring(n); s += L" 项";
|
|
|
+ p->SetText(s.c_str());
|
|
|
+}
|
|
|
+
|
|
|
+// 台账 / 库存金额的表尾:分页口径(每页 KC_PAGE_SIZE 条)
|
|
|
+void CKucunViewBase::SetFootPaged(LPCTSTR name, int shown, int total)
|
|
|
+{
|
|
|
+ CLabelUI* p = FindC<CLabelUI>(name);
|
|
|
+ if (p == NULL) return;
|
|
|
+ std::wstring s;
|
|
|
+ if (shown < total)
|
|
|
+ {
|
|
|
+ s = L"上滑自动加载下一页 · 已显示 ";
|
|
|
+ s += std::to_wstring(shown); s += L" / "; s += std::to_wstring(total); s += L" 项";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ s = L"已全部加载 · 共 "; s += std::to_wstring(total); s += L" 项";
|
|
|
+ }
|
|
|
+ p->SetText(s.c_str());
|
|
|
+}
|
|
|
+
|
|
|
+// 记录列表的表尾:按时间倒序 + 「默认加载最近 N 条」
|
|
|
+void CKucunViewBase::SetFootRecord(LPCTSTR name, int shown, int total)
|
|
|
+{
|
|
|
+ CLabelUI* p = FindC<CLabelUI>(name);
|
|
|
+ if (p == NULL) return;
|
|
|
+ std::wstring s;
|
|
|
+ if (shown < total)
|
|
|
+ {
|
|
|
+ s = L"按时间倒序 · 默认加载最近 "; s += std::to_wstring(KC_PAGE_SIZE);
|
|
|
+ s += L" 条 · 上滑自动加载更多 · 已显示 "; s += std::to_wstring(shown); s += L" 条";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ s = L"按时间倒序 · 已全部加载 · 共 "; s += std::to_wstring(total); s += L" 条";
|
|
|
+ }
|
|
|
+ p->SetText(s.c_str());
|
|
|
+}
|
|
|
+
|
|
|
+// 统计视图的表尾:一次全量,不分页
|
|
|
+void CKucunViewBase::SetFootAll(LPCTSTR name, int total)
|
|
|
+{
|
|
|
+ CLabelUI* p = FindC<CLabelUI>(name);
|
|
|
+ if (p == NULL) return;
|
|
|
+ std::wstring s = L"已全部加载 · 共 "; s += std::to_wstring(total); s += L" 项";
|
|
|
+ p->SetText(s.c_str());
|
|
|
+}
|
|
|
+
|
|
|
+// 切换胶囊三态(R45 对着设计稿 v2 重做):选中 = 全圆实心绿胶囊 + 白字,
|
|
|
+// 未选中 = 透明底 + 灰字;hover / pushed 兜底图换成灰度胶囊,保证触屏下按下去有反馈。
|
|
|
+void CKucunViewBase::StyleSeg(CButtonUI* p, bool act)
|
|
|
+{
|
|
|
+ if (p == NULL) return;
|
|
|
+ // 圆度来自源图自身(kc_seggrn_* 源图 radius 16),九宫格 corner 只定「取多大一块」
|
|
|
+ p->SetNormalImage(act ? L"file='kc_seggrn_n.png' corner='16,16,16,16'" : L"");
|
|
|
+ p->SetHotImage(act ? L"file='kc_seggrn_h.png' corner='16,16,16,16'"
|
|
|
+ : L"file='kc_seggray_h.png' corner='16,16,16,16'");
|
|
|
+ p->SetPushedImage(act ? L"file='kc_seggrn_p.png' corner='16,16,16,16'"
|
|
|
+ : L"file='kc_seggray_p.png' corner='16,16,16,16'");
|
|
|
+ p->SetFont(act ? 113 : 13);
|
|
|
+ p->SetTextColor(act ? 0xFFFFFFFF : 0xFF5F6368);
|
|
|
+ p->SetHotTextColor(act ? 0xFFFFFFFF : 0xFF2E9B5C);
|
|
|
+ p->SetPushedTextColor(act ? 0xFFFFFFFF : 0xFF2E9B5C);
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------- 页面级滚动的高度传播
|
|
|
+// 【R45】滚动条上移到页面级容器 kc_scroll(见 kucun_page.xml)之后,滚动容器里
|
|
|
+// 「最多只能有一个弹性子项」:CVerticalLayoutUI::SetPos 会对 EstimateSize().cy == 0
|
|
|
+// 的子项做 cyExpand 均分剩余高度,视图容器若留在弹性状态,cyFixed 恒等于视口高
|
|
|
+// ⇒ cyNeeded 恒等于视口高 ⇒ 滚动范围恒为 0,整页根本不滚。
|
|
|
+// 而 CContainerUI::DoPaint 会把绘制裁剪到自己的 m_rcItem 上,容器没被撑开时
|
|
|
+// 溢出的行既画不出来、外层也拿不到真实高度。
|
|
|
+// 所以「把某层撑开后,必须沿父链逐级把祖先的高度也算出来写死」,一直做到视图容器为止
|
|
|
+// (遇到滚动容器 kc_scroll 就停,滚动容器自身的尺寸由页面根给,不能写死)。
|
|
|
+// 【R48】三个「新建单」页已经从 kc_scroll 提到 kc_page 下、并且自带滚动主体
|
|
|
+// (kc_rkn_body / kc_ckn_body / kc_pdn_body,弹性高度 + vscrollbar="true")。
|
|
|
+// 这些页里**不能**再往上写死高度 —— body 一旦有了固定高度,竖向布局就按这个高度摆放,
|
|
|
+// body 自己的滚动范围恒为 0、整页也不会滚。所以父链走到 body 就停。
|
|
|
+// 每层的算法:各「可见、非 float」子项的固定高度之和 + 上下 inset + childpadding*(nVis-1);
|
|
|
+// 只要发现某个可见子项没有确定高度(弹性项)就整层放弃,绝不瞎猜。
|
|
|
+static void KcPropagateFixedHeightUp(CControlUI* pFrom)
|
|
|
+{
|
|
|
+ for (int guard = 0; pFrom != NULL && guard < 8; guard++)
|
|
|
+ {
|
|
|
+ CControlUI* p = pFrom->GetParent();
|
|
|
+ if (p == NULL) return;
|
|
|
+ CContainerUI* pC = dynamic_cast<CContainerUI*>(p);
|
|
|
+ if (pC == NULL) return;
|
|
|
+ LPCTSTR nm = pC->GetName();
|
|
|
+ if (nm != NULL && _tcscmp(nm, _T("kc_scroll")) == 0) return; // 滚动容器不写死
|
|
|
+ if (nm != NULL &&
|
|
|
+ (_tcscmp(nm, _T("kc_rkn_body")) == 0 ||
|
|
|
+ _tcscmp(nm, _T("kc_ckn_body")) == 0 ||
|
|
|
+ _tcscmp(nm, _T("kc_pdn_body")) == 0)) return; // 新建单页的滚动主体
|
|
|
+
|
|
|
+ RECT rcIn = pC->GetInset();
|
|
|
+ int total = rcIn.top + rcIn.bottom;
|
|
|
+ int nVis = 0;
|
|
|
+ bool bOk = true;
|
|
|
+ for (int i = 0; i < pC->GetCount(); i++)
|
|
|
+ {
|
|
|
+ CControlUI* q = pC->GetItemAt(i);
|
|
|
+ if (q == NULL || !q->IsVisible() || q->IsFloat()) continue;
|
|
|
+ int ch = q->GetFixedHeight();
|
|
|
+ if (ch <= 0) ch = q->GetMaxHeight();
|
|
|
+ if (ch <= 0 || ch > 4000) { bOk = false; break; } // 有弹性子项 ⇒ 整层放弃
|
|
|
+ total += ch;
|
|
|
+ nVis++;
|
|
|
+ }
|
|
|
+ if (!bOk || nVis <= 0) return;
|
|
|
+ if (nVis > 1) total += pC->GetChildPadding() * (nVis - 1);
|
|
|
+ pC->SetFixedHeight(total);
|
|
|
+ pFrom = pC;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 整页滚动适配:把 List 高度撑成「表头(44) + nRows*行高(54) + List 自身的上下 inset」,
|
|
|
+// 再沿父链把每层容器的高度逐级写死(见 KcPropagateFixedHeightUp)。
|
|
|
+// 空表至少留一行高,避免卡片塌陷。
|
|
|
+void CKucunViewBase::FitListHeight(LPCTSTR listName, int nRows)
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(listName);
|
|
|
+ if (pList == NULL) return;
|
|
|
+ const int HEADER_H = 44; // 与 kucun_page.xml 中 <ListHeader height="44"> 一致
|
|
|
+ const int ROW_H = 54; // 与 kc_row_*.xml 中 <ListContainerElement height="54"> 一致
|
|
|
+ int rows = (nRows > 0) ? nRows : 1;
|
|
|
+ // 【R45】8 张记录表的 List 新增 inset="1,1,1,10"(让出 kc_tblcard.png 的 1px 外框
|
|
|
+ // 与 10px 圆角底带)。inset 由 CContainerUI::SetPos 扣在自己的内容区上、不改 m_rcItem,
|
|
|
+ // 所以固定高度必须把这 11px 一并算上,否则最下面一行会被裁掉。
|
|
|
+ RECT rcLi = pList->GetInset();
|
|
|
+ int h = rcLi.top + rcLi.bottom + HEADER_H + rows * ROW_H;
|
|
|
+ pList->SetFixedHeight(h);
|
|
|
+
|
|
|
+ // 【R45】最后一行不画行分隔线(设计稿 .tr:last-child{border-bottom:none})。
|
|
|
+ // 行模板 kc_row_*.xml 统一带 bordersize="0,0,0,1",若不清掉,最后一行的底边会停在
|
|
|
+ // kc_tblcard.png 圆角底带上方 10px 处,和卡片自己的 1px 下边框凑成两条平行线。
|
|
|
+ // 表底那条线交给 kc_tblcard.png 的外框负责。
|
|
|
+ int nCount = pList->GetCount();
|
|
|
+ if (nCount > 0)
|
|
|
+ {
|
|
|
+ CControlUI* pLast = pList->GetItemAt(nCount - 1);
|
|
|
+ if (pLast != NULL)
|
|
|
+ {
|
|
|
+ RECT rcNoBorder = { 0, 0, 0, 0 };
|
|
|
+ pLast->SetBorderSize(rcNoBorder);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ KcPropagateFixedHeightUp(pList);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::AddComboItem(CComboUI* combo, LPCWSTR text)
|
|
|
+{
|
|
|
+ if (combo == NULL) return;
|
|
|
+ CListLabelElementUI* pElem = new CListLabelElementUI();
|
|
|
+ pElem->SetText(text);
|
|
|
+ combo->Add(pElem);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SelectCombo(CComboUI* combo, int idx)
|
|
|
+{
|
|
|
+ if (combo == NULL) return;
|
|
|
+ combo->SelectItem(idx, false);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::ReadSearch(LPCTSTR editName, std::wstring& out)
|
|
|
+{
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(editName);
|
|
|
+ if (pEdit != NULL) out = (LPCTSTR)pEdit->GetText();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunViewBase::SyncSearchTishi(LPCTSTR tishiName, LPCTSTR editName)
|
|
|
+{
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(editName);
|
|
|
+ if (pEdit == NULL) return;
|
|
|
+ CLabelUI* pTishi = FindC<CLabelUI>(tishiName);
|
|
|
+ if (pTishi == NULL) return;
|
|
|
+ // 只看「有没有内容」:Edit 是透明的,占位 Label 压在它下面那层,没有原生 placeholder 可用。
|
|
|
+ pTishi->SetVisible(((LPCTSTR)pEdit->GetText())[0] == 0);
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 视图1:库存查询 ============================
|
|
|
+// 两个子视图共用统计卡与筛选行,只有表格区互换:
|
|
|
+// kc_cx_body_state 库存状态(库存台账,6 列,带「只看预警商品」开关)
|
|
|
+// kc_cx_body_amt 库存金额(11 列只读报表,设计稿 t9,无开关)
|
|
|
+void CKucunChaxunView::OnInitControl()
|
|
|
+{
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_cx_combo_cat");
|
|
|
+ if (pCat)
|
|
|
+ {
|
|
|
+ AddComboItem(pCat, L"全部分类");
|
|
|
+ for (int i = 0; i < 6; i++) AddComboItem(pCat, KC_CATS[i]);
|
|
|
+ SelectCombo(pCat, 0);
|
|
|
+ }
|
|
|
+ ApplyWarnSwitch();
|
|
|
+ SetSeg(0, false);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChaxunView::InitShow()
|
|
|
+{
|
|
|
+ // 统计卡口径:缺货 = 当前库存 < 安全库存;预警 = 安全库存 <= 当前库存 < 2 x 安全库存。
|
|
|
+ // 库存总值 = 加权平均成本价 x 当前库存(与「库存金额」报表的库存成本同口径)。
|
|
|
+ const std::vector<KcGoods>& all = KcAllGoods();
|
|
|
+ int nTotal = (int)all.size(), nWarn = 0, nLack = 0;
|
|
|
+ double dValue = 0.0;
|
|
|
+ for (size_t i = 0; i < all.size(); i++)
|
|
|
+ {
|
|
|
+ int st = KcCxStatus(all[i].stock, all[i].safe);
|
|
|
+ if (st == 1) nWarn++;
|
|
|
+ else if (st == 2) nLack++;
|
|
|
+ dValue += all[i].avgCost * all[i].stock;
|
|
|
+ }
|
|
|
+ SetStatInt(L"kc_cx_stat_total", nTotal, L"项");
|
|
|
+ SetStatInt(L"kc_cx_stat_warn", nWarn, L"项");
|
|
|
+ SetStatInt(L"kc_cx_stat_lack", nLack, L"项");
|
|
|
+ SetStatMoney(L"kc_cx_stat_value", dValue, L"元");
|
|
|
+
|
|
|
+ // 搜索框 / 分类下拉复位(重新进页面时不该带着上次的条件)
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_cx_combo_cat");
|
|
|
+ if (pCat) SelectCombo(pCat, 0);
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(L"kc_cx_search_edit");
|
|
|
+ if (pEdit) pEdit->SetText(L"");
|
|
|
+ m_strSearch.clear();
|
|
|
+ SyncSearchTishi(L"kc_cx_search_tishi", L"kc_cx_search_edit");
|
|
|
+
|
|
|
+ SetSeg(m_nSeg, true);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChaxunView::SetSeg(int active, bool bBuild)
|
|
|
+{
|
|
|
+ m_nSeg = active;
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_cx_seg_state"), active == 0);
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_cx_seg_amt"), active == 1);
|
|
|
+
|
|
|
+ // 「只看预警商品」只在库存状态视图出现(设计稿 t9 的工具条没有这一项)
|
|
|
+ CControlUI* pSwx = Find(L"kc_cx_swx");
|
|
|
+ if (pSwx) pSwx->SetVisible(active == 0);
|
|
|
+
|
|
|
+ CControlUI* pState = Find(L"kc_cx_body_state");
|
|
|
+ CControlUI* pAmt = Find(L"kc_cx_body_amt");
|
|
|
+ if (pState) pState->SetVisible(active == 0);
|
|
|
+ if (pAmt) pAmt->SetVisible(active == 1);
|
|
|
+
|
|
|
+ if (bBuild) { if (active == 0) BuildStateRows(); else BuildAmtRows(); }
|
|
|
+}
|
|
|
+
|
|
|
+// 开关用「两张整图换 bkimage」:圆点位置已经烘进 PNG,不需要浮层定位子控件。
|
|
|
+// main.xml 的全局 Default 会给所有 Button 套 btn_green_*,所以三个态都要显式覆盖。
|
|
|
+void CKucunChaxunView::ApplyWarnSwitch()
|
|
|
+{
|
|
|
+ CButtonUI* p = FindC<CButtonUI>(L"kc_cx_warn_switch");
|
|
|
+ if (p == NULL) return;
|
|
|
+ LPCTSTR img = m_bWarnOnly ? L"file='kc_switch_on.png'" : L"file='kc_switch_off.png'";
|
|
|
+ p->SetNormalImage(img);
|
|
|
+ p->SetHotImage(img);
|
|
|
+ p->SetPushedImage(img);
|
|
|
+}
|
|
|
+
|
|
|
+// 分类 + 关键字过滤(两张表共用)
|
|
|
+bool CKucunChaxunView::RowPass(const KcGoods& g, const std::wstring& cat) const
|
|
|
+{
|
|
|
+ if (!cat.empty() && cat != L"全部分类" && g.cat != cat) return false;
|
|
|
+ if (!m_strSearch.empty() && g.name.find(m_strSearch) == std::wstring::npos) return false;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChaxunView::BuildStateRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_cx_list");
|
|
|
+ if (pList == NULL) return;
|
|
|
+
|
|
|
+ std::wstring cat;
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_cx_combo_cat");
|
|
|
+ if (pCat) cat = (LPCTSTR)pCat->GetText();
|
|
|
+ ReadSearch(L"kc_cx_search_edit", m_strSearch);
|
|
|
+
|
|
|
+ const std::vector<KcGoods>& all = KcAllGoods();
|
|
|
+ ClearList(pList);
|
|
|
+ int nMatched = 0, nAdded = 0;
|
|
|
+ for (size_t i = 0; i < all.size(); i++)
|
|
|
+ {
|
|
|
+ const KcGoods& g = all[i];
|
|
|
+ int st = KcCxStatus(g.stock, g.safe);
|
|
|
+ if (m_bWarnOnly && st == 0) continue; // 开关开 = 只要「预警 + 缺货」
|
|
|
+ if (!RowPass(g, cat)) continue;
|
|
|
+ nMatched++;
|
|
|
+ if (nAdded >= KC_PAGE_SIZE) continue; // 本页已满,只继续统计总数
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_cx.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ SetRowText(row, L"col_name", g.name.c_str());
|
|
|
+ SetRowText(row, L"col_cat", g.cat.c_str());
|
|
|
+ SetRowText(row, L"col_unit", g.unit.c_str());
|
|
|
+ SetRowText(row, L"col_cur", std::to_wstring(g.stock).c_str());
|
|
|
+ SetRowText(row, L"col_safe", std::to_wstring(g.safe).c_str());
|
|
|
+
|
|
|
+ const wchar_t* stxt; int stag;
|
|
|
+ if (st == 0) { stxt = L"正常"; stag = KC_TAG_GRN; }
|
|
|
+ else if (st == 1) { stxt = L"预警"; stag = KC_TAG_ORG; }
|
|
|
+ else { stxt = L"缺货"; stag = KC_TAG_RED; }
|
|
|
+ SetTag(row, L"tag_status", stxt, stag);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_cx_list_count", nAdded);
|
|
|
+ SetFootPaged(L"kc_cx_list_foot", nAdded, nMatched);
|
|
|
+ FitListHeight(L"kc_cx_list", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChaxunView::BuildAmtRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_cx_amt_list");
|
|
|
+ if (pList == NULL) return;
|
|
|
+
|
|
|
+ std::wstring cat;
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_cx_combo_cat");
|
|
|
+ if (pCat) cat = (LPCTSTR)pCat->GetText();
|
|
|
+ ReadSearch(L"kc_cx_search_edit", m_strSearch);
|
|
|
+
|
|
|
+ // 报表口径(设计稿 t9):
|
|
|
+ // 库存成本 = 加权平均成本价 x 当前库存
|
|
|
+ // 预计销售额 = 当前销售价 x 当前库存
|
|
|
+ // 预计利润 = 预计销售额 - 库存成本
|
|
|
+ // 预计利润率 = 预计利润 / 预计销售额
|
|
|
+ const std::vector<KcGoods>& all = KcAllGoods();
|
|
|
+ ClearList(pList);
|
|
|
+ int nMatched = 0, nAdded = 0;
|
|
|
+ for (size_t i = 0; i < all.size(); i++)
|
|
|
+ {
|
|
|
+ const KcGoods& g = all[i];
|
|
|
+ if (!RowPass(g, cat)) continue;
|
|
|
+ nMatched++;
|
|
|
+ if (nAdded >= KC_PAGE_SIZE) continue;
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_cx_amt.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ double cost = g.avgCost * g.stock;
|
|
|
+ double amt = g.salePrice * g.stock;
|
|
|
+ double profit = amt - cost;
|
|
|
+
|
|
|
+ SetRowText(row, L"col_name", g.name.c_str());
|
|
|
+ SetTag(row, L"tag_mode", g.fixedCost ? L"固定价" : L"加权平均",
|
|
|
+ g.fixedCost ? KC_TAG_GRAY : KC_TAG_BLUE);
|
|
|
+ SetRowText(row, L"col_in", KcFormatMoney(g.inPrice).c_str());
|
|
|
+ SetRowText(row, L"col_avg", KcFormatMoney(g.avgCost).c_str());
|
|
|
+ SetRowText(row, L"col_cur", std::to_wstring(g.stock).c_str());
|
|
|
+ SetRowText(row, L"col_unit", g.unit.c_str());
|
|
|
+ SetRowText(row, L"col_cost", KcFormatMoney(cost).c_str());
|
|
|
+ SetRowText(row, L"col_sale", KcFormatMoney(g.salePrice).c_str());
|
|
|
+ SetRowText(row, L"col_amt", KcFormatMoney(amt).c_str());
|
|
|
+ SetRowText(row, L"col_profit", KcFormatMoney(profit).c_str());
|
|
|
+ wchar_t buf[32];
|
|
|
+ swprintf_s(buf, _countof(buf), L"%.1f%%", amt > 0.0 ? profit * 100.0 / amt : 0.0);
|
|
|
+ SetRowText(row, L"col_rate", buf);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_cx_amt_count", nAdded);
|
|
|
+ SetFootPaged(L"kc_cx_amt_foot", nAdded, nMatched);
|
|
|
+ FitListHeight(L"kc_cx_amt_list", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChaxunView::OnClick(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (name == L"kc_cx_seg_state") { SetSeg(0, true); }
|
|
|
+ else if (name == L"kc_cx_seg_amt") { SetSeg(1, true); }
|
|
|
+ else if (name == L"kc_cx_warn_switch")
|
|
|
+ {
|
|
|
+ m_bWarnOnly = !m_bWarnOnly;
|
|
|
+ ApplyWarnSwitch();
|
|
|
+ BuildStateRows();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChaxunView::OnItemSelect(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (name == L"kc_cx_combo_cat")
|
|
|
+ {
|
|
|
+ if (m_nSeg == 0) BuildStateRows();
|
|
|
+ else BuildAmtRows();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChaxunView::OnTextChanged(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (name != L"kc_cx_search_edit") return;
|
|
|
+ SyncSearchTishi(L"kc_cx_search_tishi", L"kc_cx_search_edit");
|
|
|
+ if (m_nSeg == 0) BuildStateRows();
|
|
|
+ else BuildAmtRows();
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 视图2:入库 ============================
|
|
|
+// 记录态工具条:[全部类型 下拉] ...... [新建入库单]
|
|
|
+// 统计态工具条:[本月 时段] [搜索] [全部分类] ...... [新建入库单]
|
|
|
+// 两态共用同一行 —— HorizontalLayout 会跳过不可见子项,所以只用一份控件 + 一个按钮。
|
|
|
+void CKucunRukuView::OnInitControl()
|
|
|
+{
|
|
|
+ CComboUI* pRange = FindC<CComboUI>(L"kc_rk_combo_range");
|
|
|
+ if (pRange)
|
|
|
+ {
|
|
|
+ AddComboItem(pRange, L"本月"); AddComboItem(pRange, L"上月"); AddComboItem(pRange, L"最近三个月");
|
|
|
+ SelectCombo(pRange, 0);
|
|
|
+ }
|
|
|
+ CComboUI* pType = FindC<CComboUI>(L"kc_rk_combo_type");
|
|
|
+ if (pType)
|
|
|
+ {
|
|
|
+ AddComboItem(pType, L"全部类型"); AddComboItem(pType, L"采购入库");
|
|
|
+ AddComboItem(pType, L"调拨入库"); AddComboItem(pType, L"退货入库");
|
|
|
+ SelectCombo(pType, 0);
|
|
|
+ }
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_rk_combo_cat");
|
|
|
+ if (pCat)
|
|
|
+ {
|
|
|
+ AddComboItem(pCat, L"全部分类");
|
|
|
+ for (int i = 0; i < 6; i++) AddComboItem(pCat, KC_CATS[i]);
|
|
|
+ SelectCombo(pCat, 0);
|
|
|
+ }
|
|
|
+ SetSeg(0, false);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void CKucunRukuView::InitShow()
|
|
|
+{
|
|
|
+ int nNum = g_rk_n, nGoods = 0, nReturn = 0;
|
|
|
+ double dAmount = 0.0;
|
|
|
+ for (int i = 0; i < g_rk_n; i++)
|
|
|
+ {
|
|
|
+ dAmount += g_rk[i].amount;
|
|
|
+ nGoods += g_rk[i].goods;
|
|
|
+ if (g_rk[i].type == L"退货入库") nReturn++;
|
|
|
+ }
|
|
|
+ SetStatInt(L"kc_rk_stat_num", nNum, L"单");
|
|
|
+ SetStatMoney(L"kc_rk_stat_amount", dAmount, L"元");
|
|
|
+ SetStatInt(L"kc_rk_stat_goods", nGoods, L"项");
|
|
|
+ SetStatInt(L"kc_rk_stat_return", nReturn, L"单");
|
|
|
+
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(L"kc_rk_search_edit");
|
|
|
+ if (pEdit) pEdit->SetText(L"");
|
|
|
+ m_strSearch.clear();
|
|
|
+ SyncSearchTishi(L"kc_rk_search_tishi", L"kc_rk_search_edit");
|
|
|
+ SetSeg(m_nSeg, true);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRukuView::SetSeg(int active, bool bBuild)
|
|
|
+{
|
|
|
+ m_nSeg = active;
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_rk_seg_record"), active == 0);
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_rk_seg_stat"), active == 1);
|
|
|
+
|
|
|
+ // 时段筛选与搜索、分类下拉只在统计态出现(设计稿:记录列表不做搜索框、不做时段筛选)
|
|
|
+ CControlUI* pRange = Find(L"kc_rk_combo_range");
|
|
|
+ if (pRange) pRange->SetVisible(active == 1);
|
|
|
+ CControlUI* pSearch = Find(L"kc_rk_search_box");
|
|
|
+ if (pSearch) pSearch->SetVisible(active == 1);
|
|
|
+ CControlUI* pCat = Find(L"kc_rk_combo_cat");
|
|
|
+ if (pCat) pCat->SetVisible(active == 1);
|
|
|
+ CControlUI* pType = Find(L"kc_rk_combo_type");
|
|
|
+ if (pType) pType->SetVisible(active == 0);
|
|
|
+
|
|
|
+ CControlUI* pBodyRec = Find(L"kc_rk_body_record");
|
|
|
+ CControlUI* pBodyStat = Find(L"kc_rk_body_stat");
|
|
|
+ if (pBodyRec) pBodyRec->SetVisible(active == 0);
|
|
|
+ if (pBodyStat) pBodyStat->SetVisible(active == 1);
|
|
|
+
|
|
|
+ KcSyncNativeEditsIn(pSearch); // 含原生 Edit:显隐后必须手工催一次 SetPos
|
|
|
+
|
|
|
+ if (bBuild) { if (active == 0) BuildRecordRows(); else BuildStatRows(); }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRukuView::BuildRecordRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_rk_list");
|
|
|
+ if (pList == NULL) return;
|
|
|
+ std::wstring t;
|
|
|
+ CComboUI* pType = FindC<CComboUI>(L"kc_rk_combo_type");
|
|
|
+ if (pType) t = (LPCTSTR)pType->GetText();
|
|
|
+ bool all = (t.empty() || t == L"全部类型");
|
|
|
+
|
|
|
+ ClearList(pList);
|
|
|
+ int nMatched = 0, nAdded = 0;
|
|
|
+ for (int i = 0; i < g_rk_n; i++)
|
|
|
+ {
|
|
|
+ if (!all && g_rk[i].type != t) continue;
|
|
|
+ nMatched++;
|
|
|
+ if (nAdded >= KC_PAGE_SIZE) continue;
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_rk.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ SetRowText(row, L"col_no", g_rk[i].no.c_str());
|
|
|
+ SetTag(row, L"tag_type", g_rk[i].type.c_str(),
|
|
|
+ g_rk[i].type == L"采购入库" ? KC_TAG_GRN
|
|
|
+ : (g_rk[i].type == L"调拨入库" ? KC_TAG_BLUE : KC_TAG_ORG));
|
|
|
+ SetRowText(row, L"col_goods", std::to_wstring(g_rk[i].goods).c_str());
|
|
|
+ SetRowText(row, L"col_amount", KcFormatMoney(g_rk[i].amount).c_str());
|
|
|
+ SetRowText(row, L"col_handler", g_rk[i].handler.c_str());
|
|
|
+ SetTag(row, L"tag_status", g_rk[i].status == 0 ? L"已入库" : L"待入库",
|
|
|
+ g_rk[i].status == 0 ? KC_TAG_GRN : KC_TAG_ORG);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_rk_list_count", nAdded);
|
|
|
+ SetFootRecord(L"kc_rk_list_foot", nAdded, nMatched);
|
|
|
+ FitListHeight(L"kc_rk_list", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRukuView::BuildStatRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_rk_list_stat");
|
|
|
+ if (pList == NULL) return;
|
|
|
+
|
|
|
+ std::wstring cat;
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_rk_combo_cat");
|
|
|
+ if (pCat) cat = (LPCTSTR)pCat->GetText();
|
|
|
+ ReadSearch(L"kc_rk_search_edit", m_strSearch);
|
|
|
+
|
|
|
+ // 占比分母 = 未过滤的入库总金额(设计稿:占比 = 该商品金额 / 入库总金额)
|
|
|
+ double total = 0.0;
|
|
|
+ for (int i = 0; i < g_rk_stat_n; i++) total += g_rk_stat[i].amount;
|
|
|
+
|
|
|
+ ClearList(pList);
|
|
|
+ int nAdded = 0;
|
|
|
+ for (int i = 0; i < g_rk_stat_n; i++)
|
|
|
+ {
|
|
|
+ const KcRkStat& it = g_rk_stat[i];
|
|
|
+ if (!cat.empty() && cat != L"全部分类" && it.cat != cat) continue;
|
|
|
+ if (!m_strSearch.empty() && it.name.find(m_strSearch) == std::wstring::npos) continue;
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_rk_stat.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ SetRowText(row, L"col_name", it.name.c_str());
|
|
|
+ SetRowText(row, L"col_cat", it.cat.c_str());
|
|
|
+ SetRowText(row, L"col_unit", it.unit.c_str());
|
|
|
+ SetRowText(row, L"col_cnt", std::to_wstring(it.cnt).c_str());
|
|
|
+ // 入库数量列设计稿带单位("60 桶")
|
|
|
+ SetRowText(row, L"col_qty", (std::to_wstring(it.qty) + L" " + it.unit).c_str());
|
|
|
+ SetRowText(row, L"col_amount", KcFormatMoney(it.amount).c_str());
|
|
|
+
|
|
|
+ double pct = total > 0.0 ? it.amount * 100.0 / total : 0.0;
|
|
|
+ int fillw = (int)(120.0 * pct / 100.0 + 0.5);
|
|
|
+ if (fillw > 120) fillw = 120;
|
|
|
+ if (fillw < 0) fillw = 0;
|
|
|
+ SetRowWidth(row, L"bar_fill", fillw);
|
|
|
+ wchar_t buf[32];
|
|
|
+ swprintf_s(buf, _countof(buf), L"%.0f%%", pct);
|
|
|
+ SetRowText(row, L"bar_pct", buf);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_rk_stat_count", nAdded);
|
|
|
+ SetFootAll(L"kc_rk_stat_foot", nAdded);
|
|
|
+ FitListHeight(L"kc_rk_list_stat", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRukuView::OnClick(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (name == L"kc_rk_seg_record") SetSeg(0, true);
|
|
|
+ else if (name == L"kc_rk_seg_stat") SetSeg(1, true);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRukuView::OnItemSelect(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (m_nSeg == 0)
|
|
|
+ {
|
|
|
+ if (name == L"kc_rk_combo_type") BuildRecordRows();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // 时段下拉目前只切文案:占位数据没有日期字段,等接口联调后接真实筛选
|
|
|
+ if (name == L"kc_rk_combo_range") BuildStatRows();
|
|
|
+ else if (name == L"kc_rk_combo_cat") BuildStatRows();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRukuView::OnTextChanged(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ if (m_nSeg != 1) return;
|
|
|
+ if (msg.pSender->GetName() != CDuiString(L"kc_rk_search_edit")) return;
|
|
|
+ SyncSearchTishi(L"kc_rk_search_tishi", L"kc_rk_search_edit");
|
|
|
+ BuildStatRows();
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 视图3:出库 ============================
|
|
|
+void CKucunChukuView::OnInitControl()
|
|
|
+{
|
|
|
+ CComboUI* pRange = FindC<CComboUI>(L"kc_ck_combo_range");
|
|
|
+ if (pRange)
|
|
|
+ {
|
|
|
+ AddComboItem(pRange, L"本月"); AddComboItem(pRange, L"上月"); AddComboItem(pRange, L"最近三个月");
|
|
|
+ SelectCombo(pRange, 0);
|
|
|
+ }
|
|
|
+ CComboUI* pType = FindC<CComboUI>(L"kc_ck_combo_type");
|
|
|
+ if (pType)
|
|
|
+ {
|
|
|
+ AddComboItem(pType, L"全部类型"); AddComboItem(pType, L"调拨出库");
|
|
|
+ AddComboItem(pType, L"报损"); AddComboItem(pType, L"采购退货");
|
|
|
+ SelectCombo(pType, 0);
|
|
|
+ }
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_ck_combo_cat");
|
|
|
+ if (pCat)
|
|
|
+ {
|
|
|
+ AddComboItem(pCat, L"全部分类");
|
|
|
+ for (int i = 0; i < 6; i++) AddComboItem(pCat, KC_CATS[i]);
|
|
|
+ SelectCombo(pCat, 0);
|
|
|
+ }
|
|
|
+ SetSeg(0, false);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChukuView::InitShow()
|
|
|
+{
|
|
|
+ int nNum = g_ck_n, nGoods = 0;
|
|
|
+ double dAmount = 0.0, dLoss = 0.0;
|
|
|
+ for (int i = 0; i < g_ck_n; i++)
|
|
|
+ {
|
|
|
+ dAmount += g_ck[i].amount;
|
|
|
+ nGoods += g_ck[i].goods;
|
|
|
+ if (g_ck[i].type == L"报损") dLoss += g_ck[i].amount;
|
|
|
+ }
|
|
|
+ SetStatInt(L"kc_ck_stat_num", nNum, L"单");
|
|
|
+ SetStatMoney(L"kc_ck_stat_amount", dAmount, L"元");
|
|
|
+ SetStatMoney(L"kc_ck_stat_loss", dLoss, L"元");
|
|
|
+ SetStatInt(L"kc_ck_stat_goods", nGoods, L"项");
|
|
|
+
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(L"kc_ck_search_edit");
|
|
|
+ if (pEdit) pEdit->SetText(L"");
|
|
|
+ m_strSearch.clear();
|
|
|
+ SyncSearchTishi(L"kc_ck_search_tishi", L"kc_ck_search_edit");
|
|
|
+ SetSeg(m_nSeg, true);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChukuView::SetSeg(int active, bool bBuild)
|
|
|
+{
|
|
|
+ m_nSeg = active;
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_ck_seg_record"), active == 0);
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_ck_seg_stat"), active == 1);
|
|
|
+
|
|
|
+ CControlUI* pRange = Find(L"kc_ck_combo_range");
|
|
|
+ if (pRange) pRange->SetVisible(active == 1);
|
|
|
+ CControlUI* pSearch = Find(L"kc_ck_search_box");
|
|
|
+ if (pSearch) pSearch->SetVisible(active == 1);
|
|
|
+ CControlUI* pCat = Find(L"kc_ck_combo_cat");
|
|
|
+ if (pCat) pCat->SetVisible(active == 1);
|
|
|
+ CControlUI* pType = Find(L"kc_ck_combo_type");
|
|
|
+ if (pType) pType->SetVisible(active == 0);
|
|
|
+
|
|
|
+ CControlUI* pBodyRec = Find(L"kc_ck_body_record");
|
|
|
+ CControlUI* pBodyStat = Find(L"kc_ck_body_stat");
|
|
|
+ if (pBodyRec) pBodyRec->SetVisible(active == 0);
|
|
|
+ if (pBodyStat) pBodyStat->SetVisible(active == 1);
|
|
|
+
|
|
|
+ KcSyncNativeEditsIn(pSearch);
|
|
|
+
|
|
|
+ if (bBuild) { if (active == 0) BuildRecordRows(); else BuildStatRows(); }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChukuView::BuildRecordRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_ck_list");
|
|
|
+ if (pList == NULL) return;
|
|
|
+ std::wstring t;
|
|
|
+ CComboUI* pType = FindC<CComboUI>(L"kc_ck_combo_type");
|
|
|
+ if (pType) t = (LPCTSTR)pType->GetText();
|
|
|
+ bool all = (t.empty() || t == L"全部类型");
|
|
|
+
|
|
|
+ ClearList(pList);
|
|
|
+ int nMatched = 0, nAdded = 0;
|
|
|
+ for (int i = 0; i < g_ck_n; i++)
|
|
|
+ {
|
|
|
+ if (!all && g_ck[i].type != t) continue;
|
|
|
+ nMatched++;
|
|
|
+ if (nAdded >= KC_PAGE_SIZE) continue;
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_ck.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ SetRowText(row, L"col_no", g_ck[i].no.c_str());
|
|
|
+ SetTag(row, L"tag_type", g_ck[i].type.c_str(),
|
|
|
+ g_ck[i].type == L"调拨出库" ? KC_TAG_BLUE
|
|
|
+ : (g_ck[i].type == L"报损" ? KC_TAG_RED : KC_TAG_ORG));
|
|
|
+ SetRowText(row, L"col_goods", std::to_wstring(g_ck[i].goods).c_str());
|
|
|
+ SetRowText(row, L"col_amount", KcFormatMoney(g_ck[i].amount).c_str());
|
|
|
+ SetRowText(row, L"col_handler", g_ck[i].handler.c_str());
|
|
|
+ SetTag(row, L"tag_status", g_ck[i].status == 0 ? L"已出库" : L"待出库",
|
|
|
+ g_ck[i].status == 0 ? KC_TAG_GRN : KC_TAG_ORG);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_ck_list_count", nAdded);
|
|
|
+ SetFootRecord(L"kc_ck_list_foot", nAdded, nMatched);
|
|
|
+ FitListHeight(L"kc_ck_list", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChukuView::BuildStatRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_ck_list_stat");
|
|
|
+ if (pList == NULL) return;
|
|
|
+
|
|
|
+ std::wstring cat;
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_ck_combo_cat");
|
|
|
+ if (pCat) cat = (LPCTSTR)pCat->GetText();
|
|
|
+ ReadSearch(L"kc_ck_search_edit", m_strSearch);
|
|
|
+
|
|
|
+ double total = 0.0;
|
|
|
+ for (int i = 0; i < g_ck_stat_n; i++) total += g_ck_stat[i].amount;
|
|
|
+
|
|
|
+ ClearList(pList);
|
|
|
+ int nAdded = 0;
|
|
|
+ for (int i = 0; i < g_ck_stat_n; i++)
|
|
|
+ {
|
|
|
+ const KcRkStat& it = g_ck_stat[i];
|
|
|
+ if (!cat.empty() && cat != L"全部分类" && it.cat != cat) continue;
|
|
|
+ if (!m_strSearch.empty() && it.name.find(m_strSearch) == std::wstring::npos) continue;
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_ck_stat.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ SetRowText(row, L"col_name", it.name.c_str());
|
|
|
+ SetRowText(row, L"col_cat", it.cat.c_str());
|
|
|
+ SetRowText(row, L"col_unit", it.unit.c_str());
|
|
|
+ SetRowText(row, L"col_cnt", std::to_wstring(it.cnt).c_str());
|
|
|
+ SetRowText(row, L"col_qty", (std::to_wstring(it.qty) + L" " + it.unit).c_str());
|
|
|
+ SetRowText(row, L"col_amount", KcFormatMoney(it.amount).c_str());
|
|
|
+
|
|
|
+ double pct = total > 0.0 ? it.amount * 100.0 / total : 0.0;
|
|
|
+ int fillw = (int)(120.0 * pct / 100.0 + 0.5);
|
|
|
+ if (fillw > 120) fillw = 120;
|
|
|
+ if (fillw < 0) fillw = 0;
|
|
|
+ SetRowWidth(row, L"bar_fill", fillw);
|
|
|
+ wchar_t buf[32];
|
|
|
+ swprintf_s(buf, _countof(buf), L"%.0f%%", pct);
|
|
|
+ SetRowText(row, L"bar_pct", buf);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_ck_stat_count", nAdded);
|
|
|
+ SetFootAll(L"kc_ck_stat_foot", nAdded);
|
|
|
+ FitListHeight(L"kc_ck_list_stat", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChukuView::OnClick(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (name == L"kc_ck_seg_record") SetSeg(0, true);
|
|
|
+ else if (name == L"kc_ck_seg_stat") SetSeg(1, true);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChukuView::OnItemSelect(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (m_nSeg == 0)
|
|
|
+ {
|
|
|
+ if (name == L"kc_ck_combo_type") BuildRecordRows();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (name == L"kc_ck_combo_range") BuildStatRows();
|
|
|
+ else if (name == L"kc_ck_combo_cat") BuildStatRows();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunChukuView::OnTextChanged(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ if (m_nSeg != 1) return;
|
|
|
+ if (msg.pSender->GetName() != CDuiString(L"kc_ck_search_edit")) return;
|
|
|
+ SyncSearchTishi(L"kc_ck_search_tishi", L"kc_ck_search_edit");
|
|
|
+ BuildStatRows();
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 视图4:库存盘点 ============================
|
|
|
+void CKucunPandianView::OnInitControl()
|
|
|
+{
|
|
|
+ CComboUI* pRange = FindC<CComboUI>(L"kc_pd_combo_range");
|
|
|
+ if (pRange)
|
|
|
+ {
|
|
|
+ AddComboItem(pRange, L"本月"); AddComboItem(pRange, L"上月"); AddComboItem(pRange, L"最近三个月");
|
|
|
+ SelectCombo(pRange, 0);
|
|
|
+ }
|
|
|
+ CComboUI* pResult = FindC<CComboUI>(L"kc_pd_combo_result");
|
|
|
+ if (pResult)
|
|
|
+ {
|
|
|
+ AddComboItem(pResult, L"全部结果"); AddComboItem(pResult, L"盘盈");
|
|
|
+ AddComboItem(pResult, L"盘亏"); AddComboItem(pResult, L"无差异");
|
|
|
+ SelectCombo(pResult, 0);
|
|
|
+ }
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_pd_combo_cat");
|
|
|
+ if (pCat)
|
|
|
+ {
|
|
|
+ AddComboItem(pCat, L"全部分类");
|
|
|
+ for (int i = 0; i < 6; i++) AddComboItem(pCat, KC_CATS[i]);
|
|
|
+ SelectCombo(pCat, 0);
|
|
|
+ }
|
|
|
+ SetSeg(0, false);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPandianView::InitShow()
|
|
|
+{
|
|
|
+ int nNum = g_pd_n, nGain = 0, nLoss = 0;
|
|
|
+ double dAmount = 0.0;
|
|
|
+ for (int i = 0; i < g_pd_n; i++)
|
|
|
+ {
|
|
|
+ nGain += g_pd[i].gain;
|
|
|
+ nLoss += g_pd[i].loss;
|
|
|
+ dAmount += g_pd[i].amount;
|
|
|
+ }
|
|
|
+ SetStatInt(L"kc_pd_stat_num", nNum, L"次");
|
|
|
+ SetStatInt(L"kc_pd_stat_gain", nGain, L"项");
|
|
|
+ SetStatInt(L"kc_pd_stat_loss", nLoss, L"项");
|
|
|
+ SetStatMoney(L"kc_pd_stat_amount", dAmount, L"元");
|
|
|
+
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(L"kc_pd_search_edit");
|
|
|
+ if (pEdit) pEdit->SetText(L"");
|
|
|
+ m_strSearch.clear();
|
|
|
+ SyncSearchTishi(L"kc_pd_search_tishi", L"kc_pd_search_edit");
|
|
|
+ SetSeg(m_nSeg, true);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPandianView::SetSeg(int active, bool bBuild)
|
|
|
+{
|
|
|
+ m_nSeg = active;
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_pd_seg_record"), active == 0);
|
|
|
+ StyleSeg(FindC<CButtonUI>(L"kc_pd_seg_stat"), active == 1);
|
|
|
+
|
|
|
+ CControlUI* pRange = Find(L"kc_pd_combo_range");
|
|
|
+ if (pRange) pRange->SetVisible(active == 1);
|
|
|
+ CControlUI* pSearch = Find(L"kc_pd_search_box");
|
|
|
+ if (pSearch) pSearch->SetVisible(active == 1);
|
|
|
+ CControlUI* pCat = Find(L"kc_pd_combo_cat");
|
|
|
+ if (pCat) pCat->SetVisible(active == 1);
|
|
|
+ CControlUI* pResult = Find(L"kc_pd_combo_result");
|
|
|
+ if (pResult) pResult->SetVisible(active == 0);
|
|
|
+
|
|
|
+ CControlUI* pBodyRec = Find(L"kc_pd_body_record");
|
|
|
+ CControlUI* pBodyStat = Find(L"kc_pd_body_stat");
|
|
|
+ if (pBodyRec) pBodyRec->SetVisible(active == 0);
|
|
|
+ if (pBodyStat) pBodyStat->SetVisible(active == 1);
|
|
|
+
|
|
|
+ KcSyncNativeEditsIn(pSearch);
|
|
|
+
|
|
|
+ if (bBuild) { if (active == 0) BuildRecordRows(); else BuildDiffRows(); }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPandianView::BuildRecordRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_pd_list");
|
|
|
+ if (pList == NULL) return;
|
|
|
+
|
|
|
+ // 下拉项顺序:全部结果 / 盘盈 / 盘亏 / 无差异 -> 结果码 -1 / 0 / 1 / 2
|
|
|
+ int sel = 0;
|
|
|
+ CComboUI* pResult = FindC<CComboUI>(L"kc_pd_combo_result");
|
|
|
+ if (pResult) sel = pResult->GetCurSel();
|
|
|
+ const int fmap[4] = { -1, 0, 1, 2 };
|
|
|
+ int filter = fmap[(sel >= 0 && sel < 4) ? sel : 0];
|
|
|
+
|
|
|
+ ClearList(pList);
|
|
|
+ int nMatched = 0, nAdded = 0;
|
|
|
+ for (int i = 0; i < g_pd_n; i++)
|
|
|
+ {
|
|
|
+ if (filter >= 0 && g_pd[i].result != filter) continue;
|
|
|
+ nMatched++;
|
|
|
+ if (nAdded >= KC_PAGE_SIZE) continue;
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_pd.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ SetRowText(row, L"col_no", g_pd[i].no.c_str());
|
|
|
+ SetRowText(row, L"col_scope", g_pd[i].scope.c_str());
|
|
|
+ SetRowText(row, L"col_goods", std::to_wstring(g_pd[i].goods).c_str());
|
|
|
+ SetRowText(row, L"col_gain", std::to_wstring(g_pd[i].gain).c_str());
|
|
|
+ SetRowText(row, L"col_loss", std::to_wstring(g_pd[i].loss).c_str());
|
|
|
+ SetRowText(row, L"col_amount", KcFormatMoney(g_pd[i].amount).c_str());
|
|
|
+ SetRowText(row, L"col_handler", g_pd[i].handler.c_str());
|
|
|
+
|
|
|
+ // 盘点结果:0 盘盈(蓝) / 1 盘亏(红) / 2 无差异(灰)
|
|
|
+ const wchar_t* rtxt; int rtag;
|
|
|
+ if (g_pd[i].result == 0) { rtxt = L"盘盈"; rtag = KC_TAG_BLUE; }
|
|
|
+ else if (g_pd[i].result == 1) { rtxt = L"盘亏"; rtag = KC_TAG_RED; }
|
|
|
+ else { rtxt = L"无差异"; rtag = KC_TAG_GRAY; }
|
|
|
+ SetTag(row, L"tag_result", rtxt, rtag);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_pd_list_count", nAdded);
|
|
|
+ SetFootRecord(L"kc_pd_list_foot", nAdded, nMatched);
|
|
|
+ FitListHeight(L"kc_pd_list", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPandianView::BuildDiffRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindC<CListUI>(L"kc_pd_list_stat");
|
|
|
+ if (pList == NULL) return;
|
|
|
+
|
|
|
+ std::wstring cat;
|
|
|
+ CComboUI* pCat = FindC<CComboUI>(L"kc_pd_combo_cat");
|
|
|
+ if (pCat) cat = (LPCTSTR)pCat->GetText();
|
|
|
+ ReadSearch(L"kc_pd_search_edit", m_strSearch);
|
|
|
+
|
|
|
+ ClearList(pList);
|
|
|
+ int nAdded = 0;
|
|
|
+ for (int i = 0; i < g_pd_stat_n; i++)
|
|
|
+ {
|
|
|
+ const KcPdStat& it = g_pd_stat[i];
|
|
|
+ if (!cat.empty() && cat != L"全部分类" && it.cat != cat) continue;
|
|
|
+ if (!m_strSearch.empty() && it.name.find(m_strSearch) == std::wstring::npos) continue;
|
|
|
+
|
|
|
+ CListContainerElementUI* row = NewRow(L"kc_row_pd_stat.xml");
|
|
|
+ if (row == NULL) continue;
|
|
|
+ SetRowText(row, L"col_name", it.name.c_str());
|
|
|
+ SetRowText(row, L"col_cat", it.cat.c_str());
|
|
|
+ SetRowText(row, L"col_unit", it.unit.c_str());
|
|
|
+ SetRowText(row, L"col_cnt", std::to_wstring(it.cnt).c_str());
|
|
|
+ // 差异列带正负号(设计稿 t8 是 "+5" / "-4"),并按正负着色:盈绿亏红
|
|
|
+ wchar_t dbuf[16];
|
|
|
+ swprintf_s(dbuf, _countof(dbuf), L"%+d", it.diff);
|
|
|
+ SetRowText(row, L"col_diff", dbuf);
|
|
|
+ SetRowColor(row, L"col_diff", it.diff > 0 ? KC_COLOR_NORMAL
|
|
|
+ : (it.diff < 0 ? KC_COLOR_LACK : 0xFF1F2329));
|
|
|
+ // 盈亏金额同样带符号(设计稿 t8 是 "+65.00" / "-216.00")
|
|
|
+ std::wstring amt = KcFormatMoney(it.amount);
|
|
|
+ if (it.amount > 0.0) amt = L"+" + amt;
|
|
|
+ SetRowText(row, L"col_amount", amt.c_str());
|
|
|
+
|
|
|
+ const wchar_t* rtxt; int rtag;
|
|
|
+ if (it.diff > 0) { rtxt = L"盘盈"; rtag = KC_TAG_BLUE; }
|
|
|
+ else if (it.diff < 0) { rtxt = L"盘亏"; rtag = KC_TAG_RED; }
|
|
|
+ else { rtxt = L"无差异"; rtag = KC_TAG_GRAY; }
|
|
|
+ SetTag(row, L"tag_result", rtxt, rtag);
|
|
|
+
|
|
|
+ pList->Add(row);
|
|
|
+ nAdded++;
|
|
|
+ }
|
|
|
+ SetListCount(L"kc_pd_stat_count", nAdded);
|
|
|
+ SetFootAll(L"kc_pd_stat_foot", nAdded);
|
|
|
+ FitListHeight(L"kc_pd_list_stat", nAdded);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPandianView::OnClick(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (name == L"kc_pd_seg_record") SetSeg(0, true);
|
|
|
+ else if (name == L"kc_pd_seg_stat") SetSeg(1, true);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPandianView::OnItemSelect(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ CDuiString name = msg.pSender->GetName();
|
|
|
+ if (m_nSeg == 0)
|
|
|
+ {
|
|
|
+ if (name == L"kc_pd_combo_result") BuildRecordRows();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (name == L"kc_pd_combo_range") BuildDiffRows();
|
|
|
+ else if (name == L"kc_pd_combo_cat") BuildDiffRows();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPandianView::OnTextChanged(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ if (m_nSeg != 1) return;
|
|
|
+ if (msg.pSender->GetName() != CDuiString(L"kc_pd_search_edit")) return;
|
|
|
+ SyncSearchTishi(L"kc_pd_search_tishi", L"kc_pd_search_edit");
|
|
|
+ BuildDiffRows();
|
|
|
+}
|
|
|
+
|
|
|
+// ================================================================================
|
|
|
+// 编辑视图基类:新建入库单 / 新建出库单 / 新建盘点单(设计稿 v4 p1~p4 / p6~p8)
|
|
|
+//
|
|
|
+// 三张页在 kucun_page.xml 里是与 kc_view_* 同级的全屏子视图,靠 SetVisible 切换;
|
|
|
+// 明细行落在一个 List 里,可编辑单元格是 Button(点它把页面级键盘面板叫出来)。
|
|
|
+// 键盘面板与本视图不在同一棵子树下(它在页面根容器下),所以 Find 要穿透两个 host。
|
|
|
+// ================================================================================
|
|
|
+
|
|
|
+// 单元格底图三态:默认 input_r8_*;编辑中 btn_r8_green_*;告警(出库超库存)btn_r8_red_*
|
|
|
+static const wchar_t* KC_CELL_N = L"file='input_r8_n.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_H = L"file='input_r8_h.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_P = L"file='input_r8_n.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_EN = L"file='btn_r8_green_n.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_EH = L"file='btn_r8_green_h.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_EP = L"file='btn_r8_green_p.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_WN = L"file='btn_r8_red_n.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_WH = L"file='btn_r8_red_h.png' corner='8,8,8,8'";
|
|
|
+static const wchar_t* KC_CELL_WP = L"file='btn_r8_red_p.png' corner='8,8,8,8'";
|
|
|
+
|
|
|
+CControlUI* CKucunEditViewBase::Find(LPCTSTR name)
|
|
|
+{
|
|
|
+ CControlUI* p = CKucunViewBase::Find(name);
|
|
|
+ if (p == NULL && m_pKpHost != NULL) p = m_pKpHost->FindSubControl(name);
|
|
|
+ // 【R49】底栏 kc_new_foot_* 与键盘面板 kc_keypad 是**兄弟**(同挂页面级容器),
|
|
|
+ // 既不在本视图子树里、也不在 kc_keypad 里 ⇒ 再往上一层到共同父级找一次。
|
|
|
+ if (p == NULL && m_pKpHost != NULL)
|
|
|
+ {
|
|
|
+ CContainerUI* pPar = static_cast<CContainerUI*>(m_pKpHost->GetParent());
|
|
|
+ if (pPar != NULL) p = pPar->FindSubControl(name);
|
|
|
+ }
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+std::wstring CKucunEditViewBase::PrefixW() const
|
|
|
+{
|
|
|
+ std::wstring s = Prefix();
|
|
|
+ return s;
|
|
|
+}
|
|
|
+
|
|
|
+std::wstring CKucunEditViewBase::CellName(LPCWSTR kind, int idx) const
|
|
|
+{
|
|
|
+ std::wstring s = Prefix();
|
|
|
+ s += L"_";
|
|
|
+ s += kind;
|
|
|
+ s += L"_";
|
|
|
+ s += std::to_wstring(idx);
|
|
|
+ return s;
|
|
|
+}
|
|
|
+
|
|
|
+CListUI* CKucunEditViewBase::FindList()
|
|
|
+{
|
|
|
+ return FindC<CListUI>(ListName());
|
|
|
+}
|
|
|
+
|
|
|
+CListContainerElementUI* CKucunEditViewBase::RowAt(int i)
|
|
|
+{
|
|
|
+ CListUI* pList = FindList();
|
|
|
+ if (pList == NULL) return NULL;
|
|
|
+ if (i < 0 || i >= (int)pList->GetCount()) return NULL;
|
|
|
+ return static_cast<CListContainerElementUI*>(pList->GetItemAt(i));
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::SetCellText(int i, LPCWSTR kind, LPCTSTR text)
|
|
|
+{
|
|
|
+ CButtonUI* p = FindC<CButtonUI>(CellName(kind, i).c_str());
|
|
|
+ if (p != NULL) p->SetText(text);
|
|
|
+}
|
|
|
+
|
|
|
+// 行内只读列(col_book / col_diff / col_amount / col_note):先按行号取到行控件,
|
|
|
+// 再按列名找 Label。行控件必须从 List 现取 —— 名字里没有行号,不能走 Find。
|
|
|
+void CKucunEditViewBase::SetRowLabel(int i, LPCTSTR name, LPCTSTR text, DWORD color)
|
|
|
+{
|
|
|
+ CListContainerElementUI* row = RowAt(i);
|
|
|
+ if (row == NULL) return;
|
|
|
+ CLabelUI* p = static_cast<CLabelUI*>(row->FindSubControl(name));
|
|
|
+ if (p == NULL) return;
|
|
|
+ p->SetText(text);
|
|
|
+ if (color != 0) p->SetTextColor(color);
|
|
|
+}
|
|
|
+
|
|
|
+// 【R48】行内「备注格换成红字提示」:设计稿 p2 第 3 行超库存时,备注格不是输入框而是
|
|
|
+// 一行 12px 红字(<span class="hint">超出可用库存 8 斤</span>)。两个控件互斥显隐。
|
|
|
+// text 传 NULL / 空串 = 恢复输入框。
|
|
|
+void CKucunEditViewBase::SetRowNoteHint(int i, LPCTSTR text)
|
|
|
+{
|
|
|
+ CListContainerElementUI* row = RowAt(i);
|
|
|
+ if (row == NULL) return;
|
|
|
+ bool show = (text != NULL && text[0] != _T('\0'));
|
|
|
+ CLabelUI* pHint = static_cast<CLabelUI*>(row->FindSubControl(L"col_note_hint"));
|
|
|
+ if (pHint != NULL)
|
|
|
+ {
|
|
|
+ if (show) pHint->SetText(text);
|
|
|
+ pHint->SetVisible(show);
|
|
|
+ }
|
|
|
+ CButtonUI* pIn = FindC<CButtonUI>(CellName(L"note", i).c_str());
|
|
|
+ if (pIn != NULL) pIn->SetVisible(!show);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::SetLabel(LPCTSTR name, LPCTSTR text, DWORD color)
|
|
|
+{
|
|
|
+ CLabelUI* p = FindC<CLabelUI>(name);
|
|
|
+ if (p == NULL) return;
|
|
|
+ p->SetText(text);
|
|
|
+ if (color != 0) p->SetTextColor(color);
|
|
|
+ // 【R48】合计条改成「静态标签 + 动态值标签」之后,值文本的宽度会变("0" → "145"、"0 项" → "12 项")。
|
|
|
+ // autocalcwidth 只在 EstimateSize 被调用时才算宽度,而 EstimateSize 只在重排时跑;
|
|
|
+ // CLabelUI::SetText 既不置 m_bNeedEstimateSize、也不会触发重排。所以两件事都得补:
|
|
|
+ // ① SetFont 踢一脚 —— CLabelUI::SetFont 是唯一**无条件**置 m_bNeedEstimateSize 的
|
|
|
+ // (UILabel.cpp::SetFont,无早退判断),传当前字体号即可;
|
|
|
+ // ② NeedParentUpdate 让父布局重排一次,否则新文字仍按旧宽度摆放、会被裁掉。
|
|
|
+ p->SetFont(p->GetFont());
|
|
|
+ p->NeedParentUpdate();
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------- 构建 / 刷新
|
|
|
+void CKucunEditViewBase::InitShow()
|
|
|
+{
|
|
|
+ CloseKeypad();
|
|
|
+ m_lines.clear();
|
|
|
+ m_headNote.clear();
|
|
|
+ SeedLines();
|
|
|
+
|
|
|
+ // 单据头的「备注」字段(Button 当单元格用)
|
|
|
+ // 【R48】占位文案按页取(设计稿 p1「采购到货,含配送费」/ p2「晚市备料」/ p3「月末盘点」)
|
|
|
+ std::wstring hn = PrefixW() + L"_note";
|
|
|
+ CButtonUI* pNote = FindC<CButtonUI>(hn.c_str());
|
|
|
+ if (pNote != NULL)
|
|
|
+ {
|
|
|
+ pNote->SetText(HeadNotePh());
|
|
|
+ pNote->SetTextColor(0xFFA8ADB3);
|
|
|
+ }
|
|
|
+
|
|
|
+ RefreshAll();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::BuildRows()
|
|
|
+{
|
|
|
+ CListUI* pList = FindList();
|
|
|
+ if (pList == NULL) return;
|
|
|
+ ClearList(pList);
|
|
|
+
|
|
|
+ for (int i = 0; i < (int)m_lines.size(); i++)
|
|
|
+ {
|
|
|
+ CListContainerElementUI* row = NewRow(RowXml());
|
|
|
+ if (row == NULL) continue;
|
|
|
+
|
|
|
+ // 模板里的固定控件名 -> 带行号的唯一名,宿主按名反解 (kind, 行号)
|
|
|
+ CControlUI* p = NULL;
|
|
|
+ p = row->FindSubControl(L"cell_qty");
|
|
|
+ if (p != NULL) p->SetName(CellName(L"qty", i).c_str());
|
|
|
+ p = row->FindSubControl(L"cell_price");
|
|
|
+ if (p != NULL) p->SetName(CellName(L"price", i).c_str());
|
|
|
+ p = row->FindSubControl(L"cell_note");
|
|
|
+ if (p != NULL) p->SetName(CellName(L"note", i).c_str());
|
|
|
+ p = row->FindSubControl(L"btn_del");
|
|
|
+ if (p != NULL) p->SetName(CellName(L"del", i).c_str());
|
|
|
+
|
|
|
+ SetRowText(row, L"col_name", m_lines[i].name.c_str());
|
|
|
+ SetRowText(row, L"col_cat", m_lines[i].cat.c_str());
|
|
|
+ SetRowText(row, L"col_unit", m_lines[i].unit.c_str());
|
|
|
+
|
|
|
+ FillReadonly(row, i); // 只读列:当前库存 / 账面库存 / 备注占位
|
|
|
+ pList->Add(row);
|
|
|
+ RefreshRow(i); // 可编辑列 + 计算列 + 着色
|
|
|
+ }
|
|
|
+ SyncTableHeight();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::RefreshAll()
|
|
|
+{
|
|
|
+ BuildRows();
|
|
|
+ RefreshTot();
|
|
|
+}
|
|
|
+
|
|
|
+// 明细表高度:List = 上下 inset(1+10) + 表头 44 + n * 行高 56;外层卡片 = List 高度。
|
|
|
+// 【R48】卡片换成 kc_tblcard.png("1px 外框 + 43px 表头灰带 + 表头下 1px 分隔线" 一张图烤死),
|
|
|
+// 卡片自己**不再**有上下 inset(原来是 bb_card10 + inset 20,12,20,12 ⇒ CARD_INSET_Y = 24),
|
|
|
+// 表头灰带与底部圆角带都由这张图的 inset="1,1,1,10" 让开 ⇒ 卡片高恒等于 List 高。
|
|
|
+// 【R48】也不再沿父链写死祖先高度:这三个页的滚动主体 kc_*_body 是弹性项、自己管滚动
|
|
|
+// (见 KcPropagateFixedHeightUp 的新停靠条件)。卡片自身必须写死,否则在 body 里会被当弹性项压扁。
|
|
|
+void CKucunEditViewBase::SyncTableHeight()
|
|
|
+{
|
|
|
+ CListUI* pList = FindList();
|
|
|
+ if (pList == NULL) return;
|
|
|
+ const int HEADER_H = 44;
|
|
|
+ const int ROW_H = 56;
|
|
|
+ int rows = (int)m_lines.size();
|
|
|
+ if (rows < 1) rows = 1;
|
|
|
+ RECT rcLi = pList->GetInset();
|
|
|
+ int lh = rcLi.top + rcLi.bottom + HEADER_H + rows * ROW_H;
|
|
|
+ pList->SetFixedHeight(lh);
|
|
|
+ CControlUI* pCard = Find(TblCardName());
|
|
|
+ if (pCard != NULL) pCard->SetFixedHeight(lh);
|
|
|
+
|
|
|
+ // 【R48】行底边线由行模板的 bordersize="0,0,0,1" 自己提供(设计稿 .tr{border-bottom:1px
|
|
|
+ // solid var(--line)},实测每条明细行底边确有一条 #E8EAED)。但**最后一行不能有**:
|
|
|
+ // 它离表格卡的下边框只隔 10px 圆角带,两条线会并排出现
|
|
|
+ // (设计稿 .tr:last-child{border-bottom:none})。做法与记录表的 FitListHeight 一致。
|
|
|
+ int nCount = pList->GetCount();
|
|
|
+ if (nCount > 0)
|
|
|
+ {
|
|
|
+ CControlUI* pLast = pList->GetItemAt(nCount - 1);
|
|
|
+ if (pLast != NULL)
|
|
|
+ {
|
|
|
+ RECT rcNoBorder = { 0, 0, 0, 0 };
|
|
|
+ pLast->SetBorderSize(rcNoBorder);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::ApplyCellStyle(int i, int col)
|
|
|
+{
|
|
|
+ LPCWSTR kind = (col == KC_ECOL_QTY) ? L"qty" : ((col == KC_ECOL_PRICE) ? L"price" : L"note");
|
|
|
+ CButtonUI* p = FindC<CButtonUI>(CellName(kind, i).c_str());
|
|
|
+ if (p == NULL) return;
|
|
|
+
|
|
|
+ bool bEditing = (m_nEditRow == i && m_nEditCol == col);
|
|
|
+ bool bWarn = (col == KC_ECOL_QTY) && IsQtyWarn(i);
|
|
|
+ bool bPh = (!bEditing) && IsCellPlaceholder(i, col);
|
|
|
+
|
|
|
+ const wchar_t* n = KC_CELL_N; const wchar_t* h = KC_CELL_H; const wchar_t* pu = KC_CELL_P;
|
|
|
+ DWORD tc = 0xFF1F2329;
|
|
|
+ if (bEditing) { n = KC_CELL_EN; h = KC_CELL_EH; pu = KC_CELL_EP; tc = 0xFF2E9B5C; }
|
|
|
+ else if (bWarn) { n = KC_CELL_WN; h = KC_CELL_WH; pu = KC_CELL_WP; tc = 0xFFE14B4A; }
|
|
|
+ else if (bPh) { tc = 0xFFA8ADB3; }
|
|
|
+
|
|
|
+ p->SetNormalImage(n);
|
|
|
+ p->SetHotImage(h);
|
|
|
+ p->SetPushedImage(pu);
|
|
|
+ p->SetTextColor(tc);
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------- 键盘面板
|
|
|
+void CKucunEditViewBase::OpenKeypad(int row, int col)
|
|
|
+{
|
|
|
+ m_nEditRow = row;
|
|
|
+ m_nEditCol = col;
|
|
|
+
|
|
|
+ if (row < 0) m_editBuf = m_headNote;
|
|
|
+ else if (col == KC_ECOL_QTY) m_editBuf = m_lines[row].qty;
|
|
|
+ else if (col == KC_ECOL_PRICE) m_editBuf = m_lines[row].price;
|
|
|
+ else m_editBuf = m_lines[row].note;
|
|
|
+
|
|
|
+ KeypadShow(col == KC_ECOL_NOTE);
|
|
|
+ KeypadRefresh();
|
|
|
+
|
|
|
+ CControlUI* pKp = Find(L"kc_keypad");
|
|
|
+ if (pKp != NULL) pKp->SetVisible(true);
|
|
|
+
|
|
|
+ for (int i = 0; i < (int)m_lines.size(); i++)
|
|
|
+ {
|
|
|
+ ApplyCellStyle(i, KC_ECOL_QTY);
|
|
|
+ ApplyCellStyle(i, KC_ECOL_PRICE);
|
|
|
+ ApplyCellStyle(i, KC_ECOL_NOTE);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::KeypadShow(bool bText)
|
|
|
+{
|
|
|
+ CControlUI* pPad = Find(L"kc_kp_pad");
|
|
|
+ CControlUI* pTxt = Find(L"kc_kp_txt");
|
|
|
+ if (pPad != NULL) pPad->SetVisible(!bText);
|
|
|
+ if (pTxt != NULL) pTxt->SetVisible(bText);
|
|
|
+
|
|
|
+ // 【R49】设计稿 p6 底栏:数字键盘态换成键盘说明,文本态 / 收起时还原本页原文案。
|
|
|
+ SyncFootTip(!bText);
|
|
|
+
|
|
|
+ if (bText)
|
|
|
+ {
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(L"kc_kp_edit");
|
|
|
+ if (pEdit != NULL)
|
|
|
+ {
|
|
|
+ pEdit->SetText(m_editBuf.c_str());
|
|
|
+ pEdit->SetFocus();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::KeypadRefresh()
|
|
|
+{
|
|
|
+ std::wstring field;
|
|
|
+ std::wstring unit;
|
|
|
+
|
|
|
+ if (m_nEditRow < 0) field = L"备注";
|
|
|
+ else
|
|
|
+ {
|
|
|
+ field = m_lines[m_nEditRow].name;
|
|
|
+ field += L" · ";
|
|
|
+ // 【R49】数量列的字段名按页面给(入库数量 / 出库数量 / 实盘数量),见 QtyColLabel。
|
|
|
+ field += (m_nEditCol == KC_ECOL_QTY) ? QtyColLabel()
|
|
|
+ : ((m_nEditCol == KC_ECOL_PRICE) ? L"单价" : L"备注");
|
|
|
+ // 【R49】设计稿 p6 左栏:数值后跟一个 16px 灰的小号单位(「80 斤」)。
|
|
|
+ // 只有数量列有单位;单价列是「元」不显示(设计稿同)、备注列是文字。
|
|
|
+ if (m_nEditCol == KC_ECOL_QTY) unit = m_lines[m_nEditRow].unit;
|
|
|
+ }
|
|
|
+
|
|
|
+ SetLabel(L"kc_kp_field", field.c_str(), 0);
|
|
|
+ SetLabel(L"kc_kp_txtfield", field.c_str(), 0);
|
|
|
+ SetLabel(L"kc_kp_value", m_editBuf.empty() ? L"0" : m_editBuf.c_str(), 0);
|
|
|
+
|
|
|
+ std::wstring unitTxt;
|
|
|
+ if (!unit.empty())
|
|
|
+ {
|
|
|
+ unitTxt = L" "; // 与数值之间留一个空格(设计稿:80 斤)
|
|
|
+ unitTxt += unit;
|
|
|
+ }
|
|
|
+ SetLabel(L"kc_kp_unit", unitTxt.c_str(), 0);
|
|
|
+}
|
|
|
+
|
|
|
+// 【R49】键盘态底栏提示:原始文案从 XML 现场记一次,不在 C++ 里复制一份,
|
|
|
+// 免得以后改 XML 文案时两边对不上。
|
|
|
+CLabelUI* CKucunEditViewBase::FootTipLabel()
|
|
|
+{
|
|
|
+ std::wstring nm = PrefixW();
|
|
|
+ nm += L"_foot_tip";
|
|
|
+ return FindC<CLabelUI>(nm.c_str());
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::SyncFootTip(bool bKeypad)
|
|
|
+{
|
|
|
+ CLabelUI* p = FootTipLabel();
|
|
|
+ if (p == NULL) return;
|
|
|
+
|
|
|
+ if (m_footTipDefault.empty()) m_footTipDefault = (LPCTSTR)p->GetText();
|
|
|
+
|
|
|
+ p->SetText(bKeypad ? L"数字键盘仅用于数量 / 单价输入;确认后回到明细表"
|
|
|
+ : m_footTipDefault.c_str());
|
|
|
+ // autocalcwidth 的 Label 换文案后必须踢 SetFont 再让父级重排一次(同 SetLabel 的注释)
|
|
|
+ p->SetFont(p->GetFont());
|
|
|
+ p->NeedParentUpdate();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::KpDigit(LPCWSTR t)
|
|
|
+{
|
|
|
+ if (m_editBuf == L"0") m_editBuf.clear();
|
|
|
+ if (m_editBuf.size() + wcslen(t) > 12) return;
|
|
|
+ m_editBuf += t;
|
|
|
+ KeypadRefresh();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::KpDot()
|
|
|
+{
|
|
|
+ if (m_nEditCol != KC_ECOL_PRICE) return; // 只有单价允许小数
|
|
|
+ if (m_editBuf.find(L'.') != std::wstring::npos) return;
|
|
|
+ if (m_editBuf.empty()) m_editBuf = L"0";
|
|
|
+ m_editBuf += L".";
|
|
|
+ KeypadRefresh();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::KpDel()
|
|
|
+{
|
|
|
+ if (!m_editBuf.empty()) m_editBuf.erase(m_editBuf.size() - 1);
|
|
|
+ KeypadRefresh();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::KpClear()
|
|
|
+{
|
|
|
+ m_editBuf.clear();
|
|
|
+ KeypadRefresh();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::KpCommit()
|
|
|
+{
|
|
|
+ CEditUI* pEdit = FindC<CEditUI>(L"kc_kp_edit");
|
|
|
+
|
|
|
+ if (m_nEditRow < 0)
|
|
|
+ {
|
|
|
+ // 单据头备注
|
|
|
+ if (pEdit != NULL) m_headNote = (LPCTSTR)pEdit->GetText();
|
|
|
+ std::wstring hn = PrefixW() + L"_note";
|
|
|
+ CButtonUI* pNote = FindC<CButtonUI>(hn.c_str());
|
|
|
+ if (pNote != NULL)
|
|
|
+ {
|
|
|
+ if (m_headNote.empty())
|
|
|
+ {
|
|
|
+ pNote->SetText(HeadNotePh());
|
|
|
+ pNote->SetTextColor(0xFFA8ADB3);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ pNote->SetText(m_headNote.c_str());
|
|
|
+ pNote->SetTextColor(0xFF1F2329);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ CloseKeypad();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_nEditCol == KC_ECOL_NOTE)
|
|
|
+ {
|
|
|
+ if (pEdit != NULL) m_editBuf = (LPCTSTR)pEdit->GetText();
|
|
|
+ m_lines[m_nEditRow].note = m_editBuf;
|
|
|
+ SetCellText(m_nEditRow, L"note", m_editBuf.c_str());
|
|
|
+ CloseKeypad();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_nEditCol == KC_ECOL_QTY) m_lines[m_nEditRow].qty = m_editBuf;
|
|
|
+ else m_lines[m_nEditRow].price = m_editBuf;
|
|
|
+
|
|
|
+ LPCWSTR kind = (m_nEditCol == KC_ECOL_QTY) ? L"qty" : L"price";
|
|
|
+ SetCellText(m_nEditRow, kind, m_editBuf.c_str());
|
|
|
+
|
|
|
+ int row = m_nEditRow;
|
|
|
+ CloseKeypad(); // 内部会把所有单元格的样式复位
|
|
|
+ RefreshRow(row); // 复位之后再按最新数据着色(告警 / 占位)
|
|
|
+ RefreshTot();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::CloseKeypad()
|
|
|
+{
|
|
|
+ m_nEditRow = -1;
|
|
|
+ m_editBuf.clear();
|
|
|
+
|
|
|
+ CControlUI* pKp = Find(L"kc_keypad");
|
|
|
+ if (pKp != NULL) pKp->SetVisible(false);
|
|
|
+ // 键盘的文本面板里有原生 Edit:容器隐下去之后必须催一次 SetPos,
|
|
|
+ // 否则它会孤零零留在屏幕上(原因见 CKucunViews.h 里 KcSyncNativeEdit 的注释)。
|
|
|
+ if (pKp != NULL) KcSyncNativeEditsIn(pKp);
|
|
|
+
|
|
|
+ SyncFootTip(false); // 【R49】收起键盘 ⇔ 底栏提示还原
|
|
|
+
|
|
|
+ for (int i = 0; i < (int)m_lines.size(); i++)
|
|
|
+ {
|
|
|
+ ApplyCellStyle(i, KC_ECOL_QTY);
|
|
|
+ ApplyCellStyle(i, KC_ECOL_PRICE);
|
|
|
+ ApplyCellStyle(i, KC_ECOL_NOTE);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------- 事件
|
|
|
+void CKucunEditViewBase::OnClick(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ std::wstring n = (LPCTSTR)msg.pSender->GetName();
|
|
|
+
|
|
|
+ // ---- 键盘面板(与子视图同级,由 CKucunPageUI 转发过来) ----
|
|
|
+ if (n.size() >= 6 && n.compare(0, 6, L"kc_kp_") == 0)
|
|
|
+ {
|
|
|
+ if (n == L"kc_kp_del") KpDel();
|
|
|
+ else if (n == L"kc_kp_clr") KpClear();
|
|
|
+ else if (n == L"kc_kp_ok") KpCommit();
|
|
|
+ else if (n == L"kc_kp_txtcancel") CloseKeypad(); // 「关闭」键已按设计稿删除(第 4 列只剩 删除 / 清除 / 确认)
|
|
|
+ else if (n == L"kc_kp_txtok") KpCommit();
|
|
|
+ else if (n == L"kc_kp_kdot") KpDot();
|
|
|
+ else if (n.compare(0, 7, L"kc_kp_k") == 0) KpDigit(n.substr(7).c_str());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ---- 本视图的单元格 / 行内按钮 ----
|
|
|
+ std::wstring pfx = PrefixW();
|
|
|
+ if (n.size() <= pfx.size() || n.compare(0, pfx.size(), pfx) != 0) return;
|
|
|
+ std::wstring tail = n.substr(pfx.size()); // 例:"_qty_3"
|
|
|
+
|
|
|
+ if (tail == L"_note") { OpenKeypad(-1, KC_ECOL_NOTE); return; } // 单据头备注
|
|
|
+ if (tail.size() < 4 || tail[0] != L'_') return;
|
|
|
+
|
|
|
+ std::wstring::size_type p = tail.rfind(L'_');
|
|
|
+ if (p == std::wstring::npos || p == 0) return;
|
|
|
+ std::wstring kind = tail.substr(1, p - 1);
|
|
|
+ int idx = _wtoi(tail.c_str() + p + 1);
|
|
|
+ if (idx < 0 || idx >= (int)m_lines.size()) return;
|
|
|
+
|
|
|
+ if (kind == L"qty") OpenKeypad(idx, KC_ECOL_QTY);
|
|
|
+ else if (kind == L"price") OpenKeypad(idx, KC_ECOL_PRICE);
|
|
|
+ else if (kind == L"note") OpenKeypad(idx, KC_ECOL_NOTE);
|
|
|
+ else if (kind == L"del")
|
|
|
+ {
|
|
|
+ CloseKeypad();
|
|
|
+ m_lines.erase(m_lines.begin() + idx);
|
|
|
+ RefreshAll();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunEditViewBase::OnItemSelect(TNotifyUI& msg)
|
|
|
+{
|
|
|
+ // 单据头下拉:切换类型 / 范围只改显示,不改明细(占位实现)
|
|
|
+ (void)msg;
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------- 与「添加商品」弹层对接
|
|
|
+void CKucunEditViewBase::AppendPicked(const std::vector<std::wstring>& names)
|
|
|
+{
|
|
|
+ std::vector<KcEditLine> added;
|
|
|
+
|
|
|
+ for (size_t k = 0; k < names.size(); k++)
|
|
|
+ {
|
|
|
+ KcGoods g;
|
|
|
+ if (!KcLookupGoods(names[k], g)) continue;
|
|
|
+
|
|
|
+ if (!ReplaceOnPick())
|
|
|
+ {
|
|
|
+ bool dup = false;
|
|
|
+ for (size_t i = 0; i < m_lines.size(); i++)
|
|
|
+ {
|
|
|
+ if (m_lines[i].name == g.name) { dup = true; break; }
|
|
|
+ }
|
|
|
+ if (dup) continue; // 已在明细里的商品跳过
|
|
|
+ }
|
|
|
+
|
|
|
+ KcEditLine ln;
|
|
|
+ ln.name = g.name;
|
|
|
+ ln.cat = g.cat;
|
|
|
+ ln.unit = g.unit;
|
|
|
+ ln.book = g.stock;
|
|
|
+ OnLineAdded(ln);
|
|
|
+ added.push_back(ln);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ReplaceOnPick())
|
|
|
+ {
|
|
|
+ if (added.empty()) return; // 没勾任何商品 ⇒ 保留原范围
|
|
|
+ m_lines = added;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for (size_t i = 0; i < added.size(); i++) m_lines.push_back(added[i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ CloseKeypad();
|
|
|
+ RefreshAll();
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 子视图 A:新建入库单 ============================
|
|
|
+void CKucunRkNewView::OnInitControl()
|
|
|
+{
|
|
|
+ CComboUI* p = FindC<CComboUI>(L"kc_rkn_combo_type");
|
|
|
+ if (p != NULL)
|
|
|
+ {
|
|
|
+ AddComboItem(p, L"采购入库");
|
|
|
+ AddComboItem(p, L"调拨入库");
|
|
|
+ AddComboItem(p, L"退货入库");
|
|
|
+ SelectCombo(p, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRkNewView::SeedLines()
|
|
|
+{
|
|
|
+ // 设计稿 v4 p1:大米 80 x 3.20 / 食用油 25 x 86.00 / 生抽 40 x 15.00
|
|
|
+ static const wchar_t* nm[3] = { L"大米", L"食用油", L"生抽" };
|
|
|
+ static const wchar_t* qt[3] = { L"80", L"25", L"40" };
|
|
|
+ static const wchar_t* pr[3] = { L"3.20", L"86.00", L"15.00" };
|
|
|
+ for (int i = 0; i < 3; i++)
|
|
|
+ {
|
|
|
+ KcGoods g;
|
|
|
+ if (!KcLookupGoods(nm[i], g)) continue;
|
|
|
+ KcEditLine ln;
|
|
|
+ ln.name = g.name; ln.cat = g.cat; ln.unit = g.unit; ln.book = g.stock;
|
|
|
+ ln.qty = qt[i]; ln.price = pr[i];
|
|
|
+ m_lines.push_back(ln);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRkNewView::OnLineAdded(KcEditLine& line)
|
|
|
+{
|
|
|
+ line.qty = L"1";
|
|
|
+ KcGoods g;
|
|
|
+ if (KcLookupGoods(line.name, g)) line.price = KcFormatMoney(g.price);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRkNewView::FillReadonly(CListContainerElementUI* row, int i)
|
|
|
+{
|
|
|
+ (void)row; (void)i; // 入库明细没有只读数值列
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRkNewView::RefreshRow(int i)
|
|
|
+{
|
|
|
+ if (i < 0 || i >= (int)m_lines.size()) return;
|
|
|
+ const KcEditLine& ln = m_lines[i];
|
|
|
+
|
|
|
+ SetCellText(i, L"qty", ln.qty.c_str());
|
|
|
+ SetCellText(i, L"price", ln.price.c_str());
|
|
|
+
|
|
|
+ double q = ln.qty.empty() ? 0.0 : _wtof(ln.qty.c_str());
|
|
|
+ double p = ln.price.empty() ? 0.0 : _wtof(ln.price.c_str());
|
|
|
+ // 【R48】设计稿 p1 表内金额列是**纯数字、深色、不带 ¥**(.td.num{color:var(--t1)}):
|
|
|
+ // 金额的货币符号只在合计条上出现一次。旧实现是绿色 + "¥" 前缀,一并去掉。
|
|
|
+ SetRowLabel(i, L"col_amount", KcFormatMoney(q * p).c_str());
|
|
|
+
|
|
|
+ ApplyCellStyle(i, KC_ECOL_QTY);
|
|
|
+ ApplyCellStyle(i, KC_ECOL_PRICE);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunRkNewView::RefreshTot()
|
|
|
+{
|
|
|
+ int n = (int)m_lines.size();
|
|
|
+ double q = 0.0, amt = 0.0;
|
|
|
+ for (int i = 0; i < n; i++)
|
|
|
+ {
|
|
|
+ double qq = m_lines[i].qty.empty() ? 0.0 : _wtof(m_lines[i].qty.c_str());
|
|
|
+ double pp = m_lines[i].price.empty() ? 0.0 : _wtof(m_lines[i].price.c_str());
|
|
|
+ q += qq;
|
|
|
+ amt += qq * pp;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 【R48】设计稿 p1 合计条是「静态标签 + 动态值」两组:
|
|
|
+ // 合计商品 [3 项] 入库总数 [145] →右侧→ 入库金额 ¥3,006.00
|
|
|
+ // 标签("合计商品" / "入库总数" / "入库金额")已经写死在 kucun_page.xml 里,
|
|
|
+ // 这里只写值,别再拼整句。
|
|
|
+ wchar_t buf[192];
|
|
|
+ swprintf_s(buf, L"%d 项", n);
|
|
|
+ SetLabel(L"kc_rkn_tot_goods", buf, 0);
|
|
|
+ swprintf_s(buf, L"%.0f", q);
|
|
|
+ SetLabel(L"kc_rkn_tot_qty", buf, 0);
|
|
|
+ SetLabel(L"kc_rkn_tot_amt", (L"\u00A5" + KcFormatMoney(amt)).c_str(), 0);
|
|
|
+
|
|
|
+ swprintf_s(buf, L"已添加 %d 个商品 · 点「添加商品」勾选更多,或直接扫条码追加;"
|
|
|
+ L"点数量 / 单价单元格调出数字键盘", n);
|
|
|
+ SetLabel(L"kc_rkn_tip", buf, 0);
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 子视图 B:新建出库单 ============================
|
|
|
+void CKucunCkNewView::OnInitControl()
|
|
|
+{
|
|
|
+ CComboUI* p = FindC<CComboUI>(L"kc_ckn_combo_type");
|
|
|
+ if (p != NULL)
|
|
|
+ {
|
|
|
+ AddComboItem(p, L"调拨出库");
|
|
|
+ AddComboItem(p, L"报损");
|
|
|
+ AddComboItem(p, L"采购退货");
|
|
|
+ SelectCombo(p, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunCkNewView::SeedLines()
|
|
|
+{
|
|
|
+ // 设计稿 v4 p2:面粉 20(当前 45)/ 鸡蛋 10(18)/ 番茄 15(8,超库存 ⇒ 标红)
|
|
|
+ static const wchar_t* nm[3] = { L"面粉", L"鸡蛋", L"番茄" };
|
|
|
+ static const wchar_t* qt[3] = { L"20", L"10", L"15" };
|
|
|
+ static const wchar_t* nt[3] = { L"晚市面点", L"日常备料", L"超库存示例" };
|
|
|
+ for (int i = 0; i < 3; i++)
|
|
|
+ {
|
|
|
+ KcGoods g;
|
|
|
+ if (!KcLookupGoods(nm[i], g)) continue;
|
|
|
+ KcEditLine ln;
|
|
|
+ ln.name = g.name; ln.cat = g.cat; ln.unit = g.unit; ln.book = g.stock;
|
|
|
+ ln.qty = qt[i]; ln.note = nt[i];
|
|
|
+ m_lines.push_back(ln);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunCkNewView::OnLineAdded(KcEditLine& line)
|
|
|
+{
|
|
|
+ line.qty = L"1";
|
|
|
+}
|
|
|
+
|
|
|
+bool CKucunCkNewView::IsQtyWarn(int i) const
|
|
|
+{
|
|
|
+ if (i < 0 || i >= (int)m_lines.size()) return false;
|
|
|
+ if (m_lines[i].qty.empty()) return false;
|
|
|
+ return _wtof(m_lines[i].qty.c_str()) > (double)m_lines[i].book;
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunCkNewView::FillReadonly(CListContainerElementUI* row, int i)
|
|
|
+{
|
|
|
+ if (i < 0 || i >= (int)m_lines.size()) return;
|
|
|
+ SetRowText(row, L"col_book", std::to_wstring(m_lines[i].book).c_str());
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunCkNewView::RefreshRow(int i)
|
|
|
+{
|
|
|
+ if (i < 0 || i >= (int)m_lines.size()) return;
|
|
|
+ const KcEditLine& ln = m_lines[i];
|
|
|
+
|
|
|
+ SetCellText(i, L"qty", ln.qty.c_str());
|
|
|
+
|
|
|
+ // 【R48】设计稿 p2 第 3 行:超库存时备注格不是输入框,而是一行红字提示
|
|
|
+ // 「超出可用库存 7 斤」(.hint{font-size:12px;color:var(--red)}),两者互斥显隐。
|
|
|
+ if (IsQtyWarn(i))
|
|
|
+ {
|
|
|
+ int over = (int)_wtof(ln.qty.c_str()) - ln.book;
|
|
|
+ wchar_t hb[96];
|
|
|
+ swprintf_s(hb, L"超出可用库存 %d %s", over, ln.unit.c_str());
|
|
|
+ SetRowNoteHint(i, hb);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ SetRowNoteHint(i, NULL);
|
|
|
+ SetCellText(i, L"note", ln.note.c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ ApplyCellStyle(i, KC_ECOL_QTY);
|
|
|
+ ApplyCellStyle(i, KC_ECOL_NOTE);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunCkNewView::RefreshTot()
|
|
|
+{
|
|
|
+ int n = (int)m_lines.size();
|
|
|
+ double q = 0.0;
|
|
|
+ int lack = 0;
|
|
|
+ for (int i = 0; i < n; i++)
|
|
|
+ {
|
|
|
+ q += m_lines[i].qty.empty() ? 0.0 : _wtof(m_lines[i].qty.c_str());
|
|
|
+ if (IsQtyWarn(i)) lack++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 【R48】设计稿 p2 合计条:合计商品 [3 项] 出库总数 [45] 库存不足 [tag red 1 项]
|
|
|
+ // 「库存不足」的值是一枚胶囊 tag(12px 粗体 + 圆角底图)。>0 红胶囊、==0 灰胶囊,
|
|
|
+ // 底图和文字色都要跟着换(只换文字色会得到「灰字压红底」)。
|
|
|
+ wchar_t buf[192];
|
|
|
+ swprintf_s(buf, L"%d 项", n);
|
|
|
+ SetLabel(L"kc_ckn_tot_goods", buf, 0);
|
|
|
+ swprintf_s(buf, L"%.0f", q);
|
|
|
+ SetLabel(L"kc_ckn_tot_qty", buf, 0);
|
|
|
+ swprintf_s(buf, L"%d 项", lack);
|
|
|
+ SetLabel(L"kc_ckn_tot_lack", buf, lack > 0 ? KC_COLOR_LACK : 0xFF8A9099);
|
|
|
+ CLabelUI* pTag = FindC<CLabelUI>(L"kc_ckn_tot_lack");
|
|
|
+ if (pTag != NULL)
|
|
|
+ {
|
|
|
+ pTag->SetBkImage(lack > 0 ? L"file='kc_tag_red.png' corner='12,12,12,12'"
|
|
|
+ : L"file='kc_tag_gray.png' corner='12,12,12,12'");
|
|
|
+ pTag->NeedParentUpdate();
|
|
|
+ }
|
|
|
+
|
|
|
+ swprintf_s(buf, L"已添加 %d 个商品 · 点「添加商品」勾选更多,或直接扫条码追加;"
|
|
|
+ L"点数量单元格调出数字键盘", n);
|
|
|
+ SetLabel(L"kc_ckn_tip", buf, 0);
|
|
|
+}
|
|
|
+
|
|
|
+// ============================ 子视图 C:新建盘点单 ============================
|
|
|
+void CKucunPdNewView::OnInitControl()
|
|
|
+{
|
|
|
+ CComboUI* p = FindC<CComboUI>(L"kc_pdn_combo_type");
|
|
|
+ if (p != NULL)
|
|
|
+ {
|
|
|
+ AddComboItem(p, L"全部商品");
|
|
|
+ AddComboItem(p, L"部分商品");
|
|
|
+ AddComboItem(p, L"按分类");
|
|
|
+ SelectCombo(p, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPdNewView::SeedLines()
|
|
|
+{
|
|
|
+ // 设计稿 v4 p3:大米 320->320 / 面粉 45->43 / 食用油 12->12 / 番茄 8->6 / 鸡蛋 18->19
|
|
|
+ static const wchar_t* nm[5] = { L"大米", L"面粉", L"食用油", L"番茄", L"鸡蛋" };
|
|
|
+ static const wchar_t* qt[5] = { L"320", L"43", L"12", L"6", L"19" };
|
|
|
+ for (int i = 0; i < 5; i++)
|
|
|
+ {
|
|
|
+ KcGoods g;
|
|
|
+ if (!KcLookupGoods(nm[i], g)) continue;
|
|
|
+ KcEditLine ln;
|
|
|
+ ln.name = g.name; ln.cat = g.cat; ln.unit = g.unit; ln.book = g.stock;
|
|
|
+ ln.qty = qt[i];
|
|
|
+ m_lines.push_back(ln);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPdNewView::OnLineAdded(KcEditLine& line)
|
|
|
+{
|
|
|
+ line.qty.clear(); // 新加入的商品默认「未录」
|
|
|
+}
|
|
|
+
|
|
|
+// 「按账面库存」一键填充:把所有行(含未录行)的实盘数量写成账面库存 ⇒ 差异全为 0。
|
|
|
+// 设计稿 p3 底栏旁边那个 ghost 按钮就是干这个的,方便「先全对一遍再改少数几个」。
|
|
|
+void CKucunPdNewView::FillByBook()
|
|
|
+{
|
|
|
+ for (size_t i = 0; i < m_lines.size(); i++)
|
|
|
+ {
|
|
|
+ m_lines[i].qty = std::to_wstring(m_lines[i].book);
|
|
|
+ }
|
|
|
+ CloseKeypad();
|
|
|
+ RefreshAll();
|
|
|
+}
|
|
|
+
|
|
|
+bool CKucunPdNewView::IsCellPlaceholder(int i, int col) const
|
|
|
+{
|
|
|
+ if (col != KC_ECOL_QTY) return false;
|
|
|
+ if (i < 0 || i >= (int)m_lines.size()) return false;
|
|
|
+ return m_lines[i].qty.empty();
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPdNewView::FillReadonly(CListContainerElementUI* row, int i)
|
|
|
+{
|
|
|
+ if (i < 0 || i >= (int)m_lines.size()) return;
|
|
|
+ SetRowText(row, L"col_book", std::to_wstring(m_lines[i].book).c_str());
|
|
|
+ SetRowText(row, L"col_note", L"\u2014");
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPdNewView::RefreshRow(int i)
|
|
|
+{
|
|
|
+ if (i < 0 || i >= (int)m_lines.size()) return;
|
|
|
+ const KcEditLine& ln = m_lines[i];
|
|
|
+
|
|
|
+ SetCellText(i, L"qty", ln.qty.empty() ? L"未录" : ln.qty.c_str());
|
|
|
+
|
|
|
+ if (ln.qty.empty())
|
|
|
+ {
|
|
|
+ SetRowLabel(i, L"col_diff", L"\u2014", 0xFFA8ADB3);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ int diff = (int)_wtof(ln.qty.c_str()) - ln.book;
|
|
|
+ wchar_t buf[32];
|
|
|
+ if (diff > 0) swprintf_s(buf, L"+%d", diff);
|
|
|
+ else swprintf_s(buf, L"%d", diff);
|
|
|
+ SetRowLabel(i, L"col_diff", buf, diff > 0 ? KC_COLOR_NORMAL : (diff < 0 ? KC_COLOR_LACK : 0xFFA8ADB3));
|
|
|
+ }
|
|
|
+
|
|
|
+ ApplyCellStyle(i, KC_ECOL_QTY);
|
|
|
+}
|
|
|
+
|
|
|
+void CKucunPdNewView::RefreshTot()
|
|
|
+{
|
|
|
+ int n = (int)m_lines.size();
|
|
|
+ int filled = 0, gain = 0, loss = 0;
|
|
|
+ double amt = 0.0;
|
|
|
+
|
|
|
+ for (int i = 0; i < n; i++)
|
|
|
+ {
|
|
|
+ const KcEditLine& ln = m_lines[i];
|
|
|
+ if (ln.qty.empty()) continue;
|
|
|
+ filled++;
|
|
|
+ int diff = (int)_wtof(ln.qty.c_str()) - ln.book;
|
|
|
+ if (diff > 0) gain++;
|
|
|
+ else if (diff < 0) loss++;
|
|
|
+
|
|
|
+ KcGoods g;
|
|
|
+ double price = KcLookupGoods(ln.name, g) ? g.price : 0.0;
|
|
|
+ amt += diff * price;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 【R48】设计稿 p3 合计条:已录入 [5 项] 盘盈 [tag grn 1 项] 盘亏 [tag red 2 项]
|
|
|
+ // →右侧→ 盈亏金额 -¥86.00(负数红字,负号在 ¥ 之前)
|
|
|
+ // 标签「已录入 / 盘盈 / 盘亏 / 盈亏金额」都写死在 kucun_page.xml,这里只写值。
|
|
|
+ // 两枚 tag 的底图随计数切换(>0 彩色胶囊、==0 灰胶囊)。
|
|
|
+ wchar_t buf[192];
|
|
|
+ swprintf_s(buf, L"%d 项", filled);
|
|
|
+ SetLabel(L"kc_pdn_tot_in", buf, 0);
|
|
|
+ swprintf_s(buf, L"%d 项", gain);
|
|
|
+ SetLabel(L"kc_pdn_tot_gain", buf, gain > 0 ? KC_COLOR_NORMAL : 0xFF8A9099);
|
|
|
+ swprintf_s(buf, L"%d 项", loss);
|
|
|
+ SetLabel(L"kc_pdn_tot_loss", buf, loss > 0 ? KC_COLOR_LACK : 0xFF8A9099);
|
|
|
+
|
|
|
+ CLabelUI* pGain = FindC<CLabelUI>(L"kc_pdn_tot_gain");
|
|
|
+ if (pGain != NULL)
|
|
|
+ {
|
|
|
+ pGain->SetBkImage(gain > 0 ? L"file='kc_tag_grn.png' corner='12,12,12,12'"
|
|
|
+ : L"file='kc_tag_gray.png' corner='12,12,12,12'");
|
|
|
+ pGain->NeedParentUpdate();
|
|
|
+ }
|
|
|
+ CLabelUI* pLoss = FindC<CLabelUI>(L"kc_pdn_tot_loss");
|
|
|
+ if (pLoss != NULL)
|
|
|
+ {
|
|
|
+ pLoss->SetBkImage(loss > 0 ? L"file='kc_tag_red.png' corner='12,12,12,12'"
|
|
|
+ : L"file='kc_tag_gray.png' corner='12,12,12,12'");
|
|
|
+ pLoss->NeedParentUpdate();
|
|
|
+ }
|
|
|
+
|
|
|
+ std::wstring amtTxt = (amt < 0 ? L"-\u00A5" : L"\u00A5") + KcFormatMoney(amt < 0 ? -amt : amt);
|
|
|
+ SetLabel(L"kc_pdn_tot_amt", amtTxt.c_str(), amt < 0 ? KC_COLOR_LACK : KC_COLOR_NORMAL);
|
|
|
+
|
|
|
+ if (n > 0 && filled < n)
|
|
|
+ swprintf_s(buf, L"盘点范围:全部商品(共 %d 项,已录入 %d 项)· 点实盘数量单元格调出数字键盘", n, filled);
|
|
|
+ else
|
|
|
+ swprintf_s(buf, L"盘点范围:全部商品(共 %d 项,已全部录入)· 差异 = 实盘 − 账面", n);
|
|
|
+ SetLabel(L"kc_pdn_tip", buf, 0);
|
|
|
+}
|