开发环境
学委作业管理系统在 Microsoft Visual Studio 2013 编译器开发的 MFC 项目,计算机使用的系统是 window10。
1.2 基本原理与技术要求
熟悉文件读写、mfc 基本知识、c++ 类运用、链表使用、排序算法、Microsoft Visual Studio 2013 编译器的使用。
1.3 需求说明
学委作业管理系统要求实现五大功能部分,分别是身份登录部分、管理员部分、学委部分、学生部分、文件储存部分。
身份登陆的功能部分,要求进入者进行身份认证,防止个人信息泄露或他人篡改数据;管理员部分的功能有对班级的增删改查、班级里的学生的增删改查功能,学委、学生的身份设置;学委部分的功能比较多,老师布置的作业发布给学生,对发布的作业有增删改查的功能、对学生提交的作业有增删改查的功能,对学生作业的查询还可以按照不同的方式进行排序、学生提交情况的浏览;学生部分的功能,查看学委发布的作业和学生本人的提交情况;文件存储的功能部分,对系统正常运行提供数据支持。
二、程序概要设计
2.1 各功能完成情况
系统已经完成了需求的所有功能。
为完成功能需求,学委作业管理系统设置了四个对话框窗口,分别为登陆窗口、管理员窗口、学委窗口、学生窗口;五个类保存数据,分别是 Assignment 类、Homework 类、ClassList 类、Course 类、Person 类。窗口具体实现代码分别在 JCSDlg 类、adDlg 类、studiesDlg 类、strudentDlg 类中,完成了相应的响应和具体功能实现。
登陆窗口,用户需要输入正确的账号和密码并选择身份才能登陆成功。当用户输入错误信息时,弹出显示“输入有误”的提示框。
管理员窗口,实现对班级和学生的增删改查功能。管理员的账号和密码均为 000,由程序员设置,用户不能修改。程序从文件中读取班级信息和学生信息,班级信息显示在设置的班级 listBox 中,当选择某班级时,在学生 listBox 中显示该班学生;添加班级功能在对应的输入框中输入班级名称,当该班级在数据文件中未存在时,保存该班级,若该班级已存在,则弹出“已存在班级,不可重复”的提示框;查询班级,当班级存在时,在班级 list Box 中该班级高亮显示,若不存在则弹出“未找到班级”的提示框;修改班级,该功能用于修改已有的班级名称,当输入班级未在数据文件中找到时,弹出“未找到班级”的提示框;当找到时提示修改成功;添加或修改学生信息功能,用户填写相关信息内容,当选择添加功能时,遍历数据文件,查看学生学号是否重复,若文件中未有则添加成功,否在提示失败。当选择修改功能时,以学号作为基础,找到该学生,修改除学号以外的信息;查询学生功能与查询班级功能一样;删除功能用户在 list Box 中选择需要删除的信息,然后按下删除键即可。
学委对话窗口,实现对作业和学生提交的作业增删改查功能。程序默认当用户进入学委对话框时学生或课程 listBox 显示课程名称,学生可以在 ComboBox 中选择显示学生或课程,当在学生或课程 List Box 选择相应的选项时该选项的内容会在详细内容 ListBox 展示;查询功能在时间范围或课程名称中输入对应的时间范围或作业课程名称单击排序顺序单选按钮即可查到相关信息;学生提交作业查询同理;发布或更改作业,填写相关信息,然后点击发布或更改即可完成相应功能并把添加或更改的数据更改到数据文件中保存;更改或添加同学提交的作业也同理;作业统计功能,显示每个学委发布的作业提交人数和班级总人数。
学生对话窗口,实现学生查看和提交作业的功能,提交情况 listBox 显示该学生已经提交了的作业数据;提交作业,学生在提交作业部分填写相应的内容点击提交即可保存到数据文件中;作业公告,显示学委发布的作业,提醒学生做作业。
数据保存部分,Person 类保存个人信息、Assignment 类保存学委发布的作业信息、Homework 类保存学生提交信息、ClassList 类保存班级名称信息、Course 类保存课程名称信息。Person 类信息、Assignment 类信息和 homework 类信息以班级名称、学生学号作为中间媒介联系起来,组成完整的信息网络,完整保存每个学生的个人信息。
2.2 程序流程图
三、程序详细设计
3.1 界面设计与数据传输
通过创建 MFC 项目,以对话框作为窗口的主体,使用了静态文本框、编辑框、组合框、列表框、单选框、按钮等控件。
从界面输入的数据有两种方法可以在后端代码中获取,一种是调用 UpdateData(bool *bl)方法,把 bool 值设为 true 则可在编辑框绑定的变量中获取到对应输入的数据。另一种是通过 GetDlgItem(int nID)->GetWindowText(CString cstring)获取到编辑框的数据并赋值给 cstring 提供给程序运行。下面介绍的各种操作需要从界面获取信息的均以这两种方法。
3.2 数据保存与使用
数据的保存采用文件保存方式。在 mfc 中类之间很难通过传参的方式进行数据传输,所以采用了静态指针进行全局传参。把读取到的数据存放到链表中,用静态指针指向链表头。这样就可以通过静态指针,在其他类中调用其他类中读到或改变的数据了。
文件写入:
以 Person 为例,Person 的文件写入,写在了 Person 的 writePerson 成员方法里。
intPerson::writePerson(Person*person){FILE*fp;if((fp=fopen("person","wb"))==NULL)return1;CStringcstr(" ");while(person!=NULL){fprintf(fp,person->num);fprintf(fp,cstr);fprintf(fp,person->name);fprintf(fp,cstr);fprintf(fp,person->className);fprintf(fp,cstr);fprintf(fp,person->pw);fprintf(fp,cstr);fprintf(fp,person->status);person=person->next;if(person==NULL)break;fprintf(fp,cstr);}
成员方法 writePerson 接收到的链表头指针,创建一个文件指针用于打开指向的文件,调用 fopen 函数打开 person 文件,文件的打开方式是“wb”。实例一个 CString 用于存放空格,该空格用于间隔类成员在文件中的位置,该操作是为了方便文件读取。使用 while 循环遍历传入的链表的每一个结点,调用 fprintf 函数写入节点的类的成员并在下面写入上面创建的空格。到最后一个类成员使用 if 语句判断链表是否已经到了链表末尾,如果到链表末尾则不再写入空格
其他的 homework、assignment、classList、course 均以这种方法进行写文件。
文件获取:
Person*readPerson=newPerson();readPerson->readPerson(readPerson);Person::person=readPerson;Person*per=Person::person;
在 JCSDlg 类中调用 Person 类的 readPerson 方法,读取 person 的信息,并把头指针赋值给静态 Person 指针,以便其他函数调用。其他的 homework、assignment、classList、course 等文件读取也是这样
在 person 类中的 readPerson 方法,具体代码如下:
intPerson::readPerson(Person*person){FILE*fp;CStringstr;charbuf;intstatus=0;intjudge=1;if((fp=fopen("person","rb"))==NULL)return1;status=fread(&buf,sizeof(char),1,fp);str.Format("%c",buf);person->num+=str;while(status>0){status=fread(&buf,sizeof(char),1,fp);str.Format("%c",buf);if(status>0&&str==" "){judge++;if(judge==6){person->next=newPerson();person=person->next;judge=1;}}elseif(status>0&&judge==1)person->num+=str;elseif(status>0&&judge==2)person->name+=str;elseif(status>0&&judge==3)person->className+=str;elseif(status>0&&judge==4)person->pw+=str;elseIf(Status>0&&Judge==5)Person->Status+=Str;}rewind(fp);fclose(fp);return0;}
readPerson 成员函数从参数中接收到链表头,创建文件指针 fp 用于指向文件、CString 类型变量 str 用于暂存读出来的字符串、char 类型变量 buf 用于存放从文件中读出的字符、int 变量 status 和 judge 变量,分别用于判断文件读出情况和读出的字符串一个要赋值给 person 类的哪一个变量。程序首先调用 fopen 函数 person 文件,打开方式为 rb。调用 fread 函数把读出的字符赋值给 buf 且把当前状态赋值给 status,str 调用 Format 函数把读出的字符转成 CString 类型,在把 str 连接到 person 的第一个成员变量中,当 status>0 是进入 while 循环,每当 while 循环一次就调用 foread 函数把读出的字符赋值给 buf 且把当前状态赋值给 status,str 调用 Format 函数把读出的字符转成 CString 类型,当转换为 CString 时可以分六种情况,的一种是判断 status 是否大于 0 且 str 是否等于空格,如果是 judge++,然后判断 judge 是否大于 6,如果是创建新结点,把上一结点的 next 赋值为该节点的地址并把 judge 赋值为 1。其他的 else if 用于判断 str 应该连接到 person 的哪一个成员变量中,这里就不一一细说了。
3.3 用户登陆
从上面文件读取可以知道,读取的信息链表有对应的静态指针指着链头,利用链头遍历 person 数据获取每个用户的密码、账号和身份,与用户输入的账号、密码和单选框的身份选择是否匹配,若匹配,设置 person 的静态 who 指针指向登陆用户。但如果为管理员用户,则有静态 who 指针指向该结点。创建子窗口并进入用户界面。
以学委登陆为例:
per=Person::person;while(per!=NULL){if(per->num==num&&per->pw==pw&&per->status=="学委"){Person::who=per;StudiesDlg*studies=newStudiesDlg();studies->Create(IDD_DIALOG3,this);studies->ShowWindow(SW_SHOW);judge=0;break;}per=per->next;}
程序获取通过 Person 的静态链表把指针链表头赋值给 per,用 whil 循环遍历 per,每遍历一个节点就调用一次 if 语句判断输入的账号、密码是否正确,身份是否是学委,若不正确,通过节点移动,遍历下一个节点直至到节点全部遍历完。若正确,把该节点赋值给 Person 的一个静态指针 who,以便在学委的用户界面类中可以辨别出哪位学委登陆。创建 StudiesDlg 类指针 studies,用于实例学委用户界面类,studies 调用 Create 方法创建窗口,调用 Showindow 参数为 SW_SHOW 设置窗口打开方式,judge 是用来判断是否登陆成功。
3.4 删除功能
当选择删除按钮是,系统将执行相应的删除操作,学委作业管理系统有许多的删除操作,但大致都用了相同的算法。再 listBox 中获取要删除的内容,把内容分隔成相应的一个类成员对应的数据,以这些数据作为判断条件找去要删的数据在链表中的位置,并删除该节点。
代码如下:
ClassList*cl=ClassList::classList;ClassList*copy=cl;while(cl->next!=NULL){if(cl->className==className){if(copy!=cl){copy->next=cl->next;deletecl;//释放删掉的空间
cl=ClassList::classList;cl->writeClassList(cl);m_clasListBox.DeleteString(index);MessageBox("删除成功!");return;}else{cl=cl->next;deletecopy;cl->writeClassList(cl);m_clasListBox.DeleteString(index);ClassList::classList=cl;MessageBox("删除成功!");return;}}copy=cl;cl=cl->next;}
这段代码是以删除班级为例,从静态指针中获取班级链表表头,并使用指针 copy 保存指针副本,从表头首节点开始遍历。每遍历一个节点就调用 if 判断该节点是否为要删除节点,若不是 copy 指针保存该节点的副本,表头 cl 指针向下移,直到表头指针为 NULL。若满足 if 条件,则要分两种情况,第一种是副本 copy 等于头指针,说明该要删除的节点为链表的首节点,这时使链表的头指针 cl 下移,调用 ClassList 类的 writeClassList(ClassList *classList)方法保存数据,调用 delete 语句删除 copy 指针所指向的节点,并提示删除成功。另一种情况使该节点不使链表的首节点,copy 保存了上一节点的地址,把 copy->next 赋值为 cl->next 使链表跳过要删除的 cl 节点,直接与 cl 下一个节点相连,然后使用 delete 语句输出 cl 指向节点的空间,完成删除功能并提示删除成功。
其它的学生、课程、学委发布的作业、学生提交的作业的删除的算法大致一样,这里就不一一细说了。
3.5 添加功能
当填入相应信息并按下添加按钮时,程序将执行添加功能。添加功能先以输入的内容作为判断条件遍历相应的链表,查看添加的信息是否在链表中已经存在。若存在,则弹出提示已经存在。若不存在,则执行添加功能,把添加信息添加到链表中并保存到文件和显示到 listBox 中。
以添加班级为例:
while(head->next!=NULL){if(head->className==className){MessageBox("已存在班级,不可重复!");return;}head=head->next;}ClassList*cl=newClassList();cl->className=className;head=ClassList::classList;head=ClassList::classList;if(head==NULL){head=cl;head->writeClassList(head);ClassList::classList=head;m_clasListBox.AddString(className);MessageBox("添加成功!");return;}cl->next=head->next;head->next=cl;m_clasListBox.AddString(className);head->writeClassList(head);MessageBox("添加成功!");
当链表为空链表时,需把静态指针指向新创的对象,并保存到文件中;当链表不为空时,把新创的类添加到链表中,并重新保存链表到文件中。在对应的 ListBox 中显示添加的信息。
3.6 修改功能
班级名称修改是先输入原班级名称和新班级名称,到链表中匹配是否存在原班级,若存在,修改班级名称和在修改 person 类链表中学生的班级信息也同样要改变,并保存到文件中。若没有则弹出提示。
其他的学生添加作业修改、学委发布作业修改、学生修改,都是以特定的能唯一标识信息作为判断条件到对应链表中查找是否有该标识在链表中的某一结点,除了唯一标识外,其他的由新输入的数据代换,并保存到文件中。特别在作为管理员修改学生信息时,还要修改该学生提交的作业,把作业的班级改为修改后的班级。
以修改班级为例:
ClassList*cl=ClassList::classList;while(cl!=NULL){if(cl->className==beforName){cl->className=afterName;break;}cl=cl->next;}cl=ClassList::classList;cl->writeClassList(cl);Person*per=Person::person;while(per!=NULL){if(per->className==beforName){per->className=afterName;}per=per->next;}per=Person::person;per->writePerson(per);MessageBox("修改成功!");
3.7 查找与排序
班级名称和学生查找是在管理员窗口中完成的,当用户输入需要查找的信息并点击查找,程序以输入的信息为判断条件找出链表中是否有该查找信息。若链表中存在该消息,则会显示在 listBox 中。
学委发布的作业或学生提交的作业有两种查找方式,一是通过课程名称,二是通过时间范围。学委发布作业以时间为依据进行升降排序,学生提交作业以时间或提交次数为依据进行升降排序。链表的排序程序主要使用了冒泡排序。学生提交作业排序依据是在 ComboBox 选择的,升降排序是由单选框选择的。学委发布作业以时间为依据是程序写定的,时间是作业发布时间。
以课程名查找学生提交为例:
Homework*ho=newHomework();Homework*copy=newHomework();Homework*head=ho;Homework*homework=Homework::homework;Person*per=Person::who;Homework*copy=newHomework();while(homework!=NULL){if(homework->course==course&&homework->className==per->className){*copy=*homework;ho->next=copy;//第一个为空
copy=newHomework();ho=ho->next;}homework=homework->next;}
程序开始通过静态指针获取作业链表和登陆学委的结点指针,创建 ho 和 copy 指针并实例,创建 head 指针指向 ho 记录头指针位置。ho 为查找出来的作业链表的头结点,copy 为作为结点的存放结点调用 while 语句循环遍历 homework,每遍历一个结点就调用一次 if 语句判断作业的课程名和输入的是否一致、作业的所属班级是否与学委即是 who 的一致,若不正确则遍历下一个结点直至 homework 链表结尾。若正确把该结点赋值给副本 copy 对象,使 ho 指向的对象的 next 成员指针指向副本 copy 结点,ho 指针往下移一个结点。
冒泡排序代码如下:
for(p=head;p!=NULL;p=p->next){for(q=head;q->next!=NULL;q=q->next){if(q->time<q->next->time){*t=*q;=q->next;q->num=h->num;q->course=h->course;q->No=h->No;q->time=h->time;q->remark=h->remark;q->name=h->name;q->times=h->times;h->num=t->num;h->course=t->course;h->No=t->No;h->time=t->time;h->remark=t->remark;h->name=t->name;h->times=t->times;}}}
排序调换的实质是将链表前后节点的内容调换,而节点指针并没有变化。
3.8 作业情况统计
作业情况统计功能是统计班级人数和学委发布每一科的作业完成情况,然后把统计情况以文本形式输出到对话框的文本框中,提供给用户阅览。
人数统计是以学委班级名称为判断依据和设置一个 int 型数据 number 纪录学生人数,使用 while 循环遍历 person 链表,while 循环每遍历一个结点就调用一次 if 语句判断该同学是否与学委在同一班,若是设置一个 number 自增加一记录人数。
统计学委发布每一科作业的完成情况,具体代码如下:
while(course!=NULL){while(ass!=NULL){if(ass->course==course->courseName&&ass->className==who->className){times=0;while(homework!=NULL&&homework->className==who->className){if(homework->No==ass->No){times++;}homework=homework->next;}CStringstr;str.Format("%d",times);content+="课目:"+ass->course+" 作业编号:"+ass->No+" 提交数目:"+str+"次"+"\r\n";}homework=Homework::homework;ass=ass->next;}ass=Assignmen::assignmen;course=course->next;}GetDlgItem(IDC_EDIT18)->SetWindowTextA(content);
完成学委发布的每一科作业的学生作业情况提交统计功能,程序运用了三层 while 循环。最外面的是 course 链表遍历,中间层的是 assignment 链表遍历,最底层的是 homework 链表遍历。该程序的大致意思是遍历每一科目,根据科目名称作为中间层 while 循环内 if 语句的判断依据,中间层 while 的 if 除了科目名称外还有班级名作为判断依据,通过筛选获得对应的作业编号。在最内层,遍历 homework 链表,每遍历一个结点调用一次 if 语句,以中间层得到的作业编号和学委的班级名称作为判断依据,如果符合条件,times++ 记录次数,当最内层遍历接收把 times 转成 CString 并添加到 content 的 CString 中作为内容输出。
最外层用多一个 course 链表做循环遍历,是因为当删除科目是 course 链表删除该科目,但 homework 链表和 assignment 链表并没有删除该科目的相应信息,作业情况统计要做的是统计现有的科目作业完成情况,使用 course 链表作业筛选,去掉不必要的信息。
四、使用情况
4.1 登陆界面使用情况
登陆使用情况,如右图所示。账号为可显示的输入框,密码为隐秘数字后的输入框。管理员、学委、学生为单选框,点击登陆,账号、密码和身份正确后即可顺利进入相应的应用界面
4.2 管理员界面使用情况
管理员界面使用情况,如上图所示。各项功能使用正常。当点击班级 listBox 时,修改班级的初始会出现选择的班级名称,学生 listBox 出现该班级学生名单。当点击学生 listBox 时,下面修改或添加项目的编辑框会出现该学生的相应信息。
4.3 学委界面使用情况
学委界面使用情况,该界面可分为浏览区、查找区、发布或更改作业区、更改或添加同学提交的作业区、作业提交情况统计区。在浏览区用户可以查看同学提交的作业和每个课程发布的作业,当点击详细内容 listBox 时,里面的内容会出现在相应的编辑框中;查找区可以分为学委发布的作业查找和学生提交的作业查找。
4.4 学生界面使用情况
学生界面使用情况,可分提交情况区、提交作业区、作业公告区。
提交作业情况区展示提交该同学作业的提交情况。提交作业区用于学生提交作业。作业公告用于展示学委发布的作业。
完整项目代码:https://download.csdn.net/download/weixin_55771290/87395506