From d119dcff794370b1852c0e9a3cda28d8d2bc6ae8 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期五, 30 五月 2025 17:24:51 +0800
Subject: [PATCH] 1. 添加搬运记录对话框 2. 修复搬运记录的数据库管理类的中文问题

---
 SourceCode/Bond/Servo/Servo.vcxproj         |    2 
 SourceCode/Bond/Servo/PageAlarm.cpp         |    7 
 SourceCode/Bond/Servo/Servo.vcxproj.filters |    2 
 SourceCode/Bond/Servo/resource.h            |    0 
 SourceCode/Bond/Servo/TransferManager.h     |   22 
 SourceCode/Bond/Servo/Servo.rc              |    0 
 SourceCode/Bond/Servo/PageTransferLog.cpp   |  498 +++++++++++++++++++++++++++++++++++++++++
 SourceCode/Bond/Servo/PageTransferLog.h     |   70 +++++
 SourceCode/Bond/Servo/TransferManager.cpp   |   90 ++++---
 9 files changed, 643 insertions(+), 48 deletions(-)

diff --git a/SourceCode/Bond/Servo/PageAlarm.cpp b/SourceCode/Bond/Servo/PageAlarm.cpp
index 51c0f1a..f86f5a2 100644
--- a/SourceCode/Bond/Servo/PageAlarm.cpp
+++ b/SourceCode/Bond/Servo/PageAlarm.cpp
@@ -39,6 +39,13 @@
 
 CPageAlarm::~CPageAlarm()
 {
+	if (m_hbrBkgnd != nullptr) {
+		::DeleteObject(m_hbrBkgnd);
+	}
+	if (m_pObserver != nullptr) {
+		m_pObserver->unsubscribe();
+		m_pObserver = nullptr;
+	}
 }
 
 void CPageAlarm::InitRxWindow()
diff --git a/SourceCode/Bond/Servo/PageTransferLog.cpp b/SourceCode/Bond/Servo/PageTransferLog.cpp
new file mode 100644
index 0000000..8000c23
--- /dev/null
+++ b/SourceCode/Bond/Servo/PageTransferLog.cpp
@@ -0,0 +1,498 @@
+锘�// PageTransferLog.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "afxdialogex.h"
+#include "PageTransferLog.h"
+#include "Common.h"
+
+#define PAGE_SIZE						100
+#define PAGE_BACKGROUND_COLOR			RGB(252, 252, 255)
+
+// CPageTransferLog 瀵硅瘽妗�
+
+IMPLEMENT_DYNAMIC(CPageTransferLog, CDialogEx)
+
+CPageTransferLog::CPageTransferLog(CWnd* pParent /*=nullptr*/)
+	: CDialogEx(IDD_PAGE_TRANSFER_LOG, pParent)
+{
+	m_crBkgnd = PAGE_BACKGROUND_COLOR;
+	m_hbrBkgnd = nullptr;
+	m_pObserver = nullptr;
+
+	m_strStatus = "";
+	m_strKeyword = "";
+	m_nCurPage = 0;
+	m_nTotalPages = 0;
+
+	memset(m_szTimeStart, 0, sizeof(m_szTimeStart));
+	memset(m_szTimeEnd, 0, sizeof(m_szTimeEnd));
+	m_szTimeStart[0] = '\0';
+	m_szTimeEnd[0] = '\0';
+}
+
+CPageTransferLog::~CPageTransferLog()
+{
+	if (m_hbrBkgnd != nullptr) {
+		::DeleteObject(m_hbrBkgnd);
+	}
+	if (m_pObserver != nullptr) {
+		m_pObserver->unsubscribe();
+		m_pObserver = nullptr;
+	}
+}
+
+void CPageTransferLog::DoDataExchange(CDataExchange* pDX)
+{
+	DDX_Control(pDX, IDC_DATETIMEPICKER_START, m_dateTimeStart);
+	DDX_Control(pDX, IDC_DATETIMEPICKER_END, m_dateTimeEnd);
+	DDX_Control(pDX, IDC_LIST_ALARM, m_listCtrl);
+	CDialogEx::DoDataExchange(pDX);
+}
+
+void CPageTransferLog::InitRxWindow()
+{
+	/* code */
+	// 璁㈤槄鏁版嵁
+	IRxWindows* pRxWindows = RX_GetRxWindows();
+	pRxWindows->enableLog(5);
+	if (m_pObserver == NULL) {
+		m_pObserver = pRxWindows->allocObserver([&](IAny* pAny) -> void {
+			// onNext
+			pAny->addRef();
+			int code = pAny->getCode();
+
+			//if (RX_CODE_ALARM_SET == code) {
+			//	UpdatePageData();
+			//}
+			//else if (RX_CODE_ALARM_CLEAR == code) {
+			//	UpdatePageData();
+			//}
+
+			pAny->release();
+			}, [&]() -> void {
+				// onComplete
+			}, [&](IThrowable* pThrowable) -> void {
+				// onErrorm
+				pThrowable->printf();
+			});
+
+			theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())->subscribe(m_pObserver);
+	}
+}
+
+void CPageTransferLog::Resize()
+{
+	CRect rcClient;
+	GetClientRect(&rcClient);
+
+	// ===== 甯搁噺瀹氫箟 =====
+	const int nLeft = 12;
+	const int nRight = 12;
+	const int nTop = 58;
+	const int nButtonHeight = 28;
+	const int nButtonMarginBottom = 12;
+	const int nSpacing = 8;
+	const int nButtonWidth = 80;
+	const int nLabelWidth = 100;
+
+	// ===== 鍒嗛〉鎺т欢甯冨眬 =====
+	int yBottom = rcClient.bottom - nButtonMarginBottom - nButtonHeight;
+	int xRight = rcClient.Width() - nRight;
+
+	CWnd* pBtnNext = GetDlgItem(IDC_BUTTON_NEXT_PAGE);
+	CWnd* pBtnPrev = GetDlgItem(IDC_BUTTON_PREV_PAGE);
+	CWnd* pLabelPage = GetDlgItem(IDC_LABEL_PAGE_NUMBER);
+
+	if (pBtnNext && pBtnPrev && pLabelPage) {
+		// 鑾峰彇鍒嗛〉鏂囨湰瀹藉害浼扮畻
+		//CString strLabel;
+		//GetDlgItemText(IDC_LABEL_PAGE_NUMBER, strLabel);
+		//if (strLabel.IsEmpty()) {
+		//	strLabel = _T("绗� 1 / 1 椤�");
+		//}
+		//int nCharWidth = 8;
+		//int nLabelWidth = strLabel.GetLength() * nCharWidth + 20;
+
+		// 璁剧疆鎸夐挳鍜屾爣绛句綅缃�
+		pBtnNext->MoveWindow(xRight - nButtonWidth, yBottom, nButtonWidth, nButtonHeight);
+		xRight -= nButtonWidth + nSpacing;
+
+		pLabelPage->MoveWindow(xRight - nLabelWidth, yBottom, nLabelWidth, nButtonHeight);
+		xRight -= nLabelWidth + nSpacing;
+
+		pBtnPrev->MoveWindow(xRight - nButtonWidth, yBottom, nButtonWidth, nButtonHeight);
+	}
+
+	// ===== 琛ㄦ牸鍖哄煙甯冨眬 =====
+	if (nullptr != m_listCtrl.m_hWnd) {
+		int listHeight = yBottom - nTop - nSpacing;
+		m_listCtrl.MoveWindow(nLeft, nTop, rcClient.Width() - nLeft - nRight, listHeight);
+	}
+}
+
+void CPageTransferLog::InitStatusCombo()
+{
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_STATUS_FILTER);
+	if (nullptr != pComboBox) {
+		pComboBox->ResetContent();
+		pComboBox->AddString(_T("鍏ㄩ儴"));
+		pComboBox->AddString(_T("Ready"));
+		pComboBox->AddString(_T("Running"));
+		pComboBox->AddString(_T("Error"));
+		pComboBox->AddString(_T("Abort"));
+		pComboBox->AddString(_T("Completed"));
+		pComboBox->SetCurSel(0);
+	}
+}
+
+void CPageTransferLog::InitTimeRangeCombo()
+{
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME);
+	if (nullptr != pComboBox) {
+		pComboBox->ResetContent();
+		pComboBox->AddString(_T("涓嶉檺"));
+		pComboBox->AddString(_T("浠婂ぉ"));
+		pComboBox->AddString(_T("涓冨ぉ鍐�"));
+		pComboBox->AddString(_T("鏈湀"));
+		pComboBox->AddString(_T("浠婂勾"));
+		pComboBox->AddString(_T("鑷畾涔�"));
+		pComboBox->SetCurSel(0);
+	}
+}
+
+void CPageTransferLog::InitDateTimeControls()
+{
+	if (m_dateTimeStart.m_hWnd == nullptr || m_dateTimeEnd.m_hWnd == nullptr) {
+		return;
+	}
+
+	// 绂佺敤鍒濆鐘舵��
+	m_dateTimeStart.EnableWindow(FALSE);
+	m_dateTimeEnd.EnableWindow(FALSE);
+
+	// 璁剧疆鏍煎紡锛氭樉绀烘棩鏈� + 鏃堕棿
+	//m_dateTimeStart.SetFormat(_T("yyyy/MM/dd HH:mm:ss"));
+	//m_dateTimeEnd.SetFormat(_T("yyyy/MM/dd HH:mm:ss"));
+
+	// 淇敼鏍峰紡浠ユ敮鎸佹椂闂存牸寮�
+	//DWORD dwStyleStart = m_dateTimeStart.GetStyle();
+	//DWORD dwStyleEnd = m_dateTimeEnd.GetStyle();
+
+	//m_dateTimeStart.ModifyStyle(0, DTS_TIMEFORMAT | DTS_UPDOWN);
+	//m_dateTimeEnd.ModifyStyle(0, DTS_TIMEFORMAT);
+}
+
+void CPageTransferLog::LoadTransfers()
+{
+	m_nCurPage = 1;
+	UpdatePageData();
+}
+
+void CPageTransferLog::UpdatePageData()
+{
+	TransferData filter;
+	filter.strStatus = m_strStatus;
+	filter.strDescription = m_strKeyword;
+	filter.strCreateTime = m_szTimeStart;
+	filter.strEndTime = m_szTimeEnd;
+	auto vecData = TransferManager::getInstance().getTransfers(filter, m_nCurPage, PAGE_SIZE);
+	FillDataToListCtrl(vecData);
+
+	int nTotalRecords = TransferManager::getInstance().getFilteredTransferCount(filter);
+	m_nTotalPages = (nTotalRecords + PAGE_SIZE - 1) / PAGE_SIZE;
+	UpdatePageControls();
+}
+
+void CPageTransferLog::UpdatePageControls()
+{
+	CString strPage;
+	strPage.Format(_T("绗� %d / %d 椤�"), m_nCurPage, m_nTotalPages);
+	SetDlgItemText(IDC_LABEL_PAGE_NUMBER, strPage);
+	GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurPage > 1);
+	GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurPage < m_nTotalPages);
+
+	Resize();
+}
+
+void CPageTransferLog::UpdateDateFilter()
+{
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME);
+	if (nullptr != pComboBox) {
+		int nIndex = pComboBox->GetCurSel();
+		if (nIndex == 0) {
+			memset(m_szTimeStart, 0, sizeof(m_szTimeStart));
+			memset(m_szTimeEnd, 0, sizeof(m_szTimeEnd));
+			m_szTimeStart[0] = '\0';
+			m_szTimeEnd[0] = '\0';
+		}
+		else {
+			CTime time = CTime::GetCurrentTime();
+			if (nIndex == 1) {
+				sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", time.GetYear(), time.GetMonth(), time.GetDay());
+				sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay());
+			}
+			else if (nIndex == 2) {
+				CTime time2 = time - CTimeSpan(7, 0, 0, 0);
+				sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", time2.GetYear(), time2.GetMonth(), time2.GetDay());
+				sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay());
+			}
+			else if (nIndex == 3) {
+				sprintf_s(m_szTimeStart, 64, "%d-%02d-01 00:00:00", time.GetYear(), time.GetMonth());
+				sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay());
+			}
+			else if (nIndex == 4) {
+				sprintf_s(m_szTimeStart, 64, "%d-01-01 00:00:00", time.GetYear());
+				sprintf_s(m_szTimeEnd, 64, "%d-12-31 23:59:59", time.GetYear());
+			}
+			else if (nIndex == 5) {
+				SYSTEMTIME t1, t2;
+				m_dateTimeStart.GetTime(&t1);
+				m_dateTimeEnd.GetTime(&t2);
+				//sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d %02d:%02d:%02d", t1.wYear, t1.wMonth, t1.wDay, t1.wHour, t1.wMinute, t1.wSecond);
+				//sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d %02d:%02d:%02d", t2.wYear, t2.wMonth, t2.wDay, t2.wHour, t2.wMinute, t2.wSecond);
+
+				sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", t1.wYear, t1.wMonth, t1.wDay);
+				sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", t2.wYear, t2.wMonth, t2.wDay);
+			}
+		}
+	}
+}
+
+void CPageTransferLog::FillDataToListCtrl(const std::vector<TransferData>& vecData)
+{
+	if (m_listCtrl.m_hWnd == nullptr) {
+		return;
+	}
+
+	m_listCtrl.DeleteAllItems();
+	for (const auto& item : vecData) {
+		InsertTransferData(item);
+	}
+}
+
+void CPageTransferLog::InsertTransferData(const TransferData& data)
+{
+	if (m_listCtrl.m_hWnd == nullptr) {
+		return;
+	}
+
+	int nItem = m_listCtrl.InsertItem(0, _T(""));
+	CString str;
+	str.Format(_T("%d"), data.nRecordId);
+	m_listCtrl.SetItemText(nItem, 1, str);
+	m_listCtrl.SetItemText(nItem, 2, CString(data.strStatus.c_str()));
+	m_listCtrl.SetItemText(nItem, 3, CString(data.strClassID.c_str()));
+	m_listCtrl.SetItemText(nItem, 4, CString(data.strCreateTime.c_str()));
+	m_listCtrl.SetItemText(nItem, 5, CString(data.strPickTime.c_str()));
+	m_listCtrl.SetItemText(nItem, 6, CString(data.strPlaceTime.c_str()));
+	m_listCtrl.SetItemText(nItem, 7, CString(data.strEndTime.c_str()));
+	m_listCtrl.SetItemText(nItem, 8, CString(data.strDescription.c_str()));
+}
+
+BEGIN_MESSAGE_MAP(CPageTransferLog, CDialogEx)
+	ON_WM_CTLCOLOR()
+	ON_WM_DESTROY()
+	ON_WM_SIZE()
+	ON_WM_TIMER()
+	ON_CBN_SELCHANGE(IDC_COMBO_DATETIME, &CPageTransferLog::OnCbnSelchangeComboDatetime)
+	ON_CBN_SELCHANGE(IDC_COMBO_STATUS_FILTER, &CPageTransferLog::OnCbnSelchangeComboStatusFilter)
+	ON_BN_CLICKED(IDC_BUTTON_SEARCH, &CPageTransferLog::OnBnClickedButtonSearch)
+	ON_BN_CLICKED(IDC_BUTTON_EXPORT, &CPageTransferLog::OnBnClickedButtonExport)
+	ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CPageTransferLog::OnBnClickedButtonPrevPage)
+	ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CPageTransferLog::OnBnClickedButtonNextPage)
+END_MESSAGE_MAP()
+
+
+// CPageTransferLog 娑堟伅澶勭悊绋嬪簭
+
+BOOL CPageTransferLog::OnInitDialog()
+{
+	CDialogEx::OnInitDialog();
+
+	// TODO:  鍦ㄦ娣诲姞棰濆鐨勫垵濮嬪寲
+	SetTimer(1, 3000, nullptr);
+
+	// 涓嬫媺妗嗘帶浠�
+	InitStatusCombo();
+	InitTimeRangeCombo();
+
+	// 鏃ユ湡鎺т欢
+	InitDateTimeControls();
+
+	// 鎶ヨ〃鎺т欢
+	DWORD dwStyle = m_listCtrl.GetExtendedStyle();
+	dwStyle |= LVS_EX_FULLROWSELECT;
+	dwStyle |= LVS_EX_GRIDLINES;
+	m_listCtrl.SetExtendedStyle(dwStyle);
+
+	HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
+	ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL);
+
+	CString headers[] = { _T(""), _T("浠诲姟ID"), _T("鐘舵��"), _T("ClassID"), _T("鍒涘缓鏃堕棿"), _T("鍙栫墖鏃堕棿"), _T("鏀剧墖鏃堕棿"), _T("缁撴潫鏃堕棿"), _T("鎻忚堪") };
+	int widths[] = { 0, 80, 80, 100, 120, 120, 120, 120, 200 };
+	for (int i = 0; i < 9; ++i) {
+		m_listCtrl.InsertColumn(i, headers[i], LVCFMT_LEFT, widths[i]);
+	}
+	m_listCtrl.SetColumnWidth(8, LVSCW_AUTOSIZE_USEHEADER);
+
+	// 璇诲嚭鍒楀
+	CString strIniFile, strItem;
+	strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+	int width[8] = { 0, 80, 180, 80, 80, 100, 80, 180 };
+	for (int i = 0; i < 8; i++) {
+		strItem.Format(_T("Col_%d_Width"), i);
+		width[i] = GetPrivateProfileInt("TransferListCtrl", strItem, width[i], strIniFile);
+	}
+
+	// 璁$畻鎬婚〉鏁�
+	int nTotalRecords = TransferManager::getInstance().getTotalTransferCountAll();
+	m_nTotalPages = (nTotalRecords + PAGE_SIZE - 1) / PAGE_SIZE;
+	m_nCurPage = 1;
+
+	Resize();
+	LoadTransfers();
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	// 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
+}
+
+HBRUSH CPageTransferLog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+	if (nCtlColor == CTLCOLOR_STATIC) {
+		pDC->SetBkColor(m_crBkgnd);
+	}
+
+	if (m_hbrBkgnd == nullptr) {
+		m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
+	}
+
+	return m_hbrBkgnd;
+}
+
+void CPageTransferLog::OnDestroy()
+{
+	CDialogEx::OnDestroy();
+	if (m_hbrBkgnd != nullptr) {
+		::DeleteObject(m_hbrBkgnd);
+		m_hbrBkgnd = nullptr;
+	}
+	if (m_pObserver != nullptr) {
+		m_pObserver->unsubscribe();
+		m_pObserver = nullptr;
+	}
+
+	// 淇濆瓨鍒楀
+	CString strIniFile, strItem, strTemp;
+	strIniFile.Format(_T("%s\\configuration.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
+	CHeaderCtrl* pHeader = m_listCtrl.GetHeaderCtrl();
+	for (int i = 0; i < pHeader->GetItemCount(); i++) {
+		RECT rect;
+		pHeader->GetItemRect(i, &rect);
+		strItem.Format(_T("Col_%d_Width"), i);
+		strTemp.Format(_T("%d"), rect.right - rect.left);
+		WritePrivateProfileString("TransferListCtrl", strItem, strTemp, strIniFile);
+	}
+}
+
+void CPageTransferLog::OnSize(UINT nType, int cx, int cy)
+{
+	CDialogEx::OnSize(nType, cx, cy);
+	Resize();
+}
+
+void CPageTransferLog::OnTimer(UINT_PTR nIDEvent)
+{
+	if (nIDEvent == 1) {
+		KillTimer(1);
+		InitRxWindow();
+	}
+	CDialogEx::OnTimer(nIDEvent);
+}
+
+void CPageTransferLog::OnCbnSelchangeComboDatetime()
+{
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME);
+	int nIndex = pComboBox->GetCurSel();
+	int nCount = pComboBox->GetCount();
+	m_dateTimeStart.EnableWindow(nIndex == nCount - 1);
+	m_dateTimeEnd.EnableWindow(nIndex == nCount - 1);
+
+	// 鏇存柊鏃ユ湡杩囨护鍣ㄥ拰椤甸潰鏁版嵁
+	UpdateDateFilter();
+	LoadTransfers();
+}
+
+void CPageTransferLog::OnCbnSelchangeComboStatusFilter()
+{
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_STATUS_FILTER);
+	int nIndex = pComboBox->GetCurSel();
+	if (nIndex == 0) {
+		m_strStatus.clear();
+	}
+	else {
+		CString cstrText;
+		pComboBox->GetLBText(nIndex, cstrText);
+		m_strStatus = CT2A(cstrText);
+	}
+	LoadTransfers();
+}
+
+void CPageTransferLog::OnBnClickedButtonSearch()
+{
+	// 鑾峰彇鍏抽敭瀛楄緭鍏ユ鍐呭
+	CString strKeyword;
+	GetDlgItemText(IDC_EDIT_KEYWORD, strKeyword);
+	m_strKeyword = CT2A(strKeyword);
+
+	// 鏇存柊鏃ユ湡杩囨护鍣ㄥ拰椤甸潰鏁版嵁
+	UpdateDateFilter();
+	LoadTransfers();
+}
+
+void CPageTransferLog::OnBnClickedButtonExport()
+{
+	CFileDialog fileDialog(FALSE, _T("csv"), NULL, OFN_HIDEREADONLY, _T("CSV Files (*.csv)|*.csv||"));
+	if (fileDialog.DoModal() != IDOK) { 
+		return;
+	}
+
+	CStdioFile file;
+	if (!file.Open(fileDialog.GetPathName(), CFile::modeCreate | CFile::modeWrite | CFile::typeText)) {
+		AfxMessageBox(_T("鍒涘缓鏂囦欢澶辫触锛�"));
+		return;
+	}
+
+	CString strHeader = _T("浠诲姟ID,鐘舵��,ClassID,鍒涘缓鏃堕棿,鍙栫墖鏃堕棿,鏀剧墖鏃堕棿,缁撴潫鏃堕棿,鎻忚堪\n");
+	file.WriteString(strHeader);
+
+	for (int i = 0; i < m_listCtrl.GetItemCount(); ++i) {
+		CString row;
+		for (int j = 1; j <= 8; ++j) {
+			row += m_listCtrl.GetItemText(i, j);
+			if (j != 8) { 
+				row += ",";
+			}
+		}
+		row += "\n";
+		file.WriteString(row);
+	}
+	file.Close();
+}
+
+void CPageTransferLog::OnBnClickedButtonPrevPage()
+{
+	if (m_nCurPage > 1) {
+		m_nCurPage--;
+		UpdatePageData();
+	}
+}
+
+void CPageTransferLog::OnBnClickedButtonNextPage()
+{
+	if (m_nCurPage < m_nTotalPages) {
+		m_nCurPage++;
+		UpdatePageData();
+	}
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/PageTransferLog.h b/SourceCode/Bond/Servo/PageTransferLog.h
new file mode 100644
index 0000000..1a26e90
--- /dev/null
+++ b/SourceCode/Bond/Servo/PageTransferLog.h
@@ -0,0 +1,70 @@
+锘�#pragma once
+#include "afxdialogex.h"
+#include "ListCtrlEx.h"
+#include "TransferManager.h"
+
+
+// CPageTransferLog 瀵硅瘽妗�
+
+class CPageTransferLog : public CDialogEx
+{
+	DECLARE_DYNAMIC(CPageTransferLog)
+
+public:
+	CPageTransferLog(CWnd* pParent = nullptr);   // 鏍囧噯鏋勯�犲嚱鏁�
+	virtual ~CPageTransferLog();
+
+private:
+	COLORREF m_crBkgnd;
+	HBRUSH m_hbrBkgnd;
+	IObserver* m_pObserver;
+
+	// 鎼滅储鍏抽敭瀛�
+	std::string m_strStatus;
+	std::string m_strKeyword;
+
+	// 椤电爜
+	int m_nCurPage;
+	int m_nTotalPages;
+
+	// 鏃ユ湡
+	char m_szTimeStart[64];
+	char m_szTimeEnd[64];
+
+	// 鎺т欢
+	CDateTimeCtrl m_dateTimeStart;
+	CDateTimeCtrl m_dateTimeEnd;
+	CListCtrlEx m_listCtrl;
+
+	void InitRxWindow();
+	void Resize();
+	void InitStatusCombo();
+	void InitTimeRangeCombo();
+	void InitDateTimeControls();
+	void LoadTransfers();
+	void UpdatePageData();
+	void UpdatePageControls();
+	void UpdateDateFilter();
+	void FillDataToListCtrl(const std::vector<TransferData>& vecData);
+	void InsertTransferData(const TransferData& data);
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+	enum { IDD = IDD_PAGE_TRANSFER_LOG };
+#endif
+
+protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 鏀寔
+	virtual BOOL OnInitDialog();
+	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+	afx_msg void OnDestroy();
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnTimer(UINT_PTR nIDEvent);
+	afx_msg void OnCbnSelchangeComboDatetime();
+	afx_msg void OnCbnSelchangeComboStatusFilter();
+	afx_msg void OnBnClickedButtonSearch();
+	afx_msg void OnBnClickedButtonExport();
+	afx_msg void OnBnClickedButtonPrevPage();
+	afx_msg void OnBnClickedButtonNextPage();
+	DECLARE_MESSAGE_MAP()
+};
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index 140d51f..eedce8b 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index 11eb375..e776f64 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -304,6 +304,7 @@
     <ClInclude Include="MapPosWnd.h" />
     <ClInclude Include="Model.h" />
     <ClInclude Include="PageRobotCmd.h" />
+    <ClInclude Include="PageTransferLog.h" />
     <ClInclude Include="PortConfigurationDlg.h" />
     <ClInclude Include="ProductionLogManager.h" />
     <ClInclude Include="Resource.h" />
@@ -424,6 +425,7 @@
     <ClCompile Include="MapPosWnd.cpp" />
     <ClCompile Include="Model.cpp" />
     <ClCompile Include="PageRobotCmd.cpp" />
+    <ClCompile Include="PageTransferLog.cpp" />
     <ClCompile Include="PortConfigurationDlg.cpp" />
     <ClCompile Include="ProductionLogManager.cpp" />
     <ClCompile Include="SECSRuntimeManager.cpp" />
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index 1a8bb3f..790d462 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -150,6 +150,7 @@
     </ClCompile>
     <ClCompile Include="PortConfigurationDlg.cpp" />
     <ClCompile Include="TransferManager.cpp" />
+    <ClCompile Include="PageTransferLog.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="AlarmManager.h" />
@@ -305,6 +306,7 @@
     </ClInclude>
     <ClInclude Include="PortConfigurationDlg.h" />
     <ClInclude Include="TransferManager.h" />
+    <ClInclude Include="PageTransferLog.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Servo.rc" />
diff --git a/SourceCode/Bond/Servo/TransferManager.cpp b/SourceCode/Bond/Servo/TransferManager.cpp
index bfcf457..6079a79 100644
--- a/SourceCode/Bond/Servo/TransferManager.cpp
+++ b/SourceCode/Bond/Servo/TransferManager.cpp
@@ -135,21 +135,37 @@
 
 // 插入测试搬运记录
 void TransferManager::insertTestTransferRecord() {
-    TransferData data;
-    data.strClassID = "Task-20240529-001";
-    data.strStatus = "Running";
-    data.strCreateTime = "2024-05-29 10:30:00";
-    data.strPickTime = "2024-05-29 10:31:00";
-    data.strPlaceTime = "2024-05-29 10:32:00";
-    data.strEndTime = "2024-05-29 10:33:00";
-    data.strDescription = "搬运动作:从 Port1 取片 → Port2 放片";
+    if (nullptr != m_pDB) { 
+        int nCount = 10000;
+        for (int i = 0; i < nCount; ++i) {
+            TransferData data;
+            data.strClassID = "T-" + std::to_string(1000 + i);
+            data.strStatus = statusToString(static_cast<TransferStatus>(i % 5));
 
-    int nRecordId = -1;
-    if (TransferManager::getInstance().addTransferRecord(data, nRecordId)) {
-        std::cout << "插入成功,记录 ID = " << nRecordId << std::endl;
-    }
-    else {
-        std::cerr << "插入失败!" << std::endl;
+            std::time_t now = std::time(nullptr) + i * 60;  // 每条记录间隔1分钟
+
+            std::tm tmCreate = {}, tmPick = {}, tmPlace = {}, tmEnd = {};
+            localtime_s(&tmCreate, &now);
+            localtime_s(&tmPick, &(now += 60));
+            localtime_s(&tmPlace, &(now += 60));
+            localtime_s(&tmEnd, &(now += 60));
+
+            char szTime[64];
+            strftime(szTime, sizeof(szTime), "%Y-%m-%d %H:%M:%S", &tmCreate);
+            data.strCreateTime = szTime;
+            strftime(szTime, sizeof(szTime), "%Y-%m-%d %H:%M:%S", &tmPick);
+            data.strPickTime = szTime;
+            strftime(szTime, sizeof(szTime), "%Y-%m-%d %H:%M:%S", &tmPlace);
+            data.strPlaceTime = szTime;
+            strftime(szTime, sizeof(szTime), "%Y-%m-%d %H:%M:%S", &tmEnd);
+            data.strEndTime = szTime;
+
+            data.strDescription = "Mock transfer task " + std::to_string(i);
+            int nRecordId = 0;
+            addTransferRecord(data, nRecordId);
+        }
+
+        std::cout << "[Mock] 成功插入 " << nCount << " 条测试搬运记录。" << std::endl;
     }
 }
 
@@ -221,13 +237,13 @@
 
         TransferData data;
         data.nRecordId = std::stoi(row[0]);
-        data.strClassID = row[1];
-        data.strStatus = row[2];
+        data.strClassID = utf8ToAnsi(row[1]);
+        data.strStatus = utf8ToAnsi(row[2]);
         data.strCreateTime = row[3];
         data.strPickTime = row[4];
         data.strPlaceTime = row[5];
         data.strEndTime = row[6];
-        data.strDescription = row[7];
+        data.strDescription = utf8ToAnsi(row[7]);
 
         records.push_back(data);
     }
@@ -247,13 +263,13 @@
 	auto results = m_pDB->fetchResults(oss.str());
 	if (!results.empty() && results[0].size() == 8) {
 		data.nRecordId = std::stoi(results[0][0]);
-		data.strClassID = results[0][1];
-		data.strStatus = results[0][2];
+		data.strClassID = utf8ToAnsi(results[0][1]);
+		data.strStatus = utf8ToAnsi(results[0][2]);
 		data.strCreateTime = results[0][3];
 		data.strPickTime = results[0][4];
 		data.strPlaceTime = results[0][5];
 		data.strEndTime = results[0][6];
-		data.strDescription = results[0][7];
+		data.strDescription = utf8ToAnsi(results[0][7]);
 	}
 	return data;
 }
@@ -278,13 +294,13 @@
 		if (row.size() != 8) continue;
 		TransferData data;
 		data.nRecordId = std::stoi(row[0]);
-		data.strClassID = row[1];
-		data.strStatus = row[2];
+		data.strClassID = utf8ToAnsi(row[1]);
+		data.strStatus = utf8ToAnsi(row[2]);
 		data.strCreateTime = row[3];
 		data.strPickTime = row[4];
 		data.strPlaceTime = row[5];
 		data.strEndTime = row[6];
-		data.strDescription = row[7];
+		data.strDescription = utf8ToAnsi(row[7]);
 		records.push_back(data);
 	}
 	return records;
@@ -299,7 +315,7 @@
 
     std::ostringstream oss;
     oss << "SELECT record_id, class_id, status, create_time, pick_time, place_time, end_time, description "
-        << "FROM transfers WHERE status = '" << status << "' "
+        << "FROM transfers WHERE status = '" << ansiToUtf8(status) << "' "
         << "ORDER BY create_time DESC";
 
     auto results = m_pDB->fetchResults(oss.str());
@@ -308,13 +324,13 @@
 
         TransferData data;
         data.nRecordId = std::stoi(row[0]);
-        data.strClassID = row[1];
-        data.strStatus = row[2];
+        data.strClassID = utf8ToAnsi(row[1]);
+        data.strStatus = utf8ToAnsi(row[2]);
         data.strCreateTime = row[3];
         data.strPickTime = row[4];
         data.strPlaceTime = row[5];
         data.strEndTime = row[6];
-        data.strDescription = row[7];
+        data.strDescription = utf8ToAnsi(row[7]);
 
         records.push_back(data);
     }
@@ -348,8 +364,8 @@
 //	return records;
 //}
 
-// 获取符合条件的记录总数
-int TransferManager::getTotalTransferCount() {
+// 获取记录总数
+int TransferManager::getTotalTransferCountAll() {
     if (!m_pDB) {
         return 0;
     }
@@ -370,7 +386,7 @@
 }
 
 // 获取符合条件的记录总数
-int TransferManager::getTotalTransferCount(const TransferData& filter) {
+int TransferManager::getFilteredTransferCount(const TransferData& filter) {
     if (!m_pDB) {
         return 0;
     }
@@ -380,12 +396,12 @@
 
     // 状态筛选(完全匹配)
     if (!filter.strStatus.empty()) {
-        oss << " AND status = '" << filter.strStatus << "'";
+        oss << " AND status = '" << ansiToUtf8(filter.strStatus) << "'";
     }
 
     // 描述关键字模糊匹配
     if (!filter.strDescription.empty()) {
-        oss << " AND description LIKE '%" << filter.strDescription << "%'";
+        oss << " AND description LIKE '%" << ansiToUtf8(filter.strDescription) << "%'";
     }
 
     // 时间范围筛选
@@ -422,10 +438,10 @@
 
     // 条件拼接(与 getTotalTransferCount 保持一致)
     if (!filter.strStatus.empty()) {
-        oss << " AND status = '" << filter.strStatus << "'";
+        oss << " AND status = '" << ansiToUtf8(filter.strStatus) << "'";
     }
     if (!filter.strDescription.empty()) {
-        oss << " AND description LIKE '%" << filter.strDescription << "%'";
+        oss << " AND description LIKE '%" << ansiToUtf8(filter.strDescription) << "%'";
     }
     if (!filter.strCreateTime.empty()) {
         oss << " AND create_time >= '" << filter.strCreateTime << "'";
@@ -446,13 +462,13 @@
 
         TransferData data;
         data.nRecordId = std::stoi(row[0]);
-        data.strClassID = row[1];
-        data.strStatus = row[2];
+        data.strClassID = utf8ToAnsi(row[1]);
+        data.strStatus = utf8ToAnsi(row[2]);
         data.strCreateTime = row[3];
         data.strPickTime = row[4];
         data.strPlaceTime = row[5];
         data.strEndTime = row[6];
-        data.strDescription = row[7];
+        data.strDescription = utf8ToAnsi(row[7]);
 
         records.push_back(data);
     }
diff --git a/SourceCode/Bond/Servo/TransferManager.h b/SourceCode/Bond/Servo/TransferManager.h
index ed1bbf6..9dde8c8 100644
--- a/SourceCode/Bond/Servo/TransferManager.h
+++ b/SourceCode/Bond/Servo/TransferManager.h
@@ -109,24 +109,24 @@
     //std::vector<TransferData> getTransfers(int startPosition, int count);
 
     /**
-	 * 获取符合条件的记录总数
+	 * 获取记录总数
      */
-    int getTotalTransferCount();
-
-    /**
-	 * 分页获取符合条件的搬运记录
-	 * @param filter 过滤条件
-	 * @param pageNum 页码
-	 * @param pageSize 每页记录数
-     */
-    std::vector<TransferData> getTransfers(const TransferData& filter, int pageNum, int pageSize);
+    int getTotalTransferCountAll();
 
 	/**
 	 * 获取符合条件的记录总数
 	 * @param filter 过滤条件
 	 * @return 符合条件的记录总数
 	 */
-    int getTotalTransferCount(const TransferData& filter);;
+    int getFilteredTransferCount(const TransferData& filter);
+
+    /**
+     * 分页获取符合条件的搬运记录
+     * @param filter 过滤条件
+     * @param pageNum 页码
+     * @param pageSize 每页记录数
+     */
+    std::vector<TransferData> getTransfers(const TransferData& filter, int pageNum, int pageSize);
 
     /**
      * 清理早于某一时间的搬运记录
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index f530d52..05eec9c 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ

--
Gitblit v1.9.3