要求:在windows环境下使用Vistual studio以C/C++语言编译一个具有基础框架的客户信息管理系统。必须使用到封装、继承、map容器、SQL数据库技术。
--------------------------------------------------我 是 分 割 线-------------------------------------------------------------
未经过UI处理的基础系统功能效果:
注:本系统为初始demo、代码未经过优化。
【图1-1】
【图1-2】
【图1-3】
【图1-4】
【图1-5】
【图1-6】
【图1-7】
【图1-8】
-----------------------------------------我 是 分 割 线-------------------------------------------------------------------
不多说,解下来我将详细讲解整个过程的框架->细节;
先来说步骤:根据客户需求->提出界面范围->制定每一个界面的包含功能->确定功能可实行(搭建用户端体验感。)
第一个界面:登录界面(登录设计相关|密码设计相关|找回密码|注册功能|回退设计);
第二个界面:找回密码界面(找回密码key指令|对比容器选择map|回退设计);
第三个界面:注册界面(设置key|key的矛盾性分析|回退设计);
第四个界面:初始用户界面(查询功能设计|超级用户设计|回退设计);
第五个界面:指令开关(关于超级用户使用触发指令需求|回退设计);
第六个界面:超级用户界面(数据可修改设计|回退设计);
第七个界面:不同功能的具体显示界面(功能显示当量设计);
-----------------------------------------------------我是分割线-------------------------------------------------------------
登录界面设计:
①先来创建关于登录界面的类:
【图2-1】
公有制的函数void loginmyfunc()代码如下:
#include "login.h"
#include <map>
void login::loginmyfunc()
{
system("cls"); //清空整个屏幕、为重新回退至登录界面用
biankuang::paintWindow(10, 0, 35, 25); //外围框
biankuang::gtoxy(36,2);//鼠标光标位置 位于整体框的中间上边
printf("%s", "|再充一万 你会更强|");//打印广告
button X; X.show(35, 4, 10, 2, "账号:"); //账号框 起始位置35,离顶4字节,长10字
//节,宽两字节
button A; A.show(35, 7, 10, 2, "密码:");//密码框 起始位置35 6
buttonsert C; C.show(20, 16, 6, 2, "按回车登录");
buttonsert D; D.show(55, 16, 6, 2, "按F5键注册");
biankuang::gtoxy(39, 20); cout << "忘记密码(F3)";
biankuang::gtoxy(39, 22); cout << "退出程序(ESC)";
biankuang::gtoxy(36,5);//最后光标落在账号框的旁边
}
【图2-2】
button X; X是button的实例化对象,而类button功能将传进来的参数进行赋值,随后发送至可以画图的函数。button的类代码如下:
#pragma once
#include <string>
#include "biankuang.h"
class button
{
public:
void show (int x=0, int y=0, int w=0, int h=0,const char* name=nullptr);
private:
char aaa[50];
int x;
int y;
int w;
int h;
};
【图2-3】
公有制成员函数show的代码如下:
【图2-4】
画图类biankuang源代码如下:
#include "biankuang.h"
void biankuang::gtoxy(int x, int y) //在biankuang类中是静态函数
{
COORD pos = { x,y };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut, pos);
}
void biankuang::paintWindow(int x, int y, int w, int h)//在biankuang类中是静态函数
{
int j, i;
gtoxy(x, y); //╰╯│╮─╭
printf("╭");
for (j = 0; j < w; j++)
{
printf(" ─");
}
gtoxy(x + 2 * w, y);
printf("╮");
for (i = 0; i < h; i++)
{
gtoxy(x, y + i + 1);
printf("│\n");
}
for (i = 0; i < h; i++)
{
gtoxy(x + 2 * w, y + i + 1);
printf("│\n");
}
gtoxy(x, y + h);
printf("╰");
for (j = 0; j < w; j++)
{
printf(" ─");
}
gtoxy(x + 2 * w, y + h);
printf("╯\n");
}
【图2-5】
画图的算法paintwindow很简单,就不过多赘述了。简而言之就是定义左右平移点X,上下平移点Y,框的长W,框的宽H。
值得一提的是,我将【类】biankuang的成员函数全部定义成静态函数,所以调用类biankuang内的公有制成员函数paintwindow()及gtowy()无需创建新的对象。
好的,一直到这一步,我们的步骤一介绍的关于画界面功能的层层嵌套类都已经介绍清楚了。大家仔细看,没看懂之前不建议继续往下看。上面介绍最终实现的显示效果为图1-1。
-----------------------------------------------------------------------------------------------------------------------------
接下来我们将讲解步骤一中,关于如何让”显示界面“跳转至我们登录界面上的其他入口。
可以看到,在界面功能上,我们显示了注册、找回密码、登录、退出程序等入口。
所以我们需要使用键盘输入去激活这些指令,不过为了用户体验,这个过程中光标gtowy()也是需要进行可以移动的操作。所以接下来,我们需要做一个单独的input类进行封装,从而处理当我们键盘输入不同的值时,执行的业务动作。
但这里要注意,不要把套入口,如果可以尽量做到新的入口进入后,旧的入口已经关闭。简而言之,就是将进入下一个界面的判断条件放在上一个界面的进入前的函数部分。
先来看输入的类:
【图3-1】
再来看公有制函数int shuru();的具体写法
int i =0; int j =0;
务必注意,统计输入的账号密码的位数i,j均为全局变量
int input::shuru()
{
char zhanghao[12] = { 0 };用来存放账号 当输入为数字时才生效
int count = 0;
biankuang::gtoxy(37+i, 5);用来保证密码框按⬆时,光标可以跳转到账号最后一位处
while (1)
{
char c = _getch();获取外部进入的字符
if (c == -32) 因为箭头的ASCII值发两次,比如↑-32 72|← -32 75 ....
{
c = _getch(); 所以需要再接一下后面发送的字符
switch (c)
{
case 72: break;上 跳出switch 继续捕捉其他输入内容
case 75: break;左 跳出switch 继续捕捉其他输入内容
case 78: break;右 跳出switch 继续捕捉其他输入内容
case 80: mima(zhanghao); return 0;下,跳入至密码框中,此时的账号框结束运行
}
}
else if (c == '\b'&&i>0) 当按退格时,移动光标至前一位,使用空格覆盖后面的输出字符,
随后将当前的纳入值i归零,并且将i减一;
{
_putch('\b');
_putch(' ');
_putch('\b');
zhanghao[i] = '\0';
i--;
}
else if(c>='0'&&c<='9'&&i<10)如果输入为数字,将其纳入缓冲区
{
_putch(c);
zhanghao[i] = c;
i++;
}
else if (c == 61) 输入为F3时,跳转至找回密码入口。这个写法判断条件有缺陷
{
ftcode A; A.findpassword();
i = 0; j = 0;
return 0; /找回密码入口
}
else if (c==63) 输入为F5时,跳转至注册入口。同样写法具有缺陷
{
zhuce D; D.function();
i= 0; j = 0;
return 0;
}
else if (c == 27) 输入为ESC时,结束本程序
{
exit(0);
}
}
return 0;
}
【图3-2】
我们再来看密码端:
int input::mima(char *zhanghao)
{
char B[12] = {0};
int count = 0;
biankuang::gtoxy(37+j,8);账号框输入内容
while (1)
{
char c = _getch();获取外部进入的字符
if (c == -32)
{
c = _getch(); 再接一次
switch (c)
{
case 72: shuru(); return 0;跳转到账号框 结束当前程序,j不变
case 75: break;左
case 78: break;右
case 80: break;下
}
}
else if (c == '\b'&&j>0)
{
_putch('\b');
_putch(' ');
_putch('\b');
B[j] = '\0';
j--;
}
else if (c == 63)
{
zhuce xinxi;
xinxi.function();注册入口
for (count = 0; count < j; count++) { _putch('\b'); _putch(' '); _putch('\b'); } j = 0;
i = 0; j = 0;
return 0;
}
else if (c >= '0' && c <= '9'&&j<10)
{
_putch('*');
B[j] = c;
j++;
}
else if (c >= 65 && c <= 90 && j < 10)
{
_putch('*');
B[j] = c;
j++;
}
else if (c >= 97 && c <= 122 && j < 10)
{
_putch('*');
B[j] = c;
j++;
}
else if (c == 13&&j<11)
{
char str1[20] = { 0 };
char str2[20] = { 0 };输出为回车时,准备打开存入密码账号的文件
map<string, string>code;创建map容器,将文件读入的内容解析后读入map中。
ifstream ifs; 创建流对象
ifs.open("code.txt", ios::in);打开存储密码账号的文件
if (!ifs.is_open())
{
cout << "文件打开失败" << endl; return 0;
}
char buf[200] = { 0 }; 定义读取一行内容的缓冲区
while (ifs.peek() != EOF)开始读取文件,未读到结尾不结束
{
ifs.getline(buf, 30); 一次拿一行的数据至缓冲区
memset(str1, 0, sizeof(str1)); memset(str2, 0, sizeof(str2));清空即将存放账号和密码的缓冲区
char* N = nullptr;定义空指针,用来指向总数据,因为数组名为常量名无法自加
N = buf;
char* M = str1; 定义指向存放账号的缓冲区str1的头
char* O = str2; 定义指向存放密码的缓冲区str2的头
while (*N != ' ') { *M = *N; N++; M++; }读buf开始至未遇到空格时,将内容给str1(文件中存储的账号)
N++; 跳过空格
while(*N != '\0') { *O = *N; O++; N++; }继续读至\0,将之前的内容给str2(文件中存储的密码)
pair<string,string>p(str1,str2); 定义插入str1 str2的key,value
code.insert(p); 将内容插入容器
memset(str1, 0, sizeof(str1)); memset(str2, 0, sizeof(str2)); 清空,开始下一次读取
}
string a = zhanghao;账号转换类型
string c = code[a]; 通过输入的账号找map中是否有对应的密码
string b = B;将输入的密码转换一下
if (c.compare(b) != 0 || *B == '\0' || *zhanghao == '\0')
{上面是若输入的密码无法通过输入的账号在map容器中搜寻到,则输入错误。
//输入的密码未空,或账号未空都无法通过验证
cout << "error!"; Sleep(1200); int H = 0;
while (H < 6)
{
H++; _putch('\b'); _putch(' '); _putch('\b'); ;
memset(zhanghao, 0, sizeof(zhanghao)); memset(B, 0, sizeof(B));
}
i = 0; j = 0; 清空账号密码,清空i,j的内容,避免光标跳转异常
return 0;
}
int count = 0;
for (count = 0; count < j; count++)
{
_putch('\b');
_putch(' ');
_putch('\b');
} j = 0; 通过密码审核后,则清空内容及i,j,并进入下一个界面
biankuang::gtoxy(37 + i, 5);
count = 0;
for (count = 0; count < i; count++)
{
_putch('\b'); _putch(' '); _putch('\b');
} i = 0;
biankuang::gtoxy(25,17); Sleep(1000); 定义光标至登录框等待1s,保证用户观感
register1 X; X.denglu(); X.function();登录用户初始界面功能
break;保证初始界面正常运转,以便存在返回上一步操作,而非返回登录界面
}
}
return 0;
}
【图3-3】
那么到这里,我们的步骤一【登录界面】,配合的输入内容就已经填充完毕了。
其实此时,登录界面显示程序已经结束,仅仅有一个输入程序在while循环,所以我们仅需要输入内容,使其执行相应程序即可。
-----------------------------------------------------我 是 分 割 线---------------------------------------------------------
总结步骤一:其实所有的界面操作都是一样的,画界面->定输入规则->定回头条件->定跳入其他界面条件
-----------------------------------------------我 是 分 割 线--------------------------------------------------------
找回密码界面设计
接下来我们来设计找回密码界面的操作。
按照刚在的逻辑,我们此时已经在登录界面键盘键入了F3,已经跳入找回密码界面。
同样,先来画界面的范围,规定输入需求。这里要注意,由于上一个界面的显示还没有被清除,如果强行在同样位置画界面,会出现重叠覆盖的BUG,所以在进入找回密码界面前,我们需要先清除整个界面的显示内容,代码为system(“cls");
具体如下:
#pragma once
#include "button.h"
#include "./buttonsert.h"
#include<conio.h>
#include <map>
#include <fstream>
class ftcode
{
public:
ftcode()
{
system("cls"); 清屏
biankuang::paintWindow(10, 0, 35, 25); 外围框
biankuang::gtoxy(36, 2);光标定位至框中间上边,开始打广告
printf("%s", "|中路亚索 十五分投|");
button X; X.show(35, 4, 10, 2, "原有账号:"); 输入账号框 起始位置35 3
button ZZ; ZZ.show(35, 7, 10, 2, "存留手机号:");输入存留手机号框 起始位置35 6
biankuang::gtoxy(40, 15);
printf("%s", "按回车完成输入");
biankuang::gtoxy(10,10);
}
int findpassword(); 属于忘记密码的输入成员函数,显示完后就需要输入了
int findID(char *zhanghao); 跳转的手机号框成员函数,参数为输入的账号内容
};
【图4-1】
再来看忘记密码的input(输入成员函数)程序,和登录的input完全一样:
#include "ftcode.h"
int NN = 0;
int YY = 0;
char AA[12] = { 0 }; //存留账号
char BB[13] = { 0 }; //存留电话号
int ftcode::findpassword()
{
memset(AA, 0, sizeof(AA)); memset(BB, 0, sizeof(BB));
int count = 0;
biankuang::gtoxy(37 + NN, 5);//账号框输入内容
while (1)
{
char c = _getch();//获取外部进入的字符
if (c == -32)
{
c = _getch(); //再接一次
switch (c)
{
case 72: break;//上
case 75: break;//左
case 78: break;//右
case 80: findID(AA); return 0;//下
}
}
else if (c == '\b' && NN > 0)
{
_putch('\b');
_putch(' ');
_putch('\b');
AA[NN] = '\0';
NN--;
}
else if (c >= '0' && c <= '9' && NN < 10)
{
_putch(c);
AA[NN] = c;
NN++;
}
}
return 0;
}
【图4-2】
再来看【类】忘记密码程序,针对手机号的输入判断:
int ftcode::findID(char *zhanghao)
{
biankuang::gtoxy(37 + YY, 8);//账号框输入内容
while (1)
{
char c = _getch();//获取外部进入的字符
if (c == -32)
{
c = _getch(); //再接一次
switch (c)
{
case 72: findpassword(); return 0;//上
case 75: break;//左
case 78: break;//右
case 80: break;//下
}
}
else if (c == '\b' && YY> 0)
{
_putch('\b');
_putch(' ');
_putch('\b');
BB[YY] = '\0';
YY--;
}
else if (c >= '0' && c <= '9' &&YY < 12)
{
_putch(c);
BB[YY] = c;
YY++;
}
else if (c == 13 &&YY > 0)
{
char str1[20] = { 0 };
char str2[20] = { 0 };
map<string, string>code;
ifstream ifs; //创建流对象
ifs.open("code.txt", ios::in); //打开数据文本
if (!ifs.is_open())
{
cout << "文件打开失败" << endl; return 0;
}
char buf[200] = { 0 };
while (ifs.peek() != EOF)
{
ifs.getline(buf, 30);
memset(str1, 0, sizeof(str1)); memset(str2, 0, sizeof(str2));
char* N = nullptr;
N = buf; char* M = str1; char* O = str2; //总共、key、value
while (*N != ' ') { *M = *N; N++; M++; }
N++;
while (*N != '\0') { *O = *N; O++; N++; }
pair<string, string>p(str1, str2);
code.insert(p);
memset(str1, 0, sizeof(str1)); memset(str2, 0, sizeof(str2));
}//将文件内容读出来.一次读一行 施加一个判断s
string a = zhanghao;//转换
string c = code[BB];//通过手机号找账号
string b = BB;
if (c.compare(a) != 0 || *BB == '\0' || *zhanghao == '\0')
{
cout << "error!"; Sleep(1200);
int j = 0;
while (j < 6)
{
j++; _putch('\b'); _putch(' '); _putch('\b');
上面是识别未通过,清除表面输入的内容
}
memset(AA, 0, sizeof(AA)); memset(BB, 0, sizeof(BB));
NN = 0; YY = 0; 将统计数额归0 不然会出现再次打开光标往后移动了NN/YY
system("cls"); 清除系统表面的内容,防止回到登录界面,界面内容重合
return 0; 对比识别未通过关闭程序
}
cout << "your pwd is:" << code[a]; Sleep(3000);识别通过 显示密码
NN = 0; YY = 0; int count = 0;
while (1)
{
if (BB[count] == '\0')
{ memset(BB, 0, sizeof(BB));break; }
BB[count] = '\0'; _putch('\b'); _putch(' '); _putch('\b'); count++;
} 功能和上面清除功能一样
memset(zhanghao, 0, sizeof(zhanghao)); 记得归零,不然下次再打开有BUG
memset(BB, 0, sizeof(BB));记得归零,不然下次再打开有BUG
system("cls");清除系统表面的内容,防止回到登录界面,界面内容重合
return 0;回到登录界面
}
}
return 0;
【图4-2】
可以看到除了录入空格时需要额外写判断条件、没有跳转入口外,其他录入信息条件和登录框完全一样。而其他的界面和这个界面设计流程完全一样!
忘记密码界面最终效果为图1-4。
这里也就不介绍其他界面的操作了,大家只要看懂这个流程,完成类似的项目是没有任何问题的
祝大家敲的开心。
最后除类外的头文件
#include <iostream> C++标准头文件
#include <stdio.h> C标准头文件
using namespace std; C++命名空间标准头文件
#include <string> 写字符串必备
#include <conio.h> 获取字符头文件,主要需要使用_getch()
#include "qlite.h" SQL数据库需要用的头文件
#include <map> map容器头文件
#include <windows.h> 主要利用Sleep等待程序运行 提升用户感知
#include <fstream> C++流文件,用来将用户账号密码写入外部文件中
#include <graphics.h> 用来画星星的,可选
#include <time.h> 用来画星星的,可选
#include<stdlib.h> 用来画星星的,可选
附上图1-5说的的动态星星