目录
一、图书馆功能总览
图书功能
- 读取txt内的图书信息,并整理成可翻页,可跳转的列表,并且输入当前页对应书籍的编号可以查看该书籍的详细信息。
- 可根据书名和isbn删除图书,可增加新图书
- 可以根据书名和isbn搜索图书,可通过作者或出版社模糊搜索图书
- 可以借阅图书,归还图书
- 有排行榜系统,包含热门图书排行榜(以用户借阅数量排行),借书次数排行榜,最新图书排行榜
用户管理
- 有账户与密码,需注册。
- 用户有身份标识,有管理员、普通用户和游客,管理员有全部权限,普通用户不能增删图书和用户,游客除了不能增删图书和用户,还不能借阅图书
- 管理员可以通过用户名增删用户,可以查看所有用户(用户列表)。
- 管理员可以重置指定用户密码。
- 用户可以修改密码
- 用户可以查看自己个人的借阅记录
- 可以退出登录
二、结构解析
这个项目用多文件写成,分为
- head.h是放整个项目头文件包含,宏定义声明,类的声明与定义,各类函数的声明
- main.cpp,是项目运行的主线
- Register&SignUp.cpp是放登录与注册相关函数的定义
- FileIO.cpp是放有关程序与储存交互的函数定义
- BookManage.cpp是放实现图书管理功能的函数定义
- Page.cpp是放有关页面的函数定义
-
BorrowandReturn.cpp是放有关借阅与还书方面功能的函数定义
- Rankings.cpp是放有关排行榜方面的函数定义
- UserManage.cpp是放有关用户管理方面的函数定义
三、功能实现
光标移动
void SetCCPos(int x, int y)
{
HANDLE hOut;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);//获取标注输出句柄
COORD pos;
pos.X = x;pos.Y = y;
SetConsoleCursorPosition(hOut, pos);//偏移光标位置
}
光标从(0,0)开始偏移,只要输入对应的坐标即可,且每次光标都是从(0,0)开始,两次偏移之间不会相互影响。
登录与注册
思路:while循环输入账户与密码,当检测到用户名是reg时跳转到注册页面,否则就是正常检测输入的账户与密码能否与已有用户链表中的用户对的上,对的上则登录成功,否则登录失败
head.h中,创建用户链表
class User
{
public:
void addBorrowBook(string& bo) //增加已借阅的图书
{
borrowbookname.push_back(bo);
}
User():name(""),key(""),borrownum(0),sumbooknum(0){} //初始化
string name; //用户名
string key; //密码
short iden; //身份(0是管理员,1是普通用户,-1是游客)
short borrownum; //现在借书量
short sumbooknum; //总借书量
vector <string> borrowbookname; //现借书的书名
User *next;
};
struct UserList
{
User* head; //头节点
User* tail; //尾节点
int length; //节点数
};
之后在main中
string name = "", //装用户输入的账户与密码
key = "";
User *user = nullptr; //指向登录成功后的用户
UserList *u_list = new UserList; //创建一个管理用户链表对象
u_list->head = u_list->tail = user;
u_LordData(u_list); //载入已储存的用户链表
User* current_user; //当前登录账号的信息
while(1)
{
step1:
system("cls");
Page1(tips); //绘制登录页面
SetCCPos(9,4);
cin>>name;
if(name == "reg") //进入注册界面
{
name = "unreg";
User* tem = new User; //注册用的节点
Page2(tips); //绘制注册界面
SetCCPos(9,4);
cin>>tem->name;
SetCCPos(7,5);
cin>>tem->key;
SetCCPos(7,6);
cin>>tem->iden;
if(Register(tem,u_list)) //检查该用户名是否被占用
{
tips = "注册成功,请登录";
u_SaveData(u_list);
goto step1;
}
else
{
delete tem;
tips = "用户名已被占用";
goto step1;
}
}
else
{
SetCCPos(7,5);
cin>>key;
}
if(key == "0") //游客
{
current_user = new User;
current_user->name = name;
current_user->key = key;
current_user->iden = -1;
goto in;
}
if(SignUp(name,key,u_list)) //检查用户名与密码是否对应
{
current_user = LoacteUser(name,u_list);
break;
}
else
{
tips = "登录失败";
continue;
}
}
in:
界面绘制
界面我分为4种
a,选择界面
b,功能页面
c,列表界面
d,其他
先易后难,先解释
- 其他界面的绘制
void PageTitle(void)
{
cout<<"╭-------------------■□■□■□--╮"<<endl;
cout<<"│ 图书管理系统 │"<<endl;
cout<<"╰--■□■□■□-------------------╯"<<endl;
}
void Page1(string &tips) //登录界面
{
PageTitle();
cout<<"----------登录--------------"<<endl;
cout<<"■用户名:"<<endl;
cout<<"□密码:"<<"\t\t\t密码为0是游客"<<endl;
cout<<tips<<endl;
cout<<"(没有账号?)"<<endl;
cout<<"(在用户名输入reg进行注册.)"<<endl;
}
void Page2(string &tips) //注册界面
{
system("cls");
PageTitle();
cout<<"----------注册--------------"<<endl;
cout<<"■用户名:"<<endl;
cout<<"□密码:"<<endl;
cout<<"■身份:"<<endl;
cout<<"(管理员-0 普通用户-1)"<<endl;
cout<<tips;
}
- 选择界面、列表界面与功能界面的绘制
思路:先创建一个page类,一个里装着这个页面的所需要打印的信息和该页面的识别编号,当读到该编号时绘制该页面
//head.h中
class Page
{
public:
Page(const std::string* b,int nu,int numbe,string tipsc)
{
n = nu;
number = numbe;
for(int i = 0;i < n;i++)
{
button[i] = b[i];
}
tips = tipsc;
};
Page(const std::string* b,int nu,int numbe):tips("\0")
{
n = nu;
number = numbe;
for(int i = 0;i < n;i++)
{
button[i] = b[i];
}
};
Page(const std::string* b,int nu,int numbe,string na,string ident):tips("\0")
{
n = nu;
number = numbe;
button[0] = b[0]+na;
button[1] = b[1]+ident;
for(int i = 2;i < n;i++)
{
button[i] = b[i];
}
}
int number; //编号
void CoutDir(void); //输出指引
void GetTips(void); //获取提示
void CoutTips(void); //输出提示
void PtintPage(void); //打印选择页面
void PrintFunctionPage(UserList*&,Page*&,User*&,BookList*&,BorrowManage& bor); //打印功能页面
void CoutFunctionDir(void); //打印功能页面指引
void PrintFPage(void); //打印功能页面
void ReflashSelect(int&); //刷新选择的页面
private:
string button[10]; //用来装标题指引
string tips; //用来装提示
int n; //指引个数
};
//Page.cpp中
void Page::CoutDir(void) //输出指引
{
for(int i = 0;i < n-1;i++)
{
cout<<Page::button[i]<<endl;
}
SetCCPos(0,12);
cout<<button[n-1];
SetCCPos(5,12);
}
void Page::GetTips(void) //获取提示
{
cin>>Page::tips;
}
void Page::CoutTips(void) //输出提示
{
cout<<tips;
}
void Page::PtintPage(void) //打印该页面
{
system("cls");
PageTitle();
CoutDir();
CoutTips();
}
void Page::CoutFunctionDir(void)
{
for(int i = 0;i < n;i++)
{
cout<<Page::button[i]<<endl;
}
}
void Page::PrintFPage(void)
{
system("cls");
PageTitle();
CoutFunctionDir();
CoutTips();
}
void Page::ReflashSelect(int& select)
{
select = number *10 +select;
}
用这种逻辑制作页面,需要先将全部页面信息按顺序录入,这就需要一个对象来装着这些页面
//head.h中
class PageManage
{
public:
Page* arrow; //指向当前的Page
vector <Page*> allpage; //全部页面
void ChangePage(Page*&); //切换Page
void addPage(Page* page) //增加页面
{
allpage.push_back(page);
}
};
之后在main中按一定顺序录入页面信息
//载入页面
PageManage pm; //页面管理对象
//****************************零级页面
//Page Star;
string p0[8] = {"1-图书浏览","2-借还图书","3-个人信息","4-用户管理","5-作者信息","6-各排行榜","7-退出系统","编号:"};
Page* page0 = new Page(p0,8,0);
pm.addPage(page0);
//****************************一级页面
//Page BrowseBook;
string p1[6] = {"1-全部图书","2-查找图书","3-删除图书","4-增加图书","0-上一页","编号:"};
Page* page1 = new Page(p1,6,1);
pm.addPage(page1);
//Page Borrow;
string p2[6] = {"1-借书","2-还书","0-上一页","编号:"};
Page* page2 = new Page(p2,4,2);
pm.addPage(page2);
//Page PersonalInformation;
string p3[7] = {"用户名:","身份:","1-修改密码","2-借阅记录","3-退出登录","0-上一页","编号:"};
string t;
if(0 == current_user->iden)
{
t = "管理员";
}
else if(1 == current_user->iden)
{
t = "普通用户";
}
else if(-1 == current_user->iden)
{
t = "游客";
}
Page* page3 = new Page(p3,7,3,current_user->name,t);
pm.addPage(page3);
//Page ManageUser
if(0 == current_user->iden) //是管理员
{
string p4[6] = {"1-用户列表","2-增加用户","3-删除用户","4-重置指定用户密码","0-上一页","编号:"};
Page* page4 = new Page(p4,6,4);
pm.addPage(page4);
}
else //非管理员
{
string p4[3] = {"您没有使用该功能的权限!","0-上一页","编号:"};
Page* page4 = new Page(p4,3,4);
pm.addPage(page4);
}
//Page AuthorInformation
string p5[4] = {"作者:梁某","QQ:2803234009","0-上一页","编号:"};
Page* page5 = new Page(p5,4,5);
pm.addPage(page5);
string p6[4] = {"1-热门图书","2-借书狂人","3-最新图书","编号:"};
Page* page6 = new Page(p6,4,6);
pm.addPage(page6);
//Page EXIXT
string p7[3] = {"已保存数据","退出成功","\0"};
Page* page7 = new Page(p7,3,7);
pm.addPage(page7);
//*************************二级页面
//Page allbook //list func
string p8[4] = {"全部图书","0-上一页","输入书籍id可查看详细信息","---------'+'下一页,‘-’上一页,‘*’跳转---------"};
Page* page8 = new Page(p8,4,11);
pm.addPage(page8);
//Page searchbook
string p9[7] = {"搜索图书","1-按书名搜索","2-按isbn搜索","3-按出版社搜索","4-按作者搜索","0-上一页","编号:"};
Page* page9 = new Page(p9,7,12);
pm.addPage(page9);
//Page delbook
if(0 == current_user->iden)
{
string p10[5] = {"删除图书","1-按书名删除","2-按isbn删除","0-上一页","编号:"};
Page* page10 = new Page(p10,5,13);
pm.addPage(page10);
}
else
{
string p10[3] = {"您没有使用该功能的权限!","0-上一页","编号:"};
Page* page10 = new Page(p10,3,13);
pm.addPage(page10);
}
//Page addbook //func
if(0 == current_user->iden)
{
string p11[9] = {"增加图书","书名:","ISBN:","作者:","出版社:","出版时间:","价格:","描述:","0-上一页"};
Page* page11 = new Page(p11,9,14);
pm.addPage(page11);
}
else
{
string p11[3] = {"您没有使用该功能的权限!","0-上一页","编号:"};
Page* page11 = new Page(p11,3,14);
pm.addPage(page11);
}
if(-1 == current_user->iden)
{
string p12[3] = {"您没有使用该功能的权限!","0-上一页","编号:"};
Page* page12 = new Page(p12,3,21);
pm.addPage(page12);
string p13[3] = {"您没有使用该功能的权限!","0-上一页","编号:"};
Page* page13 = new Page(p13,3,22);
pm.addPage(page13);
}
else
{
//Page borrowbook //func
string p12[5] = {"借书","1-书名","2-ISBN","0-上一页","搜索:"};
Page* page12 = new Page(p12,5,21,"用书名搜索 1-xxx,isbn 2-12...");
pm.addPage(page12);
//Page returnbook //func
string p13[5] = {"还书","1-书名","2-ISBN","0-上一页","搜索:"};
Page* page13 = new Page(p13,5,22,"书名搜索 1-xxx,isbn搜索 2-xxx");
pm.addPage(page13);
}
//Page rewritekey //func
string p14[4] = {"修改密码","新密码:","确认密码:","0-上一页"};
Page* page14 = new Page(p14,4,31);
pm.addPage(page14);
//Page borrowrecord //list func
string p15[2] = {"借阅记录","0-上一页"};
Page* page15 = new Page(p15,2,32);
pm.addPage(page15);
//Page logoff 33
//Page Userlist //list func
string p16[2] = {"用户列表","0-上一页"};
Page* page16 = new Page(p16,2,41);
pm.addPage(page16);
//Page addUser //func
string p17[4] = {"添加用户","用户名:","身份:","0-上一页"};
Page* page17 = new Page(p17,4,42,"身份0-管理员 1-普通用户,密码默认为000000");
pm.addPage(page17);
//Page delUser //func
string p18[3] = {"删除用户","用户名:","0-上一页"};
Page* page18 = new Page(p18,3,43);
pm.addPage(page18);
//Page resetket //func
string p19[3] = {"重置密码","用户名:","0-上一页"};
Page* page19 = new Page(p19,3,44,"密码重置为000000");
pm.addPage(page19);
//Pafe hotbook //rank
string p20[2] = {"热门图书","0-上一页"};
Page* page20 = new Page(p20,2,61);
pm.addPage(page20);
//Page borrowMaxbook //rank
string p21[2] = {"借书狂人","0-上一页"};
Page* page21 = new Page(p21,2,62);
pm.addPage(page21);
//Page newbook //rank
string p22[2] = {"最新图书","0-上一页"};
Page* page22 = new Page(p22,2,63);
pm.addPage(page22);
//**************************************三级页面
//Page searchbytitle //func
string p23[3] = {"按书名搜索","书名:","0-上一页"};
Page* page23 = new Page(p23,3,121);
pm.addPage(page23);
//Page searchbyisbn //func
string p24[3] = {"按isbn搜索","ISBN:","0-上一页"};
Page* page24 = new Page(p24,3,122);
pm.addPage(page24);
//Page searchbypublisher //func
string p25[3] = {"按出版社搜索","出版社:","0-上一页"};
Page* page25 = new Page(p25,3,123);
pm.addPage(page25);
//Page searchbyauthor //func
string p26[3] = {"按作者搜索","作者:","0-上一页"};
Page* page26 = new Page(p26,3,124);
pm.addPage(page26);
//Page delbytitle //func
string p27[3] = {"按书名删除","书名:","0-上一页"};
Page* page27 = new Page(p27,3,131);
pm.addPage(page27);
//Page delbyisdn //func
string p28[3] = {"按isdn删除","ISBN:","0-上一页"};
Page* page28 = new Page(p28,3,132);
pm.addPage(page28);
如果是选择页面,绘制时只需要调用PtintPage()即可,如果是功能页面,调用PrintFunctionPage()即可
Page* arrow = pm.allpage[0]; //打印第0级页面
while(1)
{
if(arrow == pm.allpage[0]||arrow == pm.allpage[1]||arrow == pm.allpage[2]||arrow == pm.allpage[3]||arrow == pm.allpage[4]||
arrow == pm.allpage[5]||arrow == pm.allpage[7]||arrow == pm.allpage[9]||arrow == pm.allpage[10])
arrow->PtintPage(); //选择页面
else if(arrow == nullptr) //退出登录
{
u_SaveData(u_list);
book_SaveData(book_list);
borrow_SaveData(borrow);
goto step1;
}
else //功能页面和列表页面
arrow->PrintFunctionPage(u_list,arrow,current_user,book_list,borrow);
if(arrow == pm.allpage[7]) //保存退出系统
break;
pm.ChangePage(arrow); //切换页面
}
如果是功能页面或列表页面,就调用 PrintFunctionPage(),当然功能页面不仅只有指引,还要实现指定功能,只需调用对应函数实现即可
void Page::PrintFunctionPage(UserList*& ulc,Page*& arrowc,User*& cu,BookList*& blc,BorrowManage& bor)
{
arrowc->PrintFPage(); //打印功能页面和列表页面的页面指引
switch(arrowc->number)
{
case 11:
PrintBookList(blc,arrowc);
break;
case 12: //列表页面,在上面已输出
break;
case 13: //列表页面,在上面已输出
break;
case 14:
if(cu->iden!=0)
break;
AddBook(blc);
break;
case 21:
BorrowBooks(blc,cu,bor);
break;
case 22:
ShowBorrowRecord(bor,cu);
ReturnBook(bor,cu);
break;
case 31:
ChangePassport(cu);
break;
case 32:
ShowBorrowRecord(bor,cu);
break;
case 41:
if(cu->iden!=0)
break;
printUserList(ulc);
break;
case 42:
if(cu->iden!=0)
break;
addUser(ulc);
break;
case 43:
if(cu->iden!=0)
break;
SetCCPos(0,10);
printUserList(ulc);
delUser(ulc,bor);
break;
case 44:
resetKey(ulc);
break;
case 61:
HotBookRank(bor);
break;
case 62:
CrazyBorrwoMan(ulc);
break;
case 63:
LatestBook(blc);
break;
case 121:
SearchByName(blc);
break;
case 122:
SearchByISBN(blc);
break;
case 123:
SearchByPublising(blc);
break;
case 124:
SearchByAuthor(blc);
break;
case 131:
DelBookByName(blc);
break;
case 132:
DelBookByISBN(blc);
break;
}
}
界面的切换
思路:先要获取当前页面的编号,再获取用户输入的号码,将两者合并就是目标页面的编号,如第一级页面“个人信息”页面的编号是3,如果用户想要去“借阅记录”页面,用户输入了2,那么32就是”借阅记录“的编号。
这就引出了一个新的问题,要将”借阅记录“页面与编号32联系在一起,这就是PageManage中ChangePage()的作用
//Page.h中
void PageManage::ChangePage(Page *&arrow)
{
int select = 0; //当前页的选择
cin>>select;
if(arrow == allpage[3]&&select == 3)//退出登录
{
arrow = nullptr;
return;
}
if(0 == select) //返回上一页
{
arrow->ReflashSelect(select);
select/=100;
}
else
{
arrow->ReflashSelect(select);
}
switch(select)
{
case 0:
arrow = allpage[0];
break;
case 1:
arrow = allpage[1];
break;
case 2:
arrow = allpage[2];
break;
case 3:
arrow = allpage[3];
break;
case 4:
arrow = allpage[4];
break;
case 5:
arrow = allpage[5];
break;
case 6:
arrow = allpage[6];
break;
case 7:
arrow = allpage[7];
break;
case 11:
arrow = allpage[8];
break;
case 12:
arrow = allpage[9];
break;
case 13:
arrow = allpage[10];
break;
case 14:
arrow = allpage[11];
break;
case 21:
arrow = allpage[12];
break;
case 22:
arrow = allpage[13];
break;
case 31:
arrow = allpage[14];
break;
case 32:
arrow = allpage[15];
break;
case 41:
arrow = allpage[16];
break;
case 42:
arrow = allpage[17];
break;
case 43:
arrow = allpage[18];
break;
case 44:
arrow = allpage[19];
break;
case 61:
arrow = allpage[20];
break;
case 62:
arrow = allpage[21];
break;
case 63:
arrow = allpage[22];
break;
case 121:
arrow = allpage[23];
break;
case 122:
arrow = allpage[24];
break;
case 123:
arrow = allpage[25];
break;
case 124:
arrow = allpage[26];
break;
case 131:
arrow = allpage[27];
break;
case 132:
arrow = allpage[28];
break;
}
}
形成新的编号后,就将arrow指向该页面,下一次while循环时就会打印该页面
信息载入与储存
-
用户信息(链表储存)
信息储存
思路:用户信息在程序里是用链表储存的,遍历各个节点,将信息写入User.txt就行
//FileIO.cpp
void u_SaveData(UserList* p)
{
ofstream fp(userfile,ios::trunc);
User* current = p->head;
int length = p->length;
while(length--)
{
fp<<endl<<current->name<<" ";
fp<<current->key<<" ";
fp<<current->iden<<" ";
fp<<current->borrownum<<" ";
fp<<current->sumbooknum;
int booknum = current->borrowbookname.size();
if(booknum == 0)
{
current = current->next;
continue;
}
for(int i = 0;i < booknum-1;i++)
{
fp<<" "<<current->borrowbookname[i];
}
fp<<" "<<current->borrowbookname[booknum-1];
current = current->next;
}
fp.close();
}
信息载入
思路:如果User.txt里有数据,先创建一个节点,将信息读出来,并把这个节点连入链表
void u_LordData (UserList* p)
{
ifstream fp(userfile);
if(!fp)
{
return;
}
while(fp.peek() != EOF)
{
u_CreatPoint(p);
User* current = p->tail;
string bookname;
fp>>current->name>>current->key;
fp>>current->iden>>current->borrownum>>current->sumbooknum;
int num = current->borrownum;
while(num--)
{
fp>>bookname;
current->borrowbookname.push_back(bookname);
}
}
fp.close();
}
void u_CreatPoint (UserList* p)
{
User* current = new User;
current->next = nullptr;
if(p->head ==nullptr)
{
p->head = current;
p->tail = current;
}
else
{
p->tail->next = current;
p->tail = p->tail->next;
}
p->length++;
}
-
书籍信息(链表储存)
书籍信息储存
void book_SaveData(BookList* p)
{
ofstream fp(bookfile,ios::trunc);
Book* current = p->head;
int length = p->length;
fp<<"id isbn name author publishing publisheddate price pages description"<<endl;
while(length--)
{
fp<<current->id<<"\t";
fp<<current->isbn<<"\t";
fp<<current->bookname<<"\t";
fp<<current->author<<"\t";
fp<<current->publising<<"\t";
fp<<current->publisheddate<<"\t";
fp<<current->price<<"\t";
fp<<current->page<<"\t";
fp<<current->description<<"\n";
current = current->next;
}
fp.close();
}
书籍信息载入
void book_LordData(BookList* p)
{
ifstream fp(bookfile);
if(!fp)
{
return;
}
string title;
getline(fp,title,'\n');
while(fp.peek() != EOF)
{
book_CreatPoint(p);
Book* current = p->tail;
fp>>current->id;
fp.get();
getline(fp,current->isbn,'\t');
getline(fp,current->bookname,'\t');
getline(fp,current->author,'\t');
getline(fp,current->publising,'\t');
getline(fp,current->publisheddate,'\t');
fp>>current->price;
fp.get();
fp>>current->page;
fp.get();
getline(fp,current->description,'\n');
/*char c = fp.peek();
int num = current->id+1;
while(num>=10)
num/=10;
int count = 0;
while(!(c >'0'&&c<='9'))
{
string t;
getline(fp,t,'\n');
if(t == "")
count++;
if(t==""&&count>=5)
{
count = 0;
break;
}
current->description+=t;
c = fp.peek();
}*/
}
fp.close();
}
void book_CreatPoint(BookList* p)
{
Book* current = new Book;
current->next = nullptr;
if(p->head == nullptr)
{
p->head = current;
p->tail = current;
}
else
{
p->tail->next = current;
p->tail = p->tail->next;
}
p->length++;
}
注释掉的内容是为了区分文件中书籍的内容简介,如果书籍数据格式规范,就不需要这个
-
借阅信息(vector储存)
借阅信息储存
void borrow_SaveData(BorrowManage& bo)
{
ofstream fp(borrowfile,ios::trunc);
int num = bo.borrowrecording.size();
for(int i = 0;i < num;i++)
{
fp<<bo.borrowrecording[i]->Name_book()<<"\t";
fp<<bo.borrowrecording[i]->Isbn_book()<<"\t";
fp<<bo.borrowrecording[i]->Date_borrow()<<"\t";
fp<<bo.borrowrecording[i]->Date_return()<<"\t";
fp<<bo.borrowrecording[i]->Username()<<endl;
}
fp.close();
}
借阅信息载入
void borrow_LordData(BorrowManage& bo)
{
ifstream fp(borrowfile);
while(fp.peek()!=EOF)
{
string bookname,isbn,borrowdate,returndate,username;
getline(fp,bookname,'\t');
getline(fp,isbn,'\t');
getline(fp,borrowdate,'\t');
getline(fp,returndate,'\t');
getline(fp,username,'\n');
Borrow* temp = new Borrow(bookname,isbn,borrowdate,username);
bo.addPage(temp);
}
fp.close();
}
图书管理
-
全部图书(图书列表)
思路:宏定义onepage是20,定义begin和end,打印的信息就是从begin到end的节点信息,而begin和end的数值根据page来算得,那就可以通过控制page来控制打印的信息
void PrintBookList(BookList* p,Page*& pa)
{
Book* current = p->head;
int length = p->length;
int page = 1,
begin,
end;
while(1)
{
system("cls");
SetCCPos(0,0);
pa->PrintFPage();
if(page<1) //最前一页
page = 1;
else if(page>ceil(length/20)) //最后一页
page = ceil(length/20.0);
begin = (page-1)*onepage,
end = page*onepage;
cout<<"id name author publishing"<<endl;
current = p->head;
for(int i = 0;i < (page-1)*onepage;i++)
{
current = current->next;
}
for(int i = begin;i < end;i++)
{
cout<<current->id-((page-1)*onepage)<<"\t";
string name = current->bookname;
while(name.length()<55)
name+=" ";
cout<<name;
string author = current->author;
while(author.length()<15)
author+=" ";
cout<<author;
string publishing = current->publising;
while(publishing.length()<10)
publishing+=" ";
cout<<publishing<<"\n";
current = current->next;
if(current == nullptr)
break;
}
cout<<"第"<<page<<"页"<<endl;
int num = 0;
cin.sync(); //清空缓存区
char t = cin.peek();
if(t == '+')
{
page++;
continue;
}
else if(t == '-')
{
page--;
continue;
}
else if(t == '*')
{
cin.ignore();
cout<<"跳转到:";
cin>>page;
continue;
}
else if(t == '\n')
{
continue;
}
cin>>num; //接收用户想查看书籍的编号
switch(num)
{
case 0:
cout<<"输入0返回上一页"<<endl;
return;
default:
num = num+(page-1)*onepage;
PrintBookDetil(num,p);
break;
}
}
}
void PrintBookDetil(int n,BookList* p)
{
system("cls");
Book* current = p->head;
for(int i = 0;i < n-1;i++)
{
current = current->next;
}
cout<<"书名:"<<current->bookname<<endl;
cout<<"ISBN:"<<current->isbn<<endl;
cout<<"作者:"<<current->author<<endl;
cout<<"页数:"<<current->page<<endl;
cout<<"价格:"<<current->price<<endl;
cout<<"出版时间:"<<current->publisheddate<<endl;
cout<<"出版社:"<<current->publising<<endl;
cout<<"简介:"<<current->description<<endl<<endl;
system("pause");
}
-
查找图书
思路:书籍信息是用链表储存的,从用户输入中获取相关信息,再遍历链表找到对应节点,再输出该节点的相关信息
以书名搜索
//BookManage.cpp
void SearchByName(BookList*& p)
{
SetCCPos(5,4);
string name;
cin>>name;
Book* current = p->head;
SetCCPos(0,6);
cout<<"id name author publishing"<<endl;
while(current != nullptr)
{
if(current->bookname == name)
{
cout<<current->id<<"\t";
string name = current->bookname;
while(name.length()<55) //输出格式化
name+=" ";
cout<<name;
string author = current->author;
while(author.length()<15) //输出格式化
author+=" ";
cout<<author;
string publishing = current->publising;
while(publishing.length()<10) //输出格式化
publishing+=" ";
cout<<publishing<<"\n";
}
current = current->next;
}
}
以ISBN搜索
void SearchByISBN(BookList*& p)
{
SetCCPos(5,4);
string isbn;
cin>>isbn;
Book* current = p->head;
SetCCPos(0,6);
cout<<"id name author publishing"<<endl;
while(current != nullptr)
{
if(current->isbn == isbn)
{
cout<<current->id<<"\t";
string name = current->bookname;
while(name.length()<55) //输出格式化
name+=" ";
cout<<name;
string author = current->author;
while(author.length()<15) //输出格式化
author+=" ";
cout<<author;
string publishing = current->publising;
while(publishing.length()<10) //输出格式化
publishing+=" ";
cout<<publishing<<"\n";
}
current = current->next;
}
}
以出版社搜索
//BookManage.cpp
void SearchByPublising(BookList*& p)
{
SetCCPos(7,4);
string publising;
cin>>publising;
Book* current = p->head;
SetCCPos(0,6);
cout<<"id name author publishing"<<endl;
while(current != nullptr)
{
if(current->publising == publising)
{
cout<<current->id<<"\t";
string name = current->bookname;
while(name.length()<55)
name+=" ";
cout<<name;
string author = current->author;
while(author.length()<15)
author+=" ";
cout<<author;
string publishing = current->publising;
while(publishing.length()<10)
publishing+=" ";
cout<<publishing<<"\n";
}
current = current->next;
}
}
以作者搜索
void SearchByAuthor(BookList*& p)
{
SetCCPos(5,4);
string author;
cin>>author;
Book* current = p->head;
SetCCPos(0,6);
cout<<"id name author publishing"<<endl;
while(current != nullptr)
{
if(current->author == author)
{
cout<<current->id<<"\t";
string name = current->bookname;
while(name.length()<55)
name+=" ";
cout<<name;
string author = current->author;
while(author.length()<15)
author+=" ";
cout<<author;
string publishing = current->publising;
while(publishing.length()<10)
publishing+=" ";
cout<<publishing<<"\n";
}
current = current->next;
}
}
-
删除图书
思路:从用户输入获取目标书籍的信息,遍历链表,如果找到记录该书籍的节点,则删除该节点,如果没找到,就输出“未找到该书籍” 。
以书名删除
//BookManagge.cpp
void DelBookByName(BookList* p)
{
SetCCPos(5,4);
string name;
cin>>name;
Book* current = p->head;
while(current!=nullptr)
{
if(current->next->bookname == name)
{
Book* temp = current->next;
current->next = temp->next;
delete temp;
p->length--;
SetCCPos(0,6);
cout<<"删除成功"<<endl;
return;
}
current = current->next;
}
SetCCPos(0,6);
cout<<"未找到该书,请重进"<<endl;
return;
}
以ISBN删除
//BookManage.cpp
void DelBookByISBN(BookList* p)
{
SetCCPos(5,4);
string ISBN;
cin>>ISBN;
Book* current = p->head;
while(current!=nullptr)
{
if(current->next->isbn == ISBN)
{
Book* temp = current->next;
current->next = temp->next;
delete temp;
p->length--;
SetCCPos(0,6);
cout<<"删除成功"<<endl;
return;
}
current = current->next;
}
SetCCPos(0,6);
cout<<"未找到该书,请重进"<<endl;
return;
}
-
增加图书
void AddBook(BookList* p)
{
book_CreatPoint(p);
Book* current = p->tail;
p->tail->id = p->length;
SetCCPos(5,4);
cin>>current->bookname;
if(current->bookname == "0")
{
SetCCPos(0,12);
cout<<"请输入0退出"<<endl;
return;
}
SetCCPos(5,5);
cin>>current->isbn;
SetCCPos(5,6);
cin>>current->author;
SetCCPos(7,7);
cin>>current->publising;
SetCCPos(9,8);
cin>>current->publisheddate;
SetCCPos(5,9);
cin>>current->price;
SetCCPos(5,10);
cin>>current->description;
SetCCPos(0,12);
cout<<"添加成功,输入0退出"<<endl;
}
书籍借还(借还记录用vector记录)
借书
思路:从用户输入获取相关信息,搜先要判断这本书用户是否已经借了还没有还,如果借了没还,输出“已借过”,否则就查找书籍链表中是否有该书籍,如果有,就记录下这条借阅信息,如果没有该书籍,就输出“查无此书”。
void BorrowBooks(BookList* bl,User* u,BorrowManage& bo)
{
Book* current = bl->head;
SetCCPos(5,7);
string s;
cin>>s;
if(s[0] == '0')
{
SetCCPos(0,10);
cout<<"输入0返回上一页";
return;
}
else if(s[0] == '1')
{
s.erase(0,2);
vector<string>::iterator iter=std::find(u->borrowbookname.begin(),u->borrowbookname.end(),s);
if(iter != u->borrowbookname.end())
{
SetCCPos(0,9);
cout<<u->name<<"已借过"<<"<<"<<s<<">>"<<endl;
return;
}
while(current!=nullptr)
{
if(s == current->bookname)
{
u->borrownum++;
u->sumbooknum++;
RecordBorrowInf(current,bo,u);
u->borrowbookname.push_back(current->bookname);
SetCCPos(0,9);
cout<<u->name<<"成功借阅"<<"<<"<<current->bookname<<">>"<<endl;
return;
}
current = current->next;
}
SetCCPos(0,9);
cout<<"查无此书"<<endl;
return;
}
else if(s[0] == '2')
{
s.erase(0,2);
while(current!=nullptr)
{
if(s == current->isbn)
{
string sbookname = current->bookname;
vector<string>::iterator iter=std::find(u->borrowbookname.begin(),u->borrowbookname.end(),sbookname);
if(iter != u->borrowbookname.end())
{
SetCCPos(0,9);
cout<<u->name<<"已借过"<<"<<"<<current->bookname<<">>"<<endl;
return;
}
u->borrownum++;
u->sumbooknum++;
RecordBorrowInf(current,bo,u);
u->borrowbookname.push_back(current->bookname);
SetCCPos(0,9);
cout<<u->name<<"成功借阅"<<"<<"<<current->bookname<<">>"<<endl;
return;
}
current = current->next;
}
SetCCPos(0,9);
cout<<"查无此书"<<endl;
return;
}
}
void RecordBorrowInf(Book* current,BorrowManage& bo,User* u)
{
time_t timep;
string t;
time(&timep);
t = ctime(&timep);
t.pop_back();
Borrow* temp = new Borrow(current->bookname,current->isbn,t,u->name);
bo.addPage(temp);
}
代码解析:vector<string>::iterator iter=std::find(u->borrowbookname.begin(),u->borrowbookname.end(),s); //定义了一个vector<string>类型的迭代器iter用来装find函数的返回值,而传递给find函数的三个参数是查找的开头,查找的结尾,查找的内容,如果iter指向borrowbookname.end(),那么就是没有找到,如果不是,iter指向目标位置。
还书
思路:从用户输入获取要还书的书名(或isbn),遍历借阅记录,从中找到该用户的借阅记录,再对比该用户借的书与要还书的书名(或isbn)是否一致,如果一致,就修改借阅记录,将归还时间改为当前时间。如果该用户借的书中没有该书,或没有该用户的借阅记录,都输出“未借阅”。
void ReturnBook(BorrowManage& bo,User* u)
{
SetCCPos(5,7);
string s;
int num = bo.borrowrecording.size();
time_t timep;
string r_time;
time(&timep);
r_time = ctime(&timep);
cin>>s;
if(s[0] == '0')
{
SetCCPos(0,15);
cout<<"输入0返回上一页";
return;
}
else if(s[0] == '1')
{
s.erase(0,2);
for(int i = 0;i < num;i++)
{
string bookname = bo.borrowrecording[i]->Name_book();
if(u->name == bo.borrowrecording[i]->Username())
{
if(s == bookname)
{
bo.borrowrecording[i]->RewriteDate(r_time);
u->borrowbookname.erase(std::remove(u->borrowbookname.begin(), u->borrowbookname.end(), bookname), u->borrowbookname.end());
u->borrownum--;
SetCCPos(0,20);
cout<<u->name<<"成功归还"<<"<<"<<bookname<<">>"<<endl;
return;
}
}
}
SetCCPos(0,20);
cout<<u->name<<"未借阅"<<"<<"<<s<<">>"<<endl;
return;
}
else if(s[0] == '2')
{
s.erase(0,2);
for(int i = 0;i < num;i++)
{
if(s == bo.borrowrecording[i]->Isbn_book())
{
string bookname = bo.borrowrecording[i]->Name_book();
if(u->name == bo.borrowrecording[i]->Username())
{
bo.borrowrecording[i]->RewriteDate(r_time);
u->borrowbookname.erase(std::remove(u->borrowbookname.begin(), u->borrowbookname.end(), bookname), u->borrowbookname.end());
u->borrownum--;
SetCCPos(0,20);
cout<<u->name<<"成功归还"<<"<<"<<bookname<<">>"<<endl;
return;
}
}
}
SetCCPos(0,20);
cout<<u->name<<"未借阅"<<"<<"<<s<<">>"<<endl;
return;
}
}
代码解析:u->borrowbookname.erase(std::remove(u->borrowbookname.begin(), u->borrowbookname.end(), bookname), u->borrowbookname.end()); //该行代码的目的是彻底删除一个元素,从内向外看,首先调用了remove函数,传递了三个参数,分别是查找的开头,查找的结尾,目标内容,它会将要删除的元素移到容器的末尾,并返回一个指向新末尾之后第一个不匹配元素的迭代器,之后调用erase函数,删除remove返回的新结尾到u->borrowbookname.end()之间的所有元素
个人信息
-
修改密码
//UserManage.cpp
void ChangePassport(User* cu)//31
{
string p1,p2;
SetCCPos(8,4);
cin>>p1;
SetCCPos(9,5);
cin>>p2;
SetCCPos(0,7);
if(p1 == p2)
{
cu->key = p1;
cout<<"修改密码成功"<<endl;
}
else
{
cout<<"前后密码不一致,请重进"<<endl;
}
}
-
借阅记录
//BorrowandReturn.cpp
void ShowBorrowRecord(BorrowManage& bo,User* u)
{
cout<<endl<<"书籍名字 ISBN 借阅时间 还书状态"<<endl;
int num = bo.borrowrecording.size();
for(int i = 0;i < num;i++)
{
string uname = bo.borrowrecording[i]->Username();
if(uname == u->name)
{
cout<<bo.borrowrecording[i]->Name_book()<<"\t";
cout<<bo.borrowrecording[i]->Isbn_book()<<"\t";
cout<<bo.borrowrecording[i]->Date_borrow()<<"\t";
cout<<bo.borrowrecording[i]->Date_return()<<"\n";
}
}
}
-
退出登录
将arrow指向null,再在main中判断,如果arrow为空即返回登录界面
用户管理(需要管理员权限)
-
用户列表
用户信息是用链表储存,遍历链表即可
//UserManage.cpp
void printUserList(UserList* ul)//41
{
User* current = ul->head;
cout<<"用户名"<<" "<<"密码"<<" "<<"身份"<<" "<<"现借阅书数"<<endl;
while(current!= NULL)
{
cout<<current->name;
cout<<" "<<current->key;
cout<<" ";
if(current->iden == 0)
cout<<"管理员"<<" ";
else if(current->iden == 1)
cout<<"普通用户"<<" ";
cout<<current->borrownum<<endl;
current = current->next;
}
}
-
增加用户
新增节点,接到用户链表结尾
//UserManage.cpp
void addUser(UserList* p)//42
{
User* current = new User;
current->next = nullptr;
SetCCPos(7,4);
cin>>current->name;
SetCCPos(6,5);
cin>>current->iden;
current->key = "000000";
current->borrownum = 0;
SetCCPos(0,8);
if(CheckName(current->name,p))
{
p->tail->next = current;
p->length++;
p->tail = p->tail->next;
cout<<"添加成功"<<endl;
}
else
{
delete current;
cout<<"用户名已被注册,请重进"<<endl;
}
}
-
删除用户
删除该用户的节点,并且删除该用户的借阅记录
//UserManage.cpp
void delUser(UserList* p,BorrowManage& bo)
{
string dname;
SetCCPos(7,4);
cin>>dname;
User* del = LoacteUser(dname,p);
User* temp = p->head;
while(temp->next != del&&temp!=nullptr)
{
temp = temp->next;
}
if(temp == nullptr)
cout<<"找不到该用户"<<endl;
if(del->next ==nullptr)
{
p->tail = temp;
temp->next = del->next;
p->length--;
int num = bo.borrowrecording.size();
for(int i = 0;i < num;i++)
{
if(bo.borrowrecording[i]->Username() == dname)
bo.borrowrecording.erase(bo.borrowrecording.begin()+i);
}
delete del;
SetCCPos(0,6);
cout<<"删除成功"<<endl;
return;
}
else if(del->next != nullptr)
{
temp->next = del->next;
p->length--;
int num = bo.borrowrecording.size();
for(int i = 0;i < num;i++)
{
if(bo.borrowrecording[i]->Username() == dname)
bo.borrowrecording.erase(bo.borrowrecording.begin()+i);
}
delete del;
SetCCPos(0,6);
cout<<"删除成功"<<endl;
return;
}
SetCCPos(0,6);
cout<<"未查找到该用户,请重进"<<endl;
}
-
重置指定用户密码
//UserManage.cpp
void resetKey(UserList* p)
{
string rname;
SetCCPos(7,4);
cin>>rname;
User* reset = LoacteUser(rname,p);
reset->key = "000000";
SetCCPos(0,7);
cout<<"重置成功"<<endl;
}
排行榜
-
热门图书
思路:创建一个借书排行榜类,再创建一个管理借书排行榜类,借书排行榜对象中储存着这本书的书名,isbn和被借阅的次数,每个 借书排行榜对象 的地址装在 管理结束排行榜对象 的vector里,再依据被借阅次数在vector里从大到小重新排序,再输出前十个当作前十排行榜
//head.h
class BorrowBookRank
{
public:
BorrowBookRank(string bookna,string isb):number(1)
{
bookname = bookna;
isbn = isb;
}
string getBookname(void)
{
return bookname;
}
int number; //被借阅次数
private:
string bookname; //书名
string isbn;
};
class BorrowBookRankManage
{
public:
vector <BorrowBookRank*> borrowbookrank;
void addrankbook(BorrowBookRank* bo)
{
borrowbookrank.push_back(bo);
}
};
//Ranking.cpp
bool cmp(BorrowBookRank* a,BorrowBookRank* b)
{
return a->number>b->number;
}
void HotBookRank(BorrowManage& bo)
{
BorrowBookRankManage bbrm; //管理排行榜
int records = bo.borrowrecording.size();
vector <string>exist; //记录用户借过的书
vector<string>::iterator iter;
for(int i = 0;i < records;i++)
{
string recordbookname = bo.borrowrecording[i]->Name_book();
iter = std::find(exist.begin(), exist.end(), recordbookname); //查找是否已经在exist中,防止该书重复添加
if (iter == exist.end())
{
exist.push_back(recordbookname);
BorrowBookRank* temp = new BorrowBookRank(recordbookname,bo.borrowrecording[i]->Isbn_book());
bbrm.addrankbook(temp); //添加到排行榜里
}
else
{
int num = bbrm.borrowbookrank.size();
for(int j = 0;j < num;j++)
{
if(recordbookname == bbrm.borrowbookrank[j]->getBookname())
{
bbrm.borrowbookrank[j]->number++; //被借阅书加一
break;
}
}
}
}
sort(bbrm.borrowbookrank.begin(),bbrm.borrowbookrank.end(),cmp); //排行榜依据被借阅数排序
cout<<"图书排行 借阅人数"<<endl;
if(bbrm.borrowbookrank.size() == 0)
return;
for(int i = 0;i < 10;i++)
{
string na = bbrm.borrowbookrank[i]->getBookname();
while(na.length()<40)
na+=" ";
cout<<na<<bbrm.borrowbookrank[i]->number<<endl;
}
}
-
借书次数
//Ranking.cpp
struct CompareBySize
{
bool operator()(const short& a, const short& b) const
{
return a > b;
}
};
void CrazyBorrwoMan(UserList* ul)
{
User* current = ul->head;
multimap<short,string,CompareBySize> rankuser; //用户借阅数排行榜
int num = ul->length;
while(num--)
{
rankuser.insert(make_pair(current->sumbooknum,current->name)); //将用户名与借阅数之间形成映射
current = current->next;
}
cout<<"用户名\t\t\t\t借阅数"<<endl;
int count = 0;
for(auto it = rankuser.begin();it != rankuser.end()&&count<10;it++,count++)
{
cout<<it->second<<"\t\t\t\t"<<it->first<<endl;
}
}
使用multimap而不用map和其他是因为如果两个用户的借阅数相同,用map就会将前一个映射覆盖掉,而使用multimap可以储存多重映射,允许借阅数重复
-
热门图书
思路:先获取各书籍的出版时间,再创建出版时间对象,然后将出版时间对象与书籍名称形成映射存入mutimap中,(需事先重载>运算符),然后依据日期大小排序,然后输出前十
//Ranking.cpp
struct CompareBySize_date
{
bool operator()(const Date& a, const Date& b) const
{
return a > b;
}
};
void LatestBook(BookList* p)
{
Book* current = p->head;
multimap<Date,string,CompareBySize_date> rankdate; //最新书籍排行榜
int num = p->length;
Date** dd = new Date*[num];
for(int k = 0; k < num;k++)
{
string d = current->publisheddate;
short year = 0,month = 0,day = 0;
int count = 0;
int dl = d.length();
for(int i = 0;i < dl;i++) //获取该数的出版时间
{
if(d[i]>='0'&&d[i]<='9')
{
if(count ==0)
year = year*10 + (d[i]-'0');
else if(count == 1)
month = month*10 + (d[i]-'0');
else if(count == 2)
day = day*10 + (d[i]-'0');
}
else
count++;
}
dd[k] = new Date(year,month,day); //创建出版日期对象
rankdate.insert(make_pair(*dd[k],current->bookname)); //将书籍与出版日期之间形成映射
current = current->next;
}
int count1 = 0;
for(auto it = rankdate.begin();it != rankdate.end()&&count1<10;it++,count1++)
{
string na = it->second;
while(na.length()<50)
na+=" ";
cout<<na<<"\t\t\t\t"<<it->first<<endl;
}
for(int i = 0;i < num;i++)
delete dd[i];
delete[] dd;
}
四、源代码资源
在主页上传的资源里