课程设计报告课程设计(论文)任务书 软件 学 院 软件+信息 专 业 1班 班 一、课程设计(论文)题目 学籍管理系统 二、课程设计(论文)工作自 10 年 12 月 20 日起至 10 年 12 月 24 日止三、课程设计(论文) 地点: 软件学院实训中心 四、课程设计(论文)内容要求:1.本课程设计的目的(1)使学生巩固和提高Java编程技术(2)培养学生掌握程序设计的基本思路和方法; (3)加强学生研发、调试程序的能力;(4)培养学生分析、解决问题的能力;(5)提高学生的科技论文写作能力2.课程设计的任务及要求1)任务: (1)录入学生基本信息的功能;(2)修改学生基本信息的功能(3)查询学生基本信息的功能(4)删除学生基本信息的功能2)创新要求: (5)显示所有学生基本信息的功能(6)在用户进行相应操作时应弹出提示对话框(7)采用数据库存储学生基本信息3)课程设计论文编写要求(1)课程设计任务及要求(2)设计思路--工作原理、功能规划(3)详细设计---数据分析、算法思路、类设计、功能实现(含程序流程图、主要代码及注释)、界面等。
4)运行调试与分析讨论---给出运行屏幕截图,分析运行结果,有何改进想法等5)设计体会与小结---设计遇到的问题及解决办法,通过设计学到了哪些新知识,巩固了哪些知识,有哪些提高6)参考文献(必须按标准格式列出,可参考教材后面的参考文献格式)(7) 报告按规定排版打印,要求装订平整,否则要求返工;(8) 课设报告的装订顺序如下:封面---任务书---中文摘要---目录----正文---附录(代码及相关图片)(9)严禁抄袭,如有发现,按不及格处理4)答辩与评分标准: (1)考勤:10分; (2)程序检查:30分; (3)课设答辩:20分(3)课程设计报告:40分;5)参考文献:(1)丁振凡《Java语言使用教程》 北京邮电大学出版社 (2)张 .《 》 某出版社(3)6)课程设计进度安排内容 天数 地点构思及收集资料 7 图书馆编码与调试 5 实验室撰写论文 3 图书馆、实验室学生签名: 10年 12月 20 日课程设计(论文)评审意见(1)考勤(10分):优( )、良( )、中( )、一般( )、差( ); (2)程序检查(30分):优( )、良( )、中( )、一般( )、差( ); (3)课设答辩(20分):优( )、良( )、中( )、一般( )、差( ); (4)课设报告(40分):优( )、良( )、中( )、一般( )、差( );评阅人: 职称: 讲师 10 年 01 月 25 日中文摘要本程序用于用户对少量学生信息进行简单的管理,本程序针对于对安全系数要求不高,炒作不是很复杂的小型客户开发,如本程序不符合您的要求,请选择其他大型程序。
本程序可以实现对学生信息的录入、查询、修改、删除等操作,同时支持查看所有学生信息,功能完善,界面简洁美观,布局合理,操作简便,简单易用,任何人可轻松操作同时,本程序对系统要求配置较低,运行速度快,而却对数据库管理要求较低本程序数据库采用office2007版本的access数据库(.accdb)进行数据存储,该数据库可实现关系较为简单的数据管理如有需要,请与XXX公司联系本程序版本为0.9公测版,使用完全免费,未经允许,禁止用于商业用途,欢迎用户使用,如在使用中出现问题,请及时将问题反馈给我们,一边为您提供质量更优秀的软件,我们再次深表感谢!目 录一、课设任务及要求 1二、需求分析 2三、设计思路 3四、详细设计 4五、运行调试与分析讨论 19六、设计体会与小结 22七、参考文献 23一、课设任务及要求题目 学籍管理系统设计内容设计基于access数据库的学籍管理系统学生基本信息应包括对学生的相关属性进行操作,如学号、姓名、性别、专业、年级、生日等信息,另外添加备注一栏存储学生的附加信息用户可对学生基本信息进行录入、查询、修改、删除、显示所有学生信息等基本操作学生基本信息包括多项属性,关系较为复杂,应采用数据库存储学生基本信息,这样各种信息之间的关系、区别以及联系可以更好的存储起来。
设计要求1录入学生基本信息的功能1.1 用户按照要求输入信息进行注册,成功之后要提示用户注册成功1.2 当用户注册是输入不当,如学号已存在,应提示用户学号已存在,并重新输入2查询学生基本信息的功能2.1 用户可通过学号查询数据库中学生的基本信息,并将该学号的学生基本信息显示在查询窗口里2.2 当用户输入有不当,如输入学号不存在,应提示用户输入的学号有误,并重新输入2.3 用户不能对查询窗口中显示的学生基本信息进行编辑3修改学生基本信息的功能3.1 用户可通过学号在数据库中搜索需要修改的学生的基本信息,并将该学号的学生基本信息显示在修改窗口里3.2 用户可通过各行的修改按钮来改变学生信息的状态,并对学生基本信息进行修改3.3 用户输入新的学生信息后可将修改后的学生信息上传至数据库中4删除学生基本信息的功能4.1 用户可通过学号在数据库中搜索需要删除的学生的基本信息,并将该学号的学生基本信息显示在删除窗口里4.2 用户可将需要删除的学生信息从数据库中删除4.3 当用户删除成功之后应提示用处该学号的同学已从数据库中删除5显示所有学生基本信息的功能5.1 可在窗口中显示所有数据库中的学生信息5.2 学生信息要按一定规则有序的显示。
二、需求分析系统目标:软件开发的意图便于用户对学生的管理,方便查看学生的情况如用户对学生基本信息进行录入、查询、修改、删除等使用范围:本系统仅针对用户对少量学生进行学籍管理功能要求:1录入学生基本信息的功能2 查询学生基本信息的功能3修改学生基本信息的功能4删除学生基本信息的功能5显示所有学生信息的功能信息采集与使用权限:使用时由用户录入学生信息,安装本系统的用户皆可使用此软件性能需求:1、 程序运行流畅程序占用内存小,可在大多数电脑上流畅运行2、 数据的完整性,准确性录入按格式录入,同时只能录入一名学生的基本信息输出按格式输出,输出所有学生信息是要采用表格形式3、 界面适当美观,优雅,通用性较强三、设计思路界面构建:1、为了便于各种操作,软件采用多窗口的模式用户可在不同窗口进行相应操作(录入、查询、修改、删除、显示所有学生省信息) 2、主窗口是用户进行各种操作的平台,具体操作在各个独立功能窗口中完成 3、主窗口的主要功能位于菜单中,菜单为进入各个功能窗口的唯一通道 4、各功能窗口(除显示所有学生信息窗口)均由两到三个模块构成第一个模块中主要包含学号以及相关操作的组件第二个模块由姓名、性别、专业、年级、生日等相关组件构成。
第三个模块(录入、修改、删除窗口中包含)由相关操作的功能按钮组件构成 5、当用户在进行个别录入、删除或错误输入操作时,会弹出相应对话框提示用户如当录入成功或删除成功,弹出成功以及相应的简明信息提示当,当录入、删除失败以及输入不当是,弹出错误以及简单的错误原因提示用户重新操作 6、界面布局尽量合理,各组件位置分布均匀美观数据存储 1、由于学生包含多项属性,需对学生属性进行录入、修改、删除等多项操作,操作过程较为复杂,故采用数据库本软件针对小型用户使用,故使用入门级的access数据库 2、由于存储少量信息,数据库中只包含一个表,表中有学号、姓名、性别、专业、年级、生日等关键字 3、数据库在用完之后即使关闭四、详细设计1、程序主体概述:import java.awt.*;import java.awt.event.*;import java.sql.*;程序采用AWT包的相关组件设计,故包含awt包;通过各种动作完成相关操作,故包含awt.event包;学生信息用数据库存储,故包含sql包;public class StudentManageSystem extends Frame implements WindowListener,ActionListener{。
}StudentManageSystem为主类,通过继承类Frame以及借口,分别实现主窗口创建功能,动作、窗口都做监听的功能2、窗口监听:windowClosingwei()为实现各窗口关闭的方法 public void windowClosing(WindowEvent e){ try{ Frame frm=(Frame)(e.getSource()); if(e.getSource()==this){ try{ stmt.close(); con.close(); } catch(Exception ex){ System.out.println(ex.getMessage()); } System.exit(0); } else{ frm.dispose(); } }catch(Exception ex){ Dialog dig=(Dialog)(e.getSource()); dig.dispose(); } }文件中出现的窗体有两种—Frame和Dialog。
关闭方法采用异常处理机制,非异常情况需要关闭的窗口是Frame,如果需要关闭的窗体是Dialog,则进入异常处理机制当关闭Frame时先判断是否为主窗体,主窗体则关闭所有窗体并退出窗体,同时与数据库断开,当需要关闭的窗体为功能窗体时,只关闭该功能窗体3、公共组件: Panel zt;//欢迎窗体中的重要容器 MenuBar mt;//主菜单 Frame zf;//功能窗体加载的平台 Panel stuinf;//功能窗体中学生信息家在的容器 Font font1; Label number,name,sex,profession,grade,birthday;//属性标签 TextField numbers,names,professions,grades,birthdays;//属性文本域 CheckboxGroup style=new CheckboxGroup();//性别 Checkbox f,m; String url="jdbc:odbc:学生管理系统";//数据库连接 String sql="SELECT *FROM 学生信息"; Connection con; Statement stmt; ResultSet rs; Dialog wrongd;//错误对话框 Dialog confirmd;//成功对话框 String num0="";4、构造函数—主窗体: public StudentManageSystem1(){ super("学籍管理系统"); this.setVisible(true); 。
mt=new MenuBar(); Menu menu=new Menu("菜单"); MenuItem ininformation=new MenuItem("录入学生信息"); menu.add(ininformation); ininformation.addActionListener(this); mt.add(menu); setMenuBar(mt); zt=new Panel(); zt.add(new Label("\n"+"欢迎登陆学生管理系统")); this.add(zt); numbers=new TextField(20); number=new Label("学好"); m=new Checkbox("男",true,style); f=new Checkbox("女",false,style); try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(java.lang.ClassNotFoundException e){ System.err.println(e.getMessage()); } try{ con=DriverManager.getConnection(url, "", null); stmt=con.createStatement(); rs=stmt.executeQuery(sql); System.out.println("连接成功"); } catch(SQLException ex){ System.out.println("Message"+ex.getMessage()); } }通过构造函数,加载主窗体(包括家在菜单以及欢迎界面),对主窗体设置大小,添加动作监听器以及窗口监听器,初始化部分程序用到的公共组件(学生学号、姓名、性别、专业、年级、生日等相关组件),以及连接数据库。
5、学生信息加载方法: public Panel lodeinf(Panel stuinf){ }该方法用于加载功能窗体中学生信息显示组件的加载,包括学号、姓名、性别、专业、年级、生日的标签以及文本域组件6、录入学生信息窗体 public void registzf(String s){ stuinf=new Panel(); zf=new Frame(s); zf.doLayout(); zf.setLayout(new BorderLayout()); Panel ps=new Panel(); Label l=new Label("学号"); ps.setLayout(null); l.setBounds(50, 5, 30, 20); numbers.setBounds(100, 5, 220, 20); Panel px=new Panel(); px.setLayout(null); px.setBounds(0, 0, 0, 40); Button sureb,cancelb; sureb=new Button("录入"); cancelb=new Button("取消"); sureb.setBounds(150, 5, 40, 30); cancelb.setBounds(200, 5, 40, 30); px.setBackground(Color.LIGHT_GRAY); px.add(sureb); px.add(cancelb); sureb.addActionListener(this); cancelb.addActionListener(this); zf.setVisible(true); 。
zf.addWindowListener(this); }录入窗体包含三个模块通过容器ps加载学号的相关组件,成为第一模块注册窗体调用lodeinf(Panel stuinf)加载学生信息主体组件进行加载,成为第二模块通过容器px加载录入,取消按钮,来实现成绩录入以及推出该窗体,成为第三模块7、查询学生信息窗体 public void inquirezf(String s){ stuinf=new Panel(); zf=new Frame(s); Panel ps=new Panel(); Label l=new Label("请输入要查询的学号:"); Button b=new Button("查询"); b.addActionListener(this); ps.setLayout(null); ps.setBounds(0, 0, 100, 30); ps.setBackground(Color.LIGHT_GRAY); ps.add(l); l.setBounds(50, 5, 120, 20); zf.add("North",ps); zf.add("Center",lodeinf(stuinf)); zf.addWindowListener(this); names.setEnabled(false); 。
}查询窗体包含两个模块通过容器ps加载学号的相关组件,以及查询按钮,成为第一模块注册窗体调用lodeinf(Panel stuinf)加载学生信息主体组件进行加载,成为第二模块注意:该窗体中,用户不能对除学号以外的信息进行操作8、修改学生信息窗体public void modifyzf(String s){ stuinf=new Panel(); zf=new Frame(s); Panel p=new Panel(); Panel ps=new Panel(); Label l=new Label("请输入要修改的学号:"); Button b=new Button("搜索"); ps.setLayout(null); ps.setBounds(0, 0, 400, 60); ps.setBackground(Color.LIGHT_GRAY); ps.add(l); l.setBounds(25, 35, 120, 20); b.addActionListener(this); Panel px=new Panel(); px.setLayout(null); px.setBounds(0, 250, 400, 50); Button inquire,reinquire; inquire=new Button("录入修改"); reinquire=new Button("重置"); inquire.setBounds(130, 5, 60, 25); reinquire.setBounds(210, 5, 40, 25); px.add(inquire); px.add(reinquire); px.setBackground(Color.LIGHT_GRAY); inquire.addActionListener(this); reinquire.addActionListener(this); Label wl1,wl2,wl3,wl4,wl5; wl1=new Label("(新)"); 。
wb1=new Button("修改"); wl1.setBackground(Color.LIGHT_GRAY); zf.add(wl1); zf.add(wb1); wl1.setBounds(25, 76, 25, 20); wb1.setBounds(330, 75, 40, 20); wb1.addActionListener(this); wb1.setEnabled(false); p=lodeinf(stuinf); p.setBounds(0,60,400,190); zf.addWindowListener(this); zf.add(ps); names.setEnabled(false); }修改窗体包含三个模块+部分辅助组件通过容器ps加载学号的相关组件,以及搜索按钮,成为第一模块注册窗体调用lodeinf(Panel stuinf)加载学生信息主体组件进行加载,成为第二模块通过容器px加载录入修改,重置按钮,来实现成绩录入以及推出该窗体,成为第三模块还有部分显示“新”的标签,以及修改的按钮。
注意:没有显示学号之前其他组件均不能操作9、删除学生信息窗体 public void deletezf(String s){ stuinf=new Panel(); zf=new Frame(s); Panel ps=new Panel(); Label l=new Label("请输入要删除的学号:"); Button b=new Button("搜索"); ps.setLayout(null); ps.setBounds(0, 0, 100, 30); ps.setBackground(Color.LIGHT_GRAY); ps.add(l); l.setBounds(50, 5, 120, 20); b.addActionListener(this); Panel px=new Panel(); px.setLayout(null); px.setBounds(0, 0, 0, 40); Button sureb,cancelb; sureb=new Button("删除"); cancelb=new Button("取消"); sureb.setBounds(150, 5, 40, 30); cancelb.setBounds(200, 5, 40, 30); px.setBackground(Color.LIGHT_GRAY); 。
sureb.addActionListener(this); cancelb.addActionListener(this); zf.add("North",ps); zf.addWindowListener(this); names.setEnabled(false); }删除窗体包含三个模块通过容器ps加载学号的相关组件,以及搜索按钮,成为第一模块注册窗体调用lodeinf(Panel stuinf)加载学生信息主体组件进行加载,成为第二模块通过容器px加载删除,取消按钮,来实现成绩录入以及推出该窗体,成为第三模块注意:没有显示学号之前其他组件均不能操作10、显示所有学生信息窗体 public void allzf(String s){ String informations="学号 \t姓名 \t性别 \t专业 \t年级 \t生日 \r\n"; TextArea text; zf=new Frame(s); Font font=new Font("宋体",12,14); try{ sql="SELECT *FROM 学生信息"; rs=stmt.executeQuery(sql); while(rs.next()){ String s0=rs.getString("学号"); 。
informations+=s0+"\t"+s1+"\t"+s2+"\t"+s3+"\t"+s4+"\t"+s5+"\r\n"; } }catch(Exception ex){} text=new TextArea(informations); text.setBounds(10,30,380,260); text.setFont(font); text.setBackground(Color.LIGHT_GRAY); zf.add(text); zf.addWindowListener(this); }该窗体由一个TextArea构成,用于显示全部学生的年基本信息注意:显示要按一定规则输出(表格形式)11、动作监听 public void actionPerformed(ActionEvent e){ if(e.getActionCommand()=="录入学生信息"){ { if(zf!=null){ zf.dispose(); } registzf("学生信息录入"); } } if(e.getActionCommand()=="查询学生信息"){。
} if(e.getActionCommand()=="修改学生信息"){} if(e.getActionCommand()=="删除学生信息"){} if(e.getActionCommand()=="所有学生信息"){} if(e.getActionCommand()=="录入"){...} if(e.getActionCommand()=="查询"){...} if(e.getActionCommand()=="搜索"){...} if(e.getActionCommand()=="修改"){ numbers.setText(num0); if(e.getSource()==wb1) names.setEnabled(true); } if(e.getActionCommand()=="录入修改"){} if(e.getActionCommand()=="重置"){ try{ sql="SELECT *FROM 学生信息"; rs=stmt.executeQuery(sql); while(rs.next()){ String s0=rs.getString("学号"); 。
if(num0.equals(s0)){ names.setText(s1); if(s2.equals("男")) m.setState(true); } } }catch(Exception ex){ System.out.println(ex.getMessage()); } } if(e.getActionCommand()=="删除"){ ... } if(e.getActionCommand()=="取消"){ zf.dispose(); } if(e.getActionCommand()=="退出"){ System.exit(0); } if(e.getActionCommand()=="确定"){ try{ confirmd.dispose(); }catch(Exception ex){} try{ wrongd.dispose(); }catch(Exception ex){} } } 11、1 进入录入、查询、修改、删除、显示所有信息窗口 if(e.getActionCommand()=="录入学生信息"){。
} if(e.getActionCommand()=="查询学生信息"){} if(e.getActionCommand()=="修改学生信息"){} if(e.getActionCommand()=="删除学生信息"){} if(e.getActionCommand()=="所有学生信息"){} 11、2 录入 if(e.getActionCommand()=="录入"){ String num0=""; String s0=numbers.getText(); if(m.getState()) s2="男"; if(f.getState()) s2="女"; int count=0; try{ sql="SELECT *FROM 学生信息"; rs=stmt.executeQuery(sql); while(rs.next()){ num0=rs.getString("学号"); if(num0.equals(s0)){ count++; } } if(count==0){ sql="insert into 学生信息(学号,姓名,性别,专业,年级,生日,备注) values('"+s0+"','"+s1+"','"+s2+"','"+s3+"','"+s4+"','"+s5+"','"+""+"')"; stmt.executeUpdate(sql); confirmd=new Dialog(zf,"恭喜你!"); confirmd.setVisible(true); confirmd.setModal(true); confirmd.setSize(300, 100); confirmd.setBounds(20, 100, 220, 100); confirmd.addWindowListener(this); Label confirml=new Label("注册成功!\n学号:"+s0+"\n姓名:"+s1+"\n性别:"+s2+"\n专业:"+s3+"\n年级:"+s4+"\n生日:"+s5+""); Button confirmb=new Button("确定"); confirmb.addActionListener(this); confirmd.setLayout(new FlowLayout()); confirmd.add("North",confirml); confirmd.add("Center",confirmb); confirmd.pack(); } else{ wrongd=new Dialog(zf,"错误"); 。
} }catch(Exception ex){ System.out.println(ex.getMessage()); } }录入之前先检查该学号是否存在,如果存在,弹出错误对话框,提示重新输入,如果录入成功,弹出成功窗口 11、3 查询/搜索 if(e.getActionCommand()=="查询"){ num0=numbers.getText(); int count=0; try{ sql="SELECT *FROM 学生信息"; rs=stmt.executeQuery(sql); while(rs.next()){ String s0=rs.getString("学号"); if(num0.equals(s0)){ names.setText(s1); if(s2.equals("男")) m.setState(true); count++; } } if(count==0){ wrongd=new Dialog(zf,"错误"); 。
Label wrongl=new Label("您输入的学号"+num0+"不存在,请重新输入!"); numbers.setText(""); } }catch(Exception ex){ System.out.println(ex.getMessage()); } }查询该学号是否存在,如果不存在,弹出错误对话框,提示重新输入,如果存在,在查询窗体中显示该学生信息 11、4 修改 if(e.getActionCommand()=="录入修改"){ numbers.setText(num0); String s0=num0; String s1=names.getText(); if(m.getState()) s2="男"; System.out.println(s0+s1+s2+s3+s4+s5); try{ sql="update 学生信息 set 姓名 = '"+s1+"' where 学号 ="+"'"+s0+"'"; stmt.executeUpdate(sql); sql="update 学生信息 set 性别 = '"+s2+"' where 学号 ="+"'"+s0+"'"; 。
confirmd=new Dialog(zf,"恭喜你!"); names.setText(""); } catch(Exception ex){ System.out.println(ex.getMessage()); } }录入修改后该学生的信息,如果录入成功,弹出成功窗口 11、5删除 if(e.getActionCommand()=="删除"){ numbers.setText(num0); try{ sql="SELECT *FROM 学生信息"; rs=stmt.executeQuery(sql); while(rs.next()){ String s0=rs.getString("学号"); if(num0.equals(s0)){ sql="delete * from 学生信息 where 学号 = '"+num0+"'"; stmt.executeUpdate(sql); confirmd=new Dialog(zf,"成功了!"); 。
} } }catch(Exception ex){ System.out.println(ex.getMessage()); } } if(e.getActionCommand()=="取消"){ zf.dispose(); } if(e.getActionCommand()=="退出"){ System.exit(0); } if(e.getActionCommand()=="确定"){ try{ confirmd.dispose(); }catch(Exception ex){} try{ wrongd.dispose(); }catch(Exception ex){} } }删除选定学生的信息,如果删除成功,弹出成功删除窗口五、运行调试与分析讨论第 21 页 六、设计体会与小结 做课设让我对java有了新的认识,首先我接触了很多上课见过见过但没有实际用过的类和方法,让我对编程有了许多新的思想。
大一学了C与C++,虽然当时学的还不错,由于学的都是入门知识,所以自己做的都是DOS下执行的程序,虽然可以实现一定的功能,但和自己平时用的软件相差很大这学期刚开课学java感觉没什么新鲜感,它的跨平台特性也只是听听,根本没有认识,知道学到第七章图形界面用户编程的时候,我发现学会之后自己可以做出来的软件就更接近平时使用的软件了,不过由于平时学士不刻苦,到时没有时间静下心来写java代码,知道结课做课设,才静下心来开始研究java,结合以前的编程知识,做起了自己的程序 之所以会选学籍管理系统,是因为上学期C++做的也是这个,做起来会比较熟练,同时想通过做课设学习一点数据库的知识,感觉这些知识更贴近实际应用,选好之后开始准备课设,先从最简单的主窗体,到主窗体的菜单,组件添加,再到各个功能的独立窗体的建立,再到最后的数据库连接,功能的实现,一步步走来,收获颇多其中独立窗体中的组件排版是碰到的第一个问题,最简单的方法是在建每个窗体时把所有需要的组件全部重新添加进去,但是这样代码会很长,而却会多占很多内存空间,于是我决定把个功能窗体中共同的组件在一个新方法中统一加载,为了减小占内存空间,组建的在主类的构造方法中生成,所有独立功能窗口公用组件,这样既减少了代码额长度,又提高了程序的内存利用率。
还有很多问题,都在权衡之后得到了较优的解决方法它能够使这次课设还让我对java的跨平台性有了进一步认识,当我学好代码兴致勃勃的想把它做成exe可执行文件时,在网上搜索之后发现它似乎很难实现,就是应为它的java语言的实现机制,通过jre来执行执行java代码,而不是调用系统本地代码除此之外我还学到了新的编程技巧,比如对try,catch的运用刚学的时候就是照书本上的打,很不情愿但是必须打的代码,在做课设时,我发现他又很神奇的功能可以帮助我解决用其他办法不好解决的问题,比如关闭窗口刚开始程序只有主窗口时,关闭很好实现,但随着程序功能的完善,需要关闭的窗口越来越多,需要关闭的类型也便多了,到了关闭Dialog的时候,发现Dialog不能强制转化为Frame,如果想用前面的方法管还得把多处代码进行修改,不过我想到了用try来解决这个问题 再次感谢老师一学期对我们的教学!七、参考文献[1] 丁振凡 《Java语言使用教程》 北京邮电大学出版社[2] 百度 [3] JavaEye 第 23 页 。