1. 逻辑设计
设计从分析输入数据着手,输入数据中的某类相关数据可以归纳为一个表,对需要同时调用的若干表,应使它们符合关联要求。数据库设计好后,可以通过分析输出数据来验证其可用性,若发现有的输出数据不能从输入数据导出,须继续向用户征集数据。
本例根据学生信息表和课程基本信息表、教师信息表等输入单据中归纳出包括2个表的数据库,现将这些列出如下:
(1)学生单:xs (学号,姓名,性别,出生日期,是否团员,电话,通信地址,邮编,总成绩,备注)
(2)课程单:kc (课程号,课程名,学期,学分,教师,所在系)
(3)教师单:js (编号,姓名,性别,学历,职称,所授课程,联系方式,备注)
以上括号外的符号串是表名,括号外为字段名表,有下划线的字段为关联关键字,根据系统数据处理的需要,这些表的关联情况如图1.1所示。
图中用矩形框表示表,需要关联的两个表用线段连接,在线的一端标出了关联关键字,表明必须在这一端的表中建立索引。
老师
学时数课程
成绩学生
图1.1 表间关联的设计
① 同时调用不同表中的数据,须将它们关联,故而有时要在表中补充字段。
② 数据库设计须注意合理性。若将不同类的数据放进同一个表中,可能会产生数据余。表的分拆往往能减少数据余,但表的个数增多又会增加程序的复杂性,因为须在不同的工作区打开这些表,而且为了实现数据联用定要对表进行关联。
1、物理设计
下面列出学生学籍管理系统所有表的结构与必须的索引,以便于读者理解本例系统,顺便也列出表的部分记录,详细见xs, kc,js。
1.学生单(XS.DBF)
字段名类型宽度字段名小数位数
学号C字符型4学号
姓名C字符型8姓名
性别C字符型2性别
出生年月D日期型8出生年月
是否党团员L逻辑型1是否党团员
电话C字符型16电话
通信地址C字符型30通信地址
邮编C字符型6邮编
总成绩N数值型6总成绩2
备注M备注型备注
2.课程单(KC.DBF)
字段名类型宽度索引
课程号C字符型8V
课程名C字符型30
学期C字符型2
学分D日期型2
教师C字符型30
所在系C字符型30
3.教师单(JS.DBF)
字段名类型宽度索引
编号C字符型4V
姓名C字符型8
性别C字符型2
职称D日期型20
学历L逻辑型10
所授课程C字符型50
联系方式C字符型30
备注M备注型
学生学籍管理系统的运行环境是Windows 9x/Me/2000/XP+Microsoft Visual FoxPro 6.0。
学生学籍管理系统的具体设计如下:
1、学生学籍管理系统主程序(A:\main.prg)
功能:提供程序的主界面和进入系统各功能模块的接口,包括一个自右向左滚动的飞字程序。
源程序:
set talk off &&关闭人机对话
set escape off &&设置ESC脱离键不起作用,即误按该键不会终止程序的执行
clear all &&清屏
set colo to 0/7,7/0 &&设置VFP系统的默认颜色
clear &&清屏
@4,42 clear to 30,92
set colo to 7+/0+ &&从第4行第42列到第30行第92列产生白字黑底的色块
@4,42 clear to 30,92 &&显示色块
@3,40 clear to 29,90
set colo to 7+/4 &&从第3行第40列到第29行第90列产生白字黑底的色块
@3,40 clear to 29,90 &&显示色块
@4,42,28,88 box space(9) &&从左上角第4行第42列到右下角第28行88列产生线框
@7,52 say'==学生学籍管理系统==' &&在第7行第52列显示学生学籍管理系统的字样
@8,50 say'-------------------------------' &&在第8行第50列显示分隔线条
@9,57 say'==1.录入数据==' &&在第9行第57列显示1.录入数据字样
@11,57 say'==2.修改数据==' &&在第11行第57列显示2.修改数据字样
@13,57 say'==3.查询数据==' &&在第13行第57列显示3.查询数据字样
@15,57 say'==4.统计数据==' &&在第15行第57列显示4.统计数据字样
@17,57 say'==5.显示数据==' &&在第17行第57列显示5.打印数据字样
@19,57 say'==6.删除数据==' &&在第19行第57列显示6.退出数据字样
@21,57 say'==7.退 出==' &&在第21行第57列显示7.退出字样
x=1 &&设初值x=1
b1="◆欢迎您使用学生学籍管理系统,请按任意键继续◆重庆工学院吴天美、谭茂燕、袁瑶、王洪梅、曾玲研制。 &&将字符串赋给变量b1
do while.t. &&当条件成立时计算机做下面的语句
hz1=substr(b1,x,29) &&取字符串b1从第1个字符到第29个字符赋给变量hz1
@25,51 say hz1 &&在第25行第51列显示字符串hz1
bb=inkey(0.6) &&将等待0.6秒击键的inkey 函数赋给bb
if bb<>0 &&变量bb不等于0,即有击键动作发生
exit &&那么,不显示字符串
endi &&与if条件判断语句相配对
x=x+2 &&将变量x的值加2赋给x
if x>83 &&如果变量x大于83个字符
x=1 &&那么,将1赋给变量x,即从头开始显示字符串
endi &&与if条件判断语句相配对
endd &&与do while循环语句相配对
@27,51 say'请选择1--7:' &&在第27行51列显示请选择1-7:的字样
wait ' ' to k &&计算机等待用户从键盘输入一个字符给变量k
do case &&做选择语句
case k='1' &&当变量k的值等于1时
do a:\input &&计算机跳转到a:\input子程序
case k='2' &&当变量k的值等于2时
do a:\modify &&计算机跳转到a:\modify子程序
case k='3' &&当变量k的值等于3时
do a:\search &&计算机跳转到a:\search子程序
case k='4' &&当变量k的值等于4时
do a:\total &&计算机跳转到a:\total子程序
case k='5' &&当变量k的值等于5时
do a:\dp &&计算机跳转到a:\dp子程序
case k='6' &&当变量k的值等于6时
do a:\del &&计算机跳转到a:\del子程序
case k='7' &&当变量k的值等于7时
set colo to 0/7,7/0 &&设置VFP系统的默认颜色
clear &&清屏
retu &&返回VFP主程序
endcase &&与do case选择语句相配对
2、环境配置
程序名称:SETTING.PRG
set sysmenu off
set sysmenu to
set status bar off
set talk off
set notify off
set clock status
set palette off
set bell on set safety off
set escape on
set keycomp to windows
set carry on
set confirm on
set exact on
set near on
set ansi off
set lock on
set exclusive off
set multilocks on
set deleted on
set optimize on
set refresh to 0,5
set collate to ‘stroke’
set default to sys(5)+curdir()
set path to sys(5)+curdir()
set sysformats off
set seconds on
set century off
set currency left
set currency to ‘nt$’
set hours to12
set date to usa
set decimals to 2
set fdow to1
set fweek to 1
set mark to ‘.’
Set separator to ‘.”
Set point to ‘.’
环境还原
程序名称:RESET.PRG
set sysmenu to default
set sysmenu on
set talk on
set notify on
set exclusive on
set safety on
modity window screen
3.密码功能的设置
密码功能包括输入密码、修改密码、添加用户、删除用户。只有当用户正确输入本人的密码后才能进入系统,更改密码也只允许更改自己的密码。只有单位的主管人员才有权添加用户和删除用户。为此设置了用户等级,这也是由主管人员设定的。用户等级不同,权限就不同。如用户等级为4,仅能进行档案输出的操作。
(一)设计思路:
(1)建立一个数据库:密码库,
(2)并创建表checker.dbf,
(3)其中字段公别为Cpassword(密码)、Name(合法用户名)、Rank(权限等级)。
(二)输入密码表单的设计:
<1>通过组合框(combo box)选择合法用户,并在其Interactivechange事件中编写代码令输入密码的文本框得到焦点(允许输入值了),该事件发生在当控件的value值改变时。
<2>在“确定”按钮的click事件中编程,先在表中寻找用户名(用locate for语句),再通过判断输入密码与数据库中相应值是否相等来判断密码是否正确。若错误可重新输入,但设置局部变量I来累计输入次数,可设定只能输入三次,当I大于3就释放该表单。并弹出消息框说明不能进入本系统。在主程序中设有全局变量yhdj,若输入密码正确则给yhdj赋值为用户等级字段。
<3>添加“说明”标签,说明密码输入的规定。为增加美观性,可使用Active X控件Three Frame Control。
<4>Active X控件的添加方法与添加自定义类的方法相同。
<5>将标签添加其中,构成说明框。
<6>若希望当鼠标在按钮上停留一秒后有提示框,则可设置表单的Show Tips属性为.T.,按钮的Tooltip Text属性为希望显示在提示框中的内容。
<7>若希望鼠标在按钮上时改变形状,则可设置按钮的Mouse Pointer属性为:99-自定义,Mouse Icon属性为希望出现的图标文件。
(三)密码表单的设计
<1>修改密码部分与上一表单相同,当输入密码正确后方可输入新的密码或进入系统:若用户等级较高则还可进入增删用户表单。这些均在输入密码文本框的Lostfocus事件中编程。该事件在该文本框失去焦点时发生。
<2>输入新密码后检验密码不能为空,这在该文本框的Lostfocus事件中编程实现。
<3>还设置了确认密码框,防止偶然输入错误。当输入密码与确认密码不同时让用户重新输入。这些是按钮“确定”的Click事件中编程解决的。若无错误,单击“确定”钮后,用replace……with语句代替原来密码。设置缓冲区环境,可以通过前述的tableupdate()和tablerevert()函数真正修改或放弃修改密码。
(四)密码设置表单的设计
密码设置表单的设计,包括增加用户和删除用户。
<1>用页框分别完成这两项工作。一页增加用户,另一页删除用户。
<2>增加用户时未使用缓冲区,而用skip-1、ship代替,这样做同样可以把结果直接存入相应的表中。
4.权限设置:
关于用户的权限问题,因在主程序中设置了全局变量,又在输入密码和修改密码表单中为其赋了值,故在表单操作中只需判断其值就可以完成权限的配置。而在菜单中也需要限制,它也是通过这个全局变量的值,使用菜单的跳过(skip)项实现的,具体将在菜单的设计中介绍。
5、录入模块子程序(A:\input.prg)
功能:提供卡片式的录入数据界面。
源程序:set talk off
use a:\xj &&打开a:\xj.dbf数据库文件
go bott &&将记录指针移到数据库的末尾
skip &&跳到下一条记录
hk=recn() &&将当前记录号赋给变量hk
hk1=str(hk,3) &&将变量hk值的前三位,由数值转换为字符串赋给hk1
c1=.t. &&将逻辑型变量“真”赋给变量c1
do while c1 &&当条件成立时计算机做下面的语句
a1=spac(2) &&将2个空格赋给变量a1
a2=spac(6) &&将6个空格赋给变量a2
a3=spac(2) &&将2个空格赋给变量a3
a4=spac(8) &&将8个空格赋给变量a4
a5=spac(2) &&将2个空格赋给a5
a6=spac(8) &&将8个空格赋给a6
a7=spac(30) &&将30个空格赋给a7
a8=spac(6) &&将6个空格赋给a8
a9=spac(6) &&将6个空格赋给a9
a10=spac(50) &&将50个空格赋给a10
kk=.t. &&将逻辑型变量“真”赋给变量kk
do while kk &&当条件成立时计算机做下面的语句
clea &&清屏
@1,5 say"输入第"+hk1+"张卡片" &&在第1行第5列显示输入第"+hk1+"张卡片的字样,其中,hk1为宏替换
hk=hk+1 &&变量hk的值增1
hk1=str(hk,3) &&将变量hk的值取3位赋给hk1
@row()+1,5 say"学号:"get a1 &&在当前行的下一行第5列显示学号字样
@row(),40 say"姓名:"get a2 &&在当前行第40列显示姓名字样
@row()+1,5 say"性别:"get a3 &&在当前行的下一行第5列显示性别字样
@row()+1,5 say"出生年月(MM/DD/YY):"get a4 &&在当前行的下一行第5列显示出生年月字样
@row()+1,40 say"是否团员:"get a5 &&在当前行的下一行第5列显示是否团员字样
@row()+1,5 say"电话:"get a6 &&在当前行的下一行第5列显示电话字样
@row()+1,40 say"通信地址:"get a7 &&在当前行的下一行第5列显示通信地址字样
@row()+1,5 say"邮编 :"get a8 &&在当前行的下一行第5列显示邮编字样
@row()+1,5 say"总成绩:"get a9 &&在当前行第40列显示是否总成绩字样
@row()+1,5 say"备注:"get a10 &&在当前行的下一行第5列显示备注字样
read &&将键盘所输内容读入内存
y="y" &&将字符y赋给变量y
@12,5 say"输入正确吗?(Y/N)"get y &&在第12行第5列显示输入正确吗字样
read &&并将输入的y或n读入内存
if y="Y".or.y="y" &&如果变量y值为大写的Y或小写的y
kk=.f. &&那么,将逻辑否赋给变量kk
endif &&与if 相配对
enddo &&与do while相配对
appe blan &&添加新记录到空白记录
replace 学号 with a1,姓名 with a2,性别 with a3,出生年月 with ctod(a4),是否团员 with a5
replace 电话 with val(a6),通信地址 with val(a7),邮编 with val(a8),总成绩 with a9,备注with a10 &&替换键盘输入的内容到相应的字段中
y=" " &&将一空格赋给变量y
clea
@14,5 say"继续输入吗?(y/n)"get y &&在第14行第5列显示继续输入吗字样
read &&并将输入的y或n读入内存
if y="N".or.y="n" &&如果变量y值为大写的N或小写的n
kk=.f. &&那么,将逻辑否赋给变量kk
exit &&退出输入界面
endif &&与if相配对
enddo &&与do while相配对
clear &&清屏
do a:\main &&运行A:\main.prg主程序
6、修改模块子程序(A:\modify.prg)
功能:提供卡片式的修改数据的功能。
源程序:set talk off
set delimiter off
use a:\xj &&打开数据库文件A:\xj.dbf
kk=.t.
do while kk
clear
go top
yy=space(2)
@4,20 say"请输入学号" get yy
read
loca all for 学号=yy
if eof()
@5,5 say" "
wait"该记录不存在!请按任意键返回!"
y="y"
clea
@6,5 say"继续修改吗?(Y/N)" get y
read
if y="Y".or.y="y"
loop
endi
clea
do a:\main
retu
endi
clea
kk=.t.
do while kk
clear
@row()+1,1 say"学号:" get 学号
@row(),40 say"姓名:" get 姓名
@row()+1,1 say"性别:" get 性别
@row()+1,1 say "出生年月:" get 出生年月
@row(),60 say"是否团员:" get 是否团员
@row()+1,1 say"电话:" get 电话
@row()+1,25 say"通信地址:" get 通信地址
@row(),25 say""邮编:" get 邮编
@row()+1,1 say"总成绩:" get 总成绩
@row(),60 say"备注(双击文本框输入内容):" get 备注
read
y=" "
@9,15 say"修改正确吗?(Y/N)" get y
read
if y="y".or.y="Y"
kk=.f.
else
loop
endi
enddo
clear
y=""
@5,15 say"继续修改吗?(Y/N)" get y
read
if y="n".or.y="N"
kk=.f.
endif
enddo
clea
do a:\main
7、查询模块子程序(A:\search.prg)
功能:提供按学号检索、按姓名检索、按学号和姓名检索和按学号、姓名、性别检索三个子模块的查询数据功能。
表单上的按钮功能包括:输入查询条件、查询、显示查询条件、清除查询条件、打印预览、打印和放弃。起初,除输入查询条件、显示查询条件和放弃外按钮均是不能选择的。当单击“输入查询条件”按钮时,表单元上的字段均为空,用户可以输入任意知道的条件做为查询条件,这些条件均是“与”的关系。这时,按钮也可以被选择了,若单击查询,查询结果就显示在表单的逐条浏览页上,同时,在该页上还显示了共查询到几条记录及可用移动记录按钮逐条查看记录。
显示查询条件按钮可以查看本次查询输入的条件;清除查询条件按钮可以清除本次查询的条件以备下一次查询;打印按钮将本次查询结果输出到打印机;放弃按钮则放弃本次查询并释放本表单元。
为了方便查看,还可以在表单的浏览佤页中一次查看到全部查询到的记录;并能在浏览字段设定页中设定佤浏览时该字段的显示与否。
设计思路:
(1)利用拖放操作形成标
(2)签对象和文本框对象。
(3)通过BEGIN TRANSACTION命令开始一次事务处理,
(4)通过ROLLBACK命令结束这一事务处理,
(5)并且将对表所作的各种更改存储下来。
(6)新增一条记录用于存储查询条件,
(7)并新建一个过程parsecondition,它返回一个表达式,形如“字段名所输入的值”。而后在查询按钮的Click事件中运用循环语句判断在哪个字段中输入了值,并将所有输入的备件用AND连接,作为用SET FILTER TO命令过滤记录的表达式和计算记录数的FOR语句的内容。
(8)在SET FILTER TO 语句和计算记录数的COUNT语句中都用到了宏代换符“&“,宏代换在编写代码时很有用,它返回一个变量的内容。在实时处理时都要用到。
(9)为了将自己挑选的字段显示在佤浏览表格(Grid)中,首先建立一个页框(Pageframe),将上面利用拖放操作形成的标签和文本框放在第一页“逐条浏览”上,而后为每个字段名建立一个复选框,放置在页框的“浏览字段设定”页上,有对钩表示选中,无对钩,表示不选。默认状态下是全选(全部打对钩),单击后去掉对钩,表示不选。为动态反映,
(10)建立一个全局变量数组存储。并在“全局浏览”页面被激活时给表格的每个column的header1.Caption和Controlsouce赋值为数组中的值即可。
源程序:set talk off
set delimiter off
set safety off &&重建索引时不提示覆盖
use a:\xj
inde on 学号 to a:\xh &&按学号建立索引文件xh.idx
inde on 学号+姓名 to a:\xhxm &&按学号和姓名建立索引文件xhxm.idx
inde on 姓名 to a:\xm &&按姓名建立索引文件xm.idx
stor.t.to c
do while c
clear
text
检索功能表
0---退出检索系统
1---按学号检索
2---按姓名检索
3---按学号和姓名检索
4---按学号、姓名、性别检索
endt
dd=1
@row()+1,17 say"请输入功能号" get dd pict "9" rang 0,4
read
do case
case dd=0
clear
do a:\main
retu
case dd=1
clear
n1=spac(2)
@4,20 say"请输入学号" get n1
read
use a:\xj inde a:\xh &&打开数据库文件xj.dbf并打开按学号建立的索引文件xh.idx
find &n1 &&查找字符串n1,&为宏替换
case dd=2
Clear
n2=spac(6)
@4,10 say"请输入姓名" get n2
read
use a:\xj inde a:\xm
find &n2
case dd=3
clear
n1=spac(2)
n2=spac(6)
@4,10 say"请输入学号" get n1
@5,10 say"请输入姓名" get n2
read
use a:\xj inde a:\xhxm
n4=n1+n2
find &n4
case dd=4
clear
n1=spac(2)
n2=spac(6)
n3=spac(2)
@3,10 say"请输入学号:" get n1
@4,10 say"姓名:" get n2
@5,10 say"性别:" get n3
read
loca for 学号=n1.and.姓名=n2.and.性别=n3 &&查找条件是学号为n1并且姓名为n2并且性别为n3的记录
other
retu
Endc
if eof() &&如果已到文件尾
clear
wait'对不起!没找到。请按任意键返回!'
loop
endif
cc=.t.
y=" "
do while cc
set devi to scre &&设置屏幕显示字符
clea
@row(),5 say"学号:"+学号
@row(),40 say"姓名:"+姓名
@row()+1,5 say"性别:"+性别
@row()+1,5 say"出生年月:"+dtoc(出生年月)
@row()+1,40 say"是否团员:"+是否团员
@row()+1,5 say"电话:"+电话
@row(),40 say"通信地址:"+通信地址
@row()+1,5 say"邮编:"+邮编
@row()+1,5 say"总成绩:"+总成绩
@row()+1,5 say"备注:"+备注
@20,20 say"不继续查找打N,否则打任一键" get y
read
if y="N".or.y="n"
clear
stor.f.to cc
loop
else
Endif
if.not.eof()
loop
Endif
stor.f.to cc
Enddo
stor.f.to c
Enddo
use
do a:\main
8、统计模块子程序(A:\total.prg)
功能:提供统计人数、平均成绩和党员人数的功能。
源程序:set talk off
use a:
dd=0
stor.t.to c
do while c
clear
text
统计功能表
0---退出统计系统
1---统计人数
2---统计平均成绩
3---统计党员人数
endt
dd=1
@row()+1,17 say"请输入功能号" get dd pict "9" rang 0,3
read
do case
case dd=0
clear
do a:
case dd=1
clear
coun to aa &&计算记录数并把值赋给aa
@4,20 say"总人数:"
??aa &&显示变量aa的值
a=' '
@7,20 say"按任意键返回!"
read
if a=''
loop
endi
case dd=2
clear
aver 总成绩 to aa &&对总成绩求平均并把平均值分别赋给变量aa
@4,20 say"求总成绩平均成绩为:"
??aa
a=' '
@9,20 say"按任意键返回!"
read
if a=''
loop
endi
case dd=3
clear
coun for 是否团员="是" to aa &&统计党员数,并把值赋给aa
@4,20 say"团员人数为:"
??aa
a=' '
@9,20 say"按任意键返回!"
read
if a=''
loop
endi
endc
return
endd
9、显示模块子程序(A:\dp.prg)
功能:提供卡片式的显示记录的功能。
源程序:set talk off
set devi to scre
use a:\xj
kk=.t.
do while kk
clea
hk=recn()
hk1=str(hk,3)
@row(),5 say"您现在浏览的是第"+hk1+"张卡片"
@row()+1,5 say"学号:"+学号
@row()+1,5 say"姓名:"+姓名
@row()+1,5 say"性别:"+性别
@row()+1,5 say"出生年月:"+dtoc(出生年月)
@row(),40 say"是否团员:"+是否团员
@row()+1,5 say"电话:"+电话
@row()+1,40 say"通信地址:"+通信地址
@row()+1,5 say"邮编:"+邮编
@row()+1,5 say"总成绩:"+总成绩
@row()+1,5 say"备注:"+备注
wait '请按n键浏览下一条记录,按l键浏览上一条记录,按y键返回主程序!' to k
do case
case k='n'
if eof() &&如果已到文件尾
go top &&将文件记录指针移到文件记录的首记录
else
skip &&否则,跳到下一条记录
endi
hk=hk+1
case k='l'
if bof() &&如果已到文件开头
go bott &&将文件记录指针移到文件记录的最后一条记录
else
skip -1 &&否则,跳到上一条记录
endi
hk=hk-1
case k='y'
clear
do a:\main
kk=.f.
endc
endd
10、删除模块子程序(A:\del.prg)
功能:提供逻辑与物理删除的功能。
源程序:set talk off
set devi to scre
use a:\xj
yy=" "
yn=" "
de=spac(2)
clear
@4,20 say"请输入要删除记录的学号" get de
read
loca all for 学号=de
if eof()
wait"查无此卡片!击回车键退出!"
clear
do a:\xj
retu
endi
if.not.eof()
clea
hk=recn()
hk1=str(hk,3)
@row(),5 say"您现在浏览的是第"+hk1+"张卡片"
@row()+1,5 say"学号:"+学号
@row()+1,5 say"姓名:"+姓名
@row()+1,5 say"性别:"+性别
@row()+1,5 say"出生年月:"+dtoc(出生年月)
@row(),40 say"是否团员:"+是否团员
@row()+1,5 say"电话:"+电话
@row(),40 say"通信地址:"+通信地址
@row()+1,5 say"邮编:"+邮编
@row()+1,5 say"总成绩:"+总成绩
@row()+1,5 say"备注:"+备注
endif
@13,20 say"是这个记录吗?(Y/N)" get yn
read
if yn="n".or.yn="N"
clea
do a:\xj
retu
endi
if yn="y"
@14,20 say"真要删除吗?(Y/N)" get yy
read
if yy="y".or.yy="Y"
delete &&删除当前记录(逻辑删除,但不是真正将当前文件记录从VFP中删除)
endif
endi
clear
stor " " to y
@15,15 say"删除的记录从盘中彻底清除吗?(Y/N)" get y
read
if y="y".or.y="Y"
pack &&真正删除当前记录(物理删除)
endif
clea
do a:\xj
邮件给