CCaigouPageUI.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. #include "pch.h"
  2. #include "CCaigouPageUI.h"
  3. #include "../control/ControlEx.h"
  4. #include "../wnd/CMainWnd.h"
  5. #include "../wnd/CKucunQtyGuardWnd.h"
  6. namespace
  7. {
  8. // 从被点的子控件回溯到 ListContainerElement,读 col_no 标签拿采购单号
  9. std::wstring CgRowNo(CControlUI* p)
  10. {
  11. while (p != NULL)
  12. {
  13. CListContainerElementUI* row = dynamic_cast<CListContainerElementUI*>(p);
  14. if (row != NULL)
  15. {
  16. CLabelUI* lab = static_cast<CLabelUI*>(row->FindSubControl(L"col_no"));
  17. if (lab != NULL) return std::wstring((LPCTSTR)lab->GetText());
  18. return L"";
  19. }
  20. p = p->GetParent();
  21. }
  22. return L"";
  23. }
  24. }
  25. CCaigouPageUI::CCaigouPageUI()
  26. : m_pMain(NULL), m_pNew(NULL), m_pKpHost(NULL), m_pPickOverlay(NULL), m_pPickList(NULL),
  27. m_nPickCat(0), m_nSubView(-1), m_nPeriod(0), m_nPageWidth(0)
  28. {
  29. // 弹层候选项来自商品档案,勾选态(on)跨分类保留
  30. const std::vector<KcGoods>& all = KcAllGoods();
  31. for (size_t i = 0; i < all.size(); i++)
  32. {
  33. CgPickItem it;
  34. it.name = all[i].name;
  35. it.cat = all[i].cat;
  36. it.unit = all[i].unit;
  37. it.stock = all[i].stock;
  38. it.on = false;
  39. m_pick.push_back(it);
  40. }
  41. }
  42. CCaigouPageUI::~CCaigouPageUI()
  43. {
  44. if (m_pMain) { delete m_pMain; m_pMain = NULL; }
  45. if (m_pNew) { delete m_pNew; m_pNew = NULL; }
  46. }
  47. void CCaigouPageUI::InitControl()
  48. {
  49. CPaintManagerUI* pm = GetManager();
  50. // 主态视图(记录 + 统计 双卡,host = cg_scroll)
  51. CContainerUI* hMain = static_cast<CContainerUI*>(FindSubControl(L"cg_scroll"));
  52. m_pMain = new CCaigouMainView(pm, hMain);
  53. m_pMain->OnInitControl();
  54. // 新建采购单子视图(host = cg_view_order_new,键盘 host = cg_keypad)
  55. m_pKpHost = static_cast<CContainerUI*>(FindSubControl(L"cg_keypad"));
  56. CContainerUI* hNew = static_cast<CContainerUI*>(FindSubControl(L"cg_view_order_new"));
  57. m_pNew = new CCaigouNewView(pm, hNew, m_pKpHost);
  58. m_pNew->OnInitControl();
  59. // 添加商品弹层
  60. m_pPickOverlay = static_cast<CContainerUI*>(FindSubControl(L"cg_pick_overlay"));
  61. m_pPickList = static_cast<CListUI*>(FindSubControl(L"cg_pick_list"));
  62. ShowMain();
  63. }
  64. void CCaigouPageUI::InitShow()
  65. {
  66. ShowMain();
  67. if (m_pMain) m_pMain->InitShow();
  68. SyncPeriodSeg();
  69. }
  70. // ---------------------------------------------------------------- 视图显隐
  71. void CCaigouPageUI::ShowMain()
  72. {
  73. CControlUI* pScroll = FindSubControl(L"cg_scroll");
  74. if (pScroll) pScroll->SetVisible(true);
  75. CControlUI* pNew = FindSubControl(L"cg_view_order_new");
  76. if (pNew) pNew->SetVisible(false);
  77. CControlUI* pFoot = FindSubControl(L"cg_new_foot");
  78. if (pFoot) pFoot->SetVisible(false);
  79. if (m_pKpHost) m_pKpHost->SetVisible(false);
  80. if (m_pPickOverlay) m_pPickOverlay->SetVisible(false);
  81. if (m_pNew) m_pNew->CloseKeypad();
  82. m_nSubView = -1;
  83. KcSyncNativeEditsIn(this);
  84. }
  85. void CCaigouPageUI::ShowSubView(int idx)
  86. {
  87. if (idx != 0 || m_pNew == NULL) return;
  88. CControlUI* pScroll = FindSubControl(L"cg_scroll");
  89. if (pScroll) pScroll->SetVisible(false);
  90. CControlUI* pNew = FindSubControl(L"cg_view_order_new");
  91. if (pNew) pNew->SetVisible(true);
  92. CControlUI* pFoot = FindSubControl(L"cg_new_foot");
  93. if (pFoot) pFoot->SetVisible(true);
  94. if (m_pKpHost) m_pKpHost->SetVisible(false);
  95. if (m_pPickOverlay) m_pPickOverlay->SetVisible(false);
  96. m_nSubView = 0;
  97. m_pNew->InitShow();
  98. KcSyncNativeEditsIn(this);
  99. }
  100. void CCaigouPageUI::SyncPeriodSeg()
  101. {
  102. for (int i = 0; i < 3; i++)
  103. {
  104. LPCTSTR nm[3] = { L"cg_per_0", L"cg_per_1", L"cg_per_2" };
  105. CCaigouViewBase::StylePeriodSeg(
  106. static_cast<CButtonUI*>(FindSubControl(nm[i])), i == m_nPeriod);
  107. }
  108. CControlUI* pRange = FindSubControl(L"cg_per_range");
  109. // ⭐【R75】cg_per_range 是 autocalcwidth(区间文案长度按周期变)⇒ 走 SetLabelText,
  110. // 只 SetText 会被按旧宽度裁掉。口径与视图侧 CCaigouMainView::SetPeriod 一致。
  111. if (pRange != NULL && m_pMain != NULL)
  112. CCaigouViewBase::SetLabelText(pRange, CgPeriodRangeText(m_nPeriod).c_str());
  113. }
  114. // ---------------------------------------------------------------- 添加商品弹层
  115. void CCaigouPageUI::ShowPick(bool bShow)
  116. {
  117. if (m_pPickOverlay == NULL) return;
  118. if (bShow)
  119. {
  120. if (m_pNew) m_pNew->CloseKeypad();
  121. for (size_t i = 0; i < m_pick.size(); i++) m_pick[i].on = false;
  122. m_strPickSearch.clear();
  123. CEditUI* pSearch = static_cast<CEditUI*>(FindSubControl(L"cg_pick_search"));
  124. if (pSearch) pSearch->SetText(L"");
  125. PickSetCat(0);
  126. }
  127. m_pPickOverlay->SetVisible(bShow);
  128. KcSyncNativeEditsIn(this);
  129. }
  130. void CCaigouPageUI::PickSetCat(int idx)
  131. {
  132. if (idx < 0 || idx > 6) idx = 0;
  133. m_nPickCat = idx;
  134. for (int i = 0; i < 7; i++)
  135. {
  136. CButtonUI* p = static_cast<CButtonUI*>(FindSubControl(
  137. (std::wstring(L"cg_pick_cat_") + std::to_wstring(i)).c_str()));
  138. if (p == NULL) continue;
  139. bool act = (i == idx);
  140. p->SetNormalImage(act ? L"file='dc_pill_h.png' corner='11,11,11,11'" : L"");
  141. p->SetTextColor(act ? 0xFF2E9B5C : 0xFF5F6368);
  142. p->SetHotTextColor(0xFF2E9B5C);
  143. p->SetPushedTextColor(0xFF1B8A4C);
  144. p->SetFont(act ? 113 : 13);
  145. }
  146. RefreshPickList();
  147. }
  148. void CCaigouPageUI::RefreshPickList()
  149. {
  150. if (m_pPickList == NULL) return;
  151. while (m_pPickList->GetCount() > 0) m_pPickList->RemoveAt(0);
  152. m_pickView.clear();
  153. std::wstring kw = m_strPickSearch;
  154. for (int i = 0; i < (int)m_pick.size(); i++)
  155. {
  156. const CgPickItem& it = m_pick[i];
  157. if (m_nPickCat > 0 && CG_CATS[m_nPickCat] != it.cat) continue;
  158. if (!kw.empty())
  159. {
  160. KcGoods g;
  161. std::wstring py = KcLookupGoods(it.name, g) ? g.py : L"";
  162. if (it.name.find(kw) == std::wstring::npos &&
  163. py.find(kw) == std::wstring::npos) continue;
  164. }
  165. CDialogBuilder builder;
  166. CDialogBuilderCallbackEx cb;
  167. CListContainerElementUI* row =
  168. static_cast<CListContainerElementUI*>(builder.Create(L"cg_row_pick.xml", (UINT)0, &cb, GetManager()));
  169. if (row == NULL) continue;
  170. CLabelUI* pName = static_cast<CLabelUI*>(row->FindSubControl(L"pk_name"));
  171. CLabelUI* pMeta = static_cast<CLabelUI*>(row->FindSubControl(L"pk_meta"));
  172. CLabelUI* pStock = static_cast<CLabelUI*>(row->FindSubControl(L"pk_stock"));
  173. CControlUI* pCb = row->FindSubControl(L"pk_cb");
  174. if (pName) pName->SetText(it.name.c_str());
  175. if (pMeta) pMeta->SetText((it.cat + L" · " + it.unit).c_str());
  176. if (pStock) pStock->SetText(std::to_wstring(it.stock).c_str());
  177. if (pCb) pCb->SetBkImage(it.on ? L"kc_chk_on_24.png" : L"kc_chk_off_24.png");
  178. m_pPickList->Add(row);
  179. m_pickView.push_back(i);
  180. }
  181. SyncPickSelCnt();
  182. }
  183. void CCaigouPageUI::TogglePickRow(int idx)
  184. {
  185. if (idx < 0 || idx >= (int)m_pickView.size()) return;
  186. int gi = m_pickView[idx];
  187. m_pick[gi].on = !m_pick[gi].on;
  188. CListContainerElementUI* row =
  189. static_cast<CListContainerElementUI*>(m_pPickList->GetItemAt(idx));
  190. if (row != NULL)
  191. {
  192. CControlUI* pCb = row->FindSubControl(L"pk_cb");
  193. if (pCb) pCb->SetBkImage(m_pick[gi].on ? L"kc_chk_on_24.png" : L"kc_chk_off_24.png");
  194. }
  195. SyncPickSelCnt();
  196. }
  197. void CCaigouPageUI::SyncPickSelCnt()
  198. {
  199. int n = 0;
  200. for (size_t i = 0; i < m_pick.size(); i++) if (m_pick[i].on) n++;
  201. CLabelUI* p = static_cast<CLabelUI*>(FindSubControl(L"cg_pick_selcnt"));
  202. // ⭐【R75】cg_pick_selcnt 是 autocalcwidth(「已选 3 项」→「已选 12 项」位数会变)
  203. // ⇒ 走 SetLabelText,只 SetText 会被按旧宽度裁掉。
  204. if (p != NULL)
  205. CCaigouViewBase::SetLabelText(p, (L"已选 " + std::to_wstring(n) + L" 项").c_str());
  206. }
  207. std::vector<std::wstring> CCaigouPageUI::CollectPicked() const
  208. {
  209. std::vector<std::wstring> out;
  210. for (size_t i = 0; i < m_pick.size(); i++)
  211. if (m_pick[i].on) out.push_back(m_pick[i].name);
  212. return out;
  213. }
  214. void CCaigouPageUI::AppendPickedFromOverlay()
  215. {
  216. std::vector<std::wstring> picked = CollectPicked();
  217. if (m_pNew) m_pNew->AppendPicked(picked);
  218. ShowPick(false);
  219. }
  220. // ---------------------------------------------------------------- 详情弹框
  221. void CCaigouPageUI::OpenDetail(const CgDocDetail& doc)
  222. {
  223. if (m_is_show_modal_wnd) return;
  224. CCaigouDetailWnd* pDlg = new CCaigouDetailWnd(doc);
  225. m_is_show_modal_wnd = true;
  226. m_curModalWnd = pDlg;
  227. pDlg->Create(GetManager()->GetPaintWindow(), _T(""), UI_WNDSTYLE_DIALOG, WS_EX_WINDOWEDGE);
  228. pDlg->SetIcon(IDI_ICON_DUILIB);
  229. pDlg->LoadDetail();
  230. if (m_pMainWnd != NULL) m_pMainWnd->ShowGlobalDim(true);
  231. pDlg->CenterWindow();
  232. pDlg->ShowModal();
  233. if (m_pMainWnd != NULL) m_pMainWnd->ShowGlobalDim(false);
  234. m_is_show_modal_wnd = false;
  235. m_curModalWnd = NULL;
  236. const bool bPrint = pDlg->IsPrintClicked();
  237. delete pDlg;
  238. if (bPrint && m_pMainWnd != NULL)
  239. m_pMainWnd->ShowToast(L"打印小票:热敏小票本轮未接入");
  240. }
  241. // ---------------------------------------------------------------- 事件路由
  242. void CCaigouPageUI::HandleClickMsg(TNotifyUI& msg)
  243. {
  244. std::wstring n = (LPCTSTR)msg.pSender->GetName();
  245. // 主态「新建采购单」按钮 → 进子视图
  246. if (n == L"cg_rec_new_btn" || n == L"cg_stat_new_btn") { ShowSubView(0); return; }
  247. // ---- 添加商品弹层(必须在子视图分支【之前】判定!)
  248. // 弹层正是从「新建采购单」页点「添加商品」打开的,此时 m_nSubView==0。
  249. // 若把弹层判定放到子视图分支之后,弹层内任何点击都会先落进
  250. // `if (m_nSubView == 0)` 那段被整段吞掉、转发给 m_pNew->OnClick
  251. // ⇒ 弹层里的行 / 分类 / 清空 / 取消 / 确定 全部点不动。
  252. // 判定顺序与库存页 CKucunPageUI::HandleClickMsg 保持一致。
  253. if (n == L"pk_row")
  254. {
  255. int idx = (m_pPickList != NULL) ? m_pPickList->GetItemIndex(msg.pSender) : -1;
  256. TogglePickRow(idx);
  257. return;
  258. }
  259. if (n == L"cg_pick_close" || n == L"cg_pick_cancel") { ShowPick(false); return; }
  260. if (n == L"cg_pick_clear")
  261. {
  262. for (size_t i = 0; i < m_pick.size(); i++) m_pick[i].on = false;
  263. RefreshPickList();
  264. return;
  265. }
  266. if (n == L"cg_pick_ok") { AppendPickedFromOverlay(); return; }
  267. if (n.size() > 12 && n.compare(0, 12, L"cg_pick_cat_") == 0)
  268. {
  269. PickSetCat(_wtoi(n.c_str() + 12));
  270. return;
  271. }
  272. // 新建态页面级按钮
  273. if (m_nSubView == 0)
  274. {
  275. if (n == L"cg_cgn_back" || n == L"cg_cgn_cancel") { ShowMain(); return; }
  276. if (n == L"cg_cgn_save")
  277. {
  278. // 【R74】保存 / 提交前的采购数量校验,口径与库存页入库 / 出库**完全一致**:
  279. // 键盘清空即归 0、「不存在空」⇒ 0(含未录入)只能是漏填,硬拦不让走。
  280. // 明细单元格边框早已按「编辑绿 / 0 琥珀 / 常态白」着色(ApplyCellStyle),
  281. // 这里只负责把问题行摆到弹框里让用户看清是第几行、哪一项。
  282. // 采购页只有一张明细表,无盘点语义 ⇒ kind 恒为 0、允许「删除该行」。
  283. if (m_pNew != NULL)
  284. {
  285. std::vector<KcQtyIssue> issues;
  286. m_pNew->CollectQtyIssues(issues);
  287. if (!issues.empty())
  288. {
  289. // 逐行报「商品名 + 行号」:合计条只给项数,不报行号用户定位不到是哪一行。
  290. // 弹框自己会截断到 8 行并追加「…等共 N 项」,这里不截。
  291. std::vector<std::wstring> lines;
  292. for (size_t i = 0; i < issues.size(); i++)
  293. {
  294. wchar_t b[256];
  295. swprintf_s(b, L"%s(第 %d 行)· %s%s",
  296. issues[i].name.c_str(), issues[i].row + 1,
  297. issues[i].field.c_str(),
  298. issues[i].unfilled ? L"未录入" : L"为 0");
  299. lines.push_back(b);
  300. }
  301. // 「删除该行」只在**单行**出错时给 —— 多行时删哪一行有歧义。
  302. bool bAllowDel = (issues.size() == 1);
  303. CKucunQtyGuardWnd* pDlg = new CKucunQtyGuardWnd(0, lines, bAllowDel);
  304. m_is_show_modal_wnd = true;
  305. m_curModalWnd = pDlg;
  306. pDlg->Create(GetManager()->GetPaintWindow(), _T(""), UI_WNDSTYLE_DIALOG, WS_EX_WINDOWEDGE);
  307. pDlg->SetIcon(IDI_ICON_DUILIB);
  308. pDlg->Load(); // 铺文案 + 按清单行数调窗高
  309. if (m_pMainWnd != NULL) m_pMainWnd->ShowGlobalDim(true);
  310. pDlg->CenterWindow();
  311. pDlg->ShowModal();
  312. if (m_pMainWnd != NULL) m_pMainWnd->ShowGlobalDim(false);
  313. m_is_show_modal_wnd = false;
  314. m_curModalWnd = NULL;
  315. // 单行出错才点了就有反应(bAllowDel 已经保证多行时那枚按钮根本不显示,
  316. // 这里再判一次 size()==1 是防呆):把它从明细里删掉,页面停在原处接着改。
  317. bool bDel = pDlg->IsDeleteClicked();
  318. delete pDlg;
  319. if (bDel && issues.size() == 1) m_pNew->DeleteLine(issues[0].row);
  320. return; // 硬拦:不保存、也不 leave 本页
  321. }
  322. }
  323. if (m_pMainWnd != NULL) m_pMainWnd->ShowToast(L"采购单已提交(占位,后端未接入)");
  324. return;
  325. }
  326. if (n == L"cg_cgn_add") { ShowPick(true); return; }
  327. // 其余(明细行按钮、数字键盘)交给新建视图
  328. if (m_pNew) m_pNew->OnClick(msg);
  329. return;
  330. }
  331. // 记录行详情 / 打印
  332. if (n == L"op_detail")
  333. {
  334. std::wstring no = CgRowNo(msg.pSender);
  335. CgDocDetail d;
  336. if (!CgLookupDetail(no, d))
  337. {
  338. if (m_pMainWnd != NULL) m_pMainWnd->ShowToast(L"未找到单据详情:" + no);
  339. return;
  340. }
  341. OpenDetail(d);
  342. return;
  343. }
  344. if (n == L"op_print")
  345. {
  346. std::wstring no = CgRowNo(msg.pSender);
  347. if (m_pMainWnd != NULL)
  348. m_pMainWnd->ShowToast(L"打印(占位):" + (no.empty() ? std::wstring(L"未取到单号") : no));
  349. return;
  350. }
  351. // 其余交给主态视图
  352. if (m_pMain) m_pMain->OnClick(msg);
  353. }
  354. void CCaigouPageUI::HandleSelectChangeMsg(TNotifyUI& msg)
  355. {
  356. // 采购页无 Option / CheckBox;弹层勾选方块是 Control,靠整行 itemclick 切换
  357. (void)msg;
  358. }
  359. void CCaigouPageUI::HandleItemSelectMsg(TNotifyUI& msg)
  360. {
  361. if (m_pMain) m_pMain->OnItemSelect(msg);
  362. }
  363. void CCaigouPageUI::HandleTextChangedMsg(TNotifyUI& msg)
  364. {
  365. std::wstring n = (LPCTSTR)msg.pSender->GetName();
  366. if (n == L"cg_pick_search")
  367. {
  368. CEditUI* pEdit = static_cast<CEditUI*>(msg.pSender);
  369. if (pEdit) m_strPickSearch = (LPCTSTR)pEdit->GetText();
  370. RefreshPickList();
  371. return;
  372. }
  373. if (m_pMain) m_pMain->OnTextChanged(msg);
  374. }
  375. void CCaigouPageUI::HandleReturnMsg(TNotifyUI& msg)
  376. {
  377. if (m_pMain) m_pMain->SubmitSearch();
  378. (void)msg;
  379. }
  380. void CCaigouPageUI::HandleTextCapture(std::string content)
  381. {
  382. // 扫码枪:本轮不接后端,留空(将来可在新建态按条码追加商品)
  383. (void)content;
  384. }
  385. bool CCaigouPageUI::HandleCustomMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  386. {
  387. // 采购页无自定义窗口消息需要处理
  388. (void)uMsg; (void)wParam; (void)lParam;
  389. return false;
  390. }
  391. void CCaigouPageUI::SetPos(RECT rc, bool bNeedInvalidate)
  392. {
  393. m_nPageWidth = rc.right - rc.left;
  394. CContainerUI::SetPos(rc, bNeedInvalidate);
  395. }