#收银台
开发环境:C/C++、VS2017、MySQL Server 8.0
项目功能:实现小型超市管理货物和收银。
老板具有管理员超级权限可以对员工进行管理。
员工可以对货物仓库进行管理。
收银功能的简单实现。
项目描述:该项目是借助Duilib库实现了小型超市管理货物和收银功能,使用C++类
封装MySQL的一些API,并配合Duilib程序Win32消息循环进行事件响应,
分别给员工和管理员提供不同的操作,员工具有仓库管理和收银的功能,而管
理员加了管理员工的操作。针对不同用户登陆区分不同的功能。
收银台:大家每天都在使用,为人类生活提供方便。
每写一个项目都是成长的经历,当然每次都要进行环境的配置。
我就不介绍了,一切参照Duilib入门教程汇总
这个项目做起来没有太大的现实意义,因为我深知可以买到更好的,而这个项目做起来的意义在于对与我个人能力的提高,Duilib 是一款强大的界面开发工具,这个项目我学会了它的使用方法,虽然过程是艰辛的,可是收获真的不少。
下面我介绍一下这个项目:
核心功能:
1.登陆界面。
2.管理员操作界面。
3.售货员操作界面。
具体实现:如下
超级管理员和售货员根据自己的账号和密码进行登录。
输入账号以及密码后,根据身份的不同,进入不同界面,进行相应的操作。
登陆密码判断是根据数据库存储内容进行判断,具体登陆窗口主逻辑代码如下:
void LogInWnd::LogIn()
{
//从编辑框获取用户名和密码
//需要强转
CEditUI * pEditUserName = (CEditUI*)m_PaintManager.FindControl(_T("EDIT_USER_NAME"));
CDuiString strUserName = pEditUserName->GetText();
CEditUI * pEditPassword = (CEditUI*)m_PaintManager.FindControl(_T("EDIT_USER_PASSWORD"));
CDuiString strUserPassword = pEditPassword->GetText();
//查询数据库,检测用户是否存在
string strSQL("select * from ceshi where name1 = '");
//ascII UNICODE
strSQL += UnicodeToANSI(strUserName);
strSQL += "';";
vector<vector<string>> vRet = m_pMySQL->Select(strSQL);
if (vRet.empty())
{
MessageBox(m_hWnd, _T("用户名错误"), _T("Cashier"), IDOK);
return;
}
string userpassward = UnicodeToANSI(strUserPassword);
if (userpassward != vRet[0][4])
{
MessageBox(m_hWnd, _T("用户密码错误"), _T("Cashier"), IDOK);
return;
}
//隐藏登陆窗口
ShowWindow(false);
if (vRet[0][5] == "管理员")
{
//创建主窗口
MainWnd mainWnd(m_pMySQL);
mainWnd.Create(NULL, _T("MainWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
mainWnd.CenterWindow();
mainWnd.ShowModal();
}
else
{
//创建主窗口
CCashierWnd mainWnd(m_pMySQL);
mainWnd.Create(NULL, _T("CashierWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
mainWnd.CenterWindow();
mainWnd.ShowModal();
}
// 控件 tablayoue
}
查询员工基本信息
添加新员工,员工离职后,删除员工信息,员工信息变更时,更新员工信息
界面操作使用DuiDersigner_d可视化工具搭建界面,操作XML文件。
<Window size="959,600" sizebox="4,4,4,4" caption="0,0,0,32" mininfo="600,400">
<VerticalLayout width="907" height="675" bkimage="C:\Users\Administrator\Desktop\20161019212101_52148.jpg" bkcolor="#FFF0F0F0" bkcolor2="#FFAAAAA0">
<HorizontalLayout height="32" bkcolor="#FF57FF80" bkcolor2="#FFAAAAA0">
<Button name="BTN_MIN" text="最小化" float="true" pos="855,0,0,0" width="53" height="32" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="file='skin\sys_dlg_min.png'
source='26,0,52,17'" pushedimage="file='skin\sys_dlg_min.png'
source='0,0,26,17'" focusedimage="file='skin\sys_dlg_min.png'
source='52,0,78,17'" />
<Button name="BTN_CLOSE" text="关闭" float="true" pos="907,0,0,0" width="53" height="32" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="file='skin\sys_dlg_close.png'
source='90,0,135,17'" pushedimage="file='skin\sys_dlg_close.png'
source='0,0,45,17'" focusedimage="file='skin\sys_dlg_close.png'
source='45,0,90,17'" />
HorizontalLayout>
<VerticalLayout width="958" height="522">
<Edit name="username" text="page1" width="113" height="34" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" />
<Combo name="usergender" float="true" pos="112,2,0,0" width="140" height="32" bkimage="skin\GameRes\Combo_nor.bmp" itemtextcolor="#FF000000" itemselectedtextcolor="#FF000000" itemselectedbkcolor="#FFC1E3FF" itemhottextcolor="#FF000000" itemhotbkcolor="#FFE9F5FF" itemdisabledtextcolor="#FFCCCCCC" itemdisabledbkcolor="#FFFFFFFF" normalimage="file='ComboBox/Combo_nor.bmp'" hotimage="file='skin\GameRes/Combo_nor.bmp' " pushedimage="skin\GameRes/Combo_over.bmp" dropboxsize="0,150">
<ListLabelElement text="男"/>
<ListLabelElement text="女"/>
Combo>
<Edit name="userbirthday" text="2012-02-02" float="true" pos="252,2,0,0" width="103" height="33" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" />
<Combo name="position" float="true" pos="355,2,0,0" width="140" height="31" bkimage="skin\GameRes\Combo_nor.bmp" itemtextcolor="#FF000000" itemselectedtextcolor="#FF000000" itemselectedbkcolor="#FFC1E3FF" itemhottextcolor="#FF000000" itemhotbkcolor="#FFE9F5FF" itemdisabledtextcolor="#FFCCCCCC" itemdisabledbkcolor="#FFFFFFFF" normalimage="file='ComboBox/Combo_nor.bmp'" hotimage="file='skin\GameRes/Combo_nor.bmp' " pushedimage="skin\GameRes/Combo_over.bmp" dropboxsize="0,150">
<ListLabelElement text="管理员"/>
<ListLabelElement text="售货员"/>
Combo>
<Edit name="telphone" text="15353533535" float="true" pos="517,3,0,0" width="101" height="33" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" />
<Edit name="salary" text="11" float="true" pos="633,3,0,0" width="95" height="33" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" />
<List name="ListceshiInfo" float="true" pos="10,40,0,0" width="720" height="500" bkcolor="#FFFFFFFF" itemtextcolor="#FF000000" itembkcolor="#FFE2DDDF" itemselectedtextcolor="#FF000000" itemselectedbkcolor="#FFC1E3FF" itemhottextcolor="#FF000000" itemhotbkcolor="#FFE9F5FF" itemdisabledtextcolor="#FFCCCCCC" itemdisabledbkcolor="#FFFFFFFF" headerbkimage="skin\ListRes/list_header_bg.png" vscrollbar="true" hscrollbar="true">
<ListHeader name="domain" bkimage="skin\ListRes/list_header_bg.png">
<ListHeaderItem text="姓名" width="120" height="23" minwidth="16" textcolor="#FF000000" sepwidth="1" align="center" hotimage="List/list_header_hot.png" pushedimage="List/list_header_pushed.png" sepimage="List/list_header_sep.png" />
<ListHeaderItem text="性别" width="120" height="23" minwidth="16" textcolor="#FF000000" sepwidth="1" align="center" hotimage="List/list_header_hot.png" pushedimage="List/list_header_pushed.png" sepimage="List/list_header_sep.png" />
<ListHeaderItem text="生日" width="120" height="23" minwidth="16" textcolor="#FF000000" sepwidth="1" align="center" hotimage="List/list_header_hot.png" pushedimage="List/list_header_pushed.png" sepimage="List/list_header_sep.png" />
<ListHeaderItem text="职务" width="120" height="23" minwidth="16" textcolor="#FF000000" sepwidth="1" align="center" hotimage="List/list_header_hot.png" pushedimage="List/list_header_pushed.png" sepimage="List/list_header_sep.png" />
<ListHeaderItem text="电话" width="120" height="23" minwidth="16" textcolor="#FF000000" sepwidth="1" align="center" hotimage="List/list_header_hot.png" pushedimage="List/list_header_pushed.png" sepimage="List/list_header_sep.png" />
<ListHeaderItem text="薪资" width="120" height="23" minwidth="16" textcolor="#FF000000" sepwidth="1" align="center" hotimage="List/list_header_hot.png" pushedimage="List/list_header_pushed.png" sepimage="List/list_header_sep.png" />
ListHeader>
List>
<Button name="BTN_SELECT" text="查询" float="true" pos="795,90,0,0" width="102" height="45" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="skin\GameRes\button_nor.bmp" hotimage="skin\GameRes\button_over.bmp" pushedimage="skin\GameRes\button_down.bmp" />
<Button name="BTN_INSERT" text="插入" float="true" pos="795,160,0,0" width="102" height="45" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="skin\GameRes\button_nor.bmp" hotimage="skin\GameRes\button_over.bmp" pushedimage="skin\GameRes\button_down.bmp" />
<Button name="BTN_UPDATE" text="更新" float="true" pos="795,230,0,0" width="102" height="45" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="skin\GameRes\button_nor.bmp" hotimage="skin\GameRes\button_over.bmp" pushedimage="skin\GameRes\button_down.bmp" />
<Button name="BTN_SELL_RECORD" text="销售记录" float="true" pos="795,372,0,0" width="102" height="45" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="skin\GameRes\button_nor.bmp" hotimage="skin\GameRes\button_over.bmp" pushedimage="skin\GameRes\button_down.bmp" />
<Button name="BTN_DELETE" text="删除" float="true" pos="795,300,0,0" width="102" height="45" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" align="center" normalimage="skin\GameRes\button_nor.bmp" hotimage="skin\GameRes\button_over.bmp" pushedimage="skin\GameRes\button_down.bmp" />
<Combo name="COMOB_SELECT" float="true" pos="780,52,0,0" width="111" height="29" itemtextcolor="#FF000000" itemselectedtextcolor="#FF000000" itemselectedbkcolor="#FFC1E3FF" itemhottextcolor="#FF000000" itemhotbkcolor="#FFE9F5FF" itemdisabledtextcolor="#FFCCCCCC" itemdisabledbkcolor="#FFFFFFFF" normalimage="skin\GameRes\Combo_nor.bmp" hotimage="skin\GameRes\Combo_over.bmp" dropboxsize="0,150">
<ListLabelElement text=""/>
<ListLabelElement text="姓名"/>
<ListLabelElement text="职务"/>
Combo>
VerticalLayout>
<VerticalLayout width="800" height="522">
<Edit name="testpage2" text="page2" width="113" height="38" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" />
VerticalLayout>
VerticalLayout>
Window>
按照条件查询商品的信息
商品入库,过期商品的删除,商品信息更新,按照日期查询商品销售情况
经过各种界面的绘制熟悉了各个控件的使用,熟悉了XML文件的语法格式。
但是只有界面是不行的,还需要对控件以及数据库进行连接进行消息响应,这里使用Win32消息循环进行处理。
#pragma once
#include "Common.h"
#include "MySQL.h"
class CCashierWnd : public WindowImplBase
{
public:
CCashierWnd(MySQL* pMySQL = nullptr)
:m_pMySQL(pMySQL)
{}
//virtual void Notify(TNotifyUI& msg);
//父类WindowImplBase 提供的
protected:
//xml文件对应的目录
virtual CDuiString GetSkinFolder();
//xml文件的名字
virtual CDuiString GetSkinFile();
//窗口类的名字: 在注册窗口时必须提供
virtual LPCTSTR GetWindowClassName(void) const;
virtual void Notify(TNotifyUI& msg);
void SelectGoods();
void AddGoodsCount();
void SubGoodsCount();
void IsertGoodsList();
void CancelOrder();
void CommitOrder();
private:
MySQL* m_pMySQL;
};
前面看了这么多一切都离不开数据库
每种操作都建立在数据库上
使用C++封装操作数据库的类
#include "MySQL.h"
using namespace std;
MySQL::MySQL()
{
_mySQL = mysql_init(nullptr);
}
bool MySQL::ConnectMySQL(const char* host, const char* user, const char*passward, const char* bdName)
{
if (!mysql_real_connect(_mySQL, host, user, passward, bdName, 3306, nullptr, 0)) //
//mysql_real_connect() :连接MYSQL,有8个参数,
//第一个参数是MYSQL变量的地址,
//第二个参数是主机名或IP地址,如果“host”是NULL或字符串"localhost"或“127.0.0.1”,连接将被视为与本地主机的连接,
//第三个参数是MySQL登录ID
//第四个参数是用户的密码
//第五个参数是数据库名称
//第六个参数是TCP / IP连接的端口号
//第七个参数一般为NULL,不需要了解
//第八个参数一般为0,不需要了解
{
cout << "数据库连接失败" << endl;
return false;
}
mysql_query(_mySQL, "set names 'gbk'");
return true;
}
vector<vector<string>> MySQL::Select(const string& strSQL)
{
vector<vector<string>> vvRet;
if (mysql_query(_mySQL, strSQL.c_str()))
{
//SQL命令响应失败
cout << mysql_error(_mySQL) << endl;
return vvRet;
}
//配合其他函数获取查询记录集
MYSQL_RES * mySQLRES = mysql_store_result(_mySQL); //将查询到的结果集储存到result中
//返回值具有多个结果的MYSQL_RES结果集合,出现错误返回NULL
if (mySQLRES== nullptr)
{
cout << mysql_error(_mySQL) << endl;
return vvRet;
}
int num = mysql_num_fields(mySQLRES); //将结果集列数存放到num中 字段个数
MYSQL_ROW mysqlRow;
while ((mysqlRow = mysql_fetch_row(mySQLRES))) //遇到最后一行,则中止循环
{
vector<string> vItem;
for (int i = 0; i < num; i++) //利用for循环,输出该行的每一列
{
vItem.push_back(mysqlRow[i]);
//cout << mysqlRow[i] << "\t"; //mysqlRow是MYSQL_ROW变量,可以当做数组使用,i为列数
}
vvRet.push_back(vItem);
////////////cout << endl;
}
mysql_free_result(mySQLRES); //释放结果集所占用的内存 必须操作
//mysql_close(&ceshi); //关闭与mysql的连接
return vvRet;
}
bool MySQL::Insert(const string& strSQL)
{
if (mysql_query(_mySQL, strSQL.c_str()))
{
//SQL命令响应失败
cout << mysql_error(_mySQL) << endl;
return false;
}
return true;
}
bool MySQL::Delete(const string& strSQL)
{
if (mysql_query(_mySQL, strSQL.c_str()))
{
//SQL命令响应失败
cout << mysql_error(_mySQL) << endl;
return false;
}
return true;
}
bool MySQL::UpDate(const string& strSQL)
{
if (mysql_query(_mySQL, strSQL.c_str()))
{
//SQL命令响应失败
cout << mysql_error(_mySQL) << endl;
return false;
}
return true;
}
MySQL::~MySQL()
{
}
进而里面的具体功能操作只需要构造相应的SQL语句进行相应的数据库操作。
对于相应消息的响应我们这样做:
- 让CDuiFramWnd类继承INotifyUI。
- 按钮创建成功后,将按钮
- 重写INotifyUI类的Notify纯虚函数,在该函数中用户捕获其想要处理的消息。
void MainWnd::Notify(TNotifyUI& msg)
{
//拦截消息
CDuiString strName = msg.pSender->GetName();
if (msg.sType == _T("click"))
{
if (strName == _T("BTN_CLOSE"))
Close();
else if (strName == _T("BTN_MIN"))
::SendMessage(m_hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
else if (strName == _T("BTN_SELECT"))
//MessageBox(NULL, _T("SelectBTN"), _T("Cashier"), IDOK);
SelectceshiInfo();
else if (strName == _T("BTN_INSERT"))
//MessageBox(NULL, _T("BTN_INSERT"), _T("Cashier"), IDOK);
InsertceshiInfo();
else if (strName == _T("BTN_DELETE"))
Deleteceshiinfo();
else if (strName == _T("BTN_UPDATE"))
MessageBox(NULL, _T("BTN_UPDATE"), _T("Cashier"), IDOK);
else if (strName == _T("BTN_SELL_RECORD"))
MessageBox(NULL, _T("BTN_SELL_RECORD"), _T("Cashier"), IDOK);
}
else if (msg.sType == _T("selectchanged"))
{
CTabLayoutUI* pTab = (CTabLayoutUI*)m_PaintManager.FindControl(_T("tablayout"));
if (strName == _T("OPTION_EMPLOYEE"))
pTab->SelectItem(0);
else
pTab->SelectItem(1);
}
else if (msg.sType == _T("windowinit"))
{
//窗口在创建期间,将一些空间初始化
CComboBoxUI* pGender = (CComboBoxUI*)m_PaintManager.FindControl(_T("usergender"));
pGender->SelectItem(0);
CComboBoxUI* pPosition = (CComboBoxUI*)m_PaintManager.FindControl(_T("position"));
pPosition->SelectItem(0);
CComboBoxUI* pSelect = (CComboBoxUI*)m_PaintManager.FindControl(_T("COMOB_SELECT"));
pSelect->SelectItem(0);
}
}
消息捕获成功之后我们只需要构造相应的处理函数进行处理这里我只举部分例子:
void MainWnd::SelectceshiInfo()
{
string strSQL("select * from ceshi ");
CComboBoxUI* pCombo = (CComboBoxUI*)m_PaintManager.FindControl(_T("COMOB_SELECT"));
CDuiString strStyle = pCombo->GetText();
if (strStyle == _T("无"))
strSQL += ";";
else if (strStyle == _T("名字"))
{
strSQL += "where name1 = '";
CDuiString strName = ((CEditUI*)m_PaintManager.FindControl(_T("username")))->GetText();
if (strName.IsEmpty())
{
MessageBox(m_hWnd, _T("请输入查询用户的名字"), _T("Cashier"), IDOK);
}
strSQL += UnicodeToANSI(strName);
strSQL += "';";
}
else if (strStyle == _T("性别"))
;
else if (strStyle == _T("薪资"))
;
vector<vector<string>> vRet = m_pMySQL->Select(strSQL);
if (vRet.empty())
return;
CListUI* pListUI = (CListUI*)m_PaintManager.FindControl(_T("ListceshiInfo"));
pListUI->RemoveAll();
for (size_t i = 0; i < vRet.size(); ++i)
{
vector<string>& strItm = vRet[i];
CListTextElementUI* pData = new CListTextElementUI;
pData->SetAttribute(_T("align"), _T("center"));
pListUI->Add(pData);
//名字
pData->SetText(0, ANSIToUnicode(strItm[1]));
pData->SetText(1, ANSIToUnicode(strItm[2]));
pData->SetText(2, ANSIToUnicode(strItm[3]));
pData->SetText(3, ANSIToUnicode(strItm[4]));
pData->SetText(4, ANSIToUnicode(strItm[5]));
pData->SetText(5, ANSIToUnicode(strItm[6]));
pData->SetText(6, ANSIToUnicode(strItm[7]));
}
}
void MainWnd::InsertceshiInfo()
{
CDuiString strName = ((CEditUI*)m_PaintManager.FindControl(_T("username")))->GetText();
CDuiString strGender = ((CComboBoxUI*)m_PaintManager.FindControl(_T("usergender")))->GetText();
CDuiString strBirthday = ((CEditUI*)m_PaintManager.FindControl(_T("userbirthday")))->GetText();
CDuiString strPosition = ((CComboBoxUI*)m_PaintManager.FindControl(_T("position")))->GetText();
CDuiString strTel = ((CEditUI*)m_PaintManager.FindControl(_T("telphone")))->GetText();
CDuiString strSalary = ((CEditUI*)m_PaintManager.FindControl(_T("salary")))->GetText();
CListUI* pListUI = (CListUI*)m_PaintManager.FindControl(_T("ListceshiInfo"));
char szCount[32] = { 0 };
_itoa(pListUI->GetCount() + 1, szCount, 10);
//pListUI->GetCurSel();
//构造SQL命令
string strSQL("insert into ceshi values(");
strSQL += szCount;
strSQL += ",'";
strSQL += UnicodeToANSI(strName);
strSQL += "','";
strSQL += UnicodeToANSI(strGender);
strSQL += "','";
strSQL += UnicodeToANSI(strBirthday);
strSQL += "','000000','";
strSQL += UnicodeToANSI(strPosition);
strSQL += "','";
strSQL += UnicodeToANSI(strTel);
strSQL += "','";
strSQL += UnicodeToANSI(strSalary);
strSQL += "');";
//响应SQL命令
m_pMySQL->Insert(strSQL);
//将该员工的信息插入到List
CListTextElementUI* pItem = new CListTextElementUI;
pListUI->Add(pItem);
pItem->SetText(0, strName);
pItem->SetText(1, strGender);
pItem->SetText(2, strBirthday);
pItem->SetText(3, strPosition);
pItem->SetText(4, strTel);
pItem->SetText(5, strSalary);
}
整体来看这个项目对我有挺深刻的学习体会,一个学习过程是非常重要的,项目本身并不重要,在于这个过程。
全部源码详见github链接