2010年3月16日
巴巴运动网的权限模块总结
因为巴巴运动网的权限设计模块对我来说,可以实现,打算好好整理一下思路,面试时说不定还能起点作用,看了两遍,自己总结下:
权限定义/权限组(角色)模块
1.对系统的所有功能定义相应的权限
2.由网管针对不同部门不同员工分配权限组(角色),在分配角色时,
可以选择该角色具有的权限
3.权限拦截模块
具体步骤:
1.设计权限实体
联合主键(SystemPrivilegePK),该类有module和privilage两个属性
2.初始化权限
3.设计权限组(角色)PrivilegeGroup实体,如果我们希望通过修改权限组添加或删除权限,那么权限和角色之间的多对多关系的维护端由角色来维护
private String groupid;
private String name;
private Set<SystemPrivilege> privileges = new HashSet<SystemPrivilege>();
同时加上到数据库表的映射
当然由于权限和角色之间的多对多关系是双向关联,所以要在SystemPrivilege实体bean中加上和PrivilegeGroup实体Bean之间的映射
4.实现权限组的添删改
路径:/control/
新建接口PrivilegeGroupService,继承Dao<PrivilegeGroupService>,
新建PrivilegeGroupService,继承DaoSupport
当然这些Bean都需要交给Spring管理,加上相应的注解即可
5.实现权限组的分页,这个简单
6.实现添加权限,分页列表上的Action,新建Action,名称为PrivilegeGroupManageAction,
①.第一个方法是AddUI,主要是显示页面
接下来就是增删该操作了(...)
7.权限做完后要新建一个Action用于完成SystemPrivilege,权限组,超级管理员的初始化,初始化完成后点击“确定”,进入登陆页面
权限模块的核心: 权限拦截模块
1.员工要访问control开头的路径,必须登陆,即粗粒度的权限拦截,这是通过Fileter实现的,这里我们定义为PrivilegeFilter,先从session范围中得到employee对象,如果对象为空,就浏览器重定向到登陆界面,,如果登陆了,就chain.doFilter(request,response);之后在web.xml配置,这样就实现粗粒度的权限控制了3
2.对于界面上的某些功能,不是每一个登陆到系统的员工对页面的功能都有操作权限,这就是细粒度的权限拦截
关键是怎样细粒度的权限控制
怎样实现细粒度的权限控制?
每点击页面上的按钮,都会提交给相应的Action处理,细粒度的权限拦截就是对Action拦截,在不修改原代码情况下,增加拦截代码,使用的是AOP技术
每一个功能Action都有相应的方法处理,我们用JDK5中的注解为Action中的方法注解相应的权限,因为我们有时会修改方法名称,用注解维护起来比较方便比如:
@Permission(module="order",privilege="modify")
public ActionForward OrderModify(){}
实现过程:
①.新建注解Permission,
@Retention(PetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Permission{
String module();
String privilege();
}
为Action中的方法,加上注解
只有拦截到Action中的这个方法,才能实现权限拦截,现在我们的Action已经交给Spring管理,所以我们可以使用Spring的AOP技术进行权限拦截,有必要复习一下AOP的知识:
在Action类上加一个注解如下:@Aspect,当然要使这个注解起作用,我们必须在Spring的配置文件里配置:
<aop:aspectj-autoproxy> <!--注解解析器-->
要实现切面编程,抽象过程体现在代码上, 就是一个采用类来描述,要是这个类起作用,我们必须要把它交给Spring管理,很郁闷
@Aspect@Component
public class Interceptor{
@Pointcut("exception(org.apache.struts.action.ActionForward cn.itcast.web.action.. * *(org.apache.struts.actionMapping,...))")
private void actionMethod(){}
@Around("actionMethod")
public Object interceptor(ProceedingJoinPoint pjp) throw Throwable{
System.out.println("拦截到了"+pjp.getSignature().getName()+"方法");
return pjp.proceed();
}
}
注意:
通知:
前置通知,在拦截的方法前加通知,这个通知执行后,被拦截的方法仍然会执行。
所以使用环绕通知,但是这样的拦截只能是拦截Action实现的,他不能拦截继承自DispatchAction
原因:Spring的动态代理技术的问题
Spring不能对通过反射调用的方法应用上通知(Advice)
当Spring发现我们的继承的DispatchAction的那个类符合他拦截的要求,就会生成一个代理对象,因为无接口,默认使用cglib为这个Action生成代理对象,根据cglib生成代理对象的特点,继承目标类,并且重写所有非final的方法来实现
注:spring只会为本类定义的方法应用通知
posted @
2010-04-01 07:10 d66380022|
编辑 收藏
今天初步学习了一下Struts2,对于Struts2,企业中已应用十分广泛,由于他是在Struts基础上发展而来,技术上比struts高,比如
struts2对同一个路径的每个请求分别使用一个独立Action实例对象,所有使用struts2不用考虑线程安全问题,还有就是修改下配置文件,不用重启服务器
1.struts2的建立过程:
配置过程:
1.新建web项目
2.导入struts2类库. [blank项目中含有最少的jar文件]
3.配置web.xml
<filter>[...SrutsPrepareAndExecuteFilter]
4.写Action类
public class HelloWorldAction {
public String execute(){
System.out.println("hello world!");
try {
ServletActionContext.getResponse().getWriter().printf("hello!");
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
}
5.创建struts.xml
①.struts2默认在类路径下查找struts.xml文件
关于struts.xml配置,可以参看struts2-blank-2.1.8.1.war解压后的配置
设置环境为开发模式.设置为开发模式,有利于编程和调试,
<constant name="struts.devMode" value="true" />
3.让struts.xml中的Package继承struts-default.xml中的默认包.
所有的默认包中的元素配置就引入进来.
4.配置默认值.
<action name="HelloWorldAction" class="cn.itcast.struts2.helloworld.HelloWorldAction">
<result name="success">
<param name="location">/index.jsp</param>
</result>
</action>
5.strtus2中的action不是单例的,它会为每个请求单独分配一个action实例,所以可以在action类中声明实体属性.
Struts2的Action是模型层,不是控制层,原因它是需要携带业务数据的.
6.struts2中的拦截器是负责每个独立的一项任务.例如,参数传递,国际化,异常处理,StrutsPrepareAndExecuteFilter是Struts 2框架的核心控制器,它负责拦截由<url-pattern>/*</url-pattern>指定的所有用户请求,当用户请求到达时,该Filter会过滤用户的请求。
7.在struts2的jsp中输出数据,借助于struts-tag.tld标签库.该库在[struts-core.jar/META-INF/struts-tag.tld,但是其重要作用还是用于回显数据
8.Struts2可以数据的范围:ActionContext request session application
ActionContext是数据中心,ValueStack不过是该数据中心中的一个特殊对象.
在jsp中访问ActionContext中的数据的话,可以使用OGNL语言.
9.struts2中包的名字空间搜索原则:
1.<package name="xxPkg">
2.<package name="xxPkg" namespace="">
3.<package name="xxPkg" namespace="/">
4.<package name="xxPkg" namespace="/HelloWorld">
1.和2.效果一样,都是默认的包.
2.都会自后向前搜索,直到找到为止,"/" 和 "/HelloWorld"唯一不同就是也可以作为搜索目录.
3.空包是默认包,如果每个包都没有找到相应的action的话,就去默认包下去找.
10.struts2中进行动态方法调用.
http://localhost:8085/struts2/Hell/HelloWorldAction!toRewView.do
11.struts2中使用标签库
<s:form action="HelloWorldAction_doReg" [namespace=""]>
<s:textfield name="name" label="UserName" />
<s:submit />
</s:form>
namespace:如果不写,会使用当期的名字空间.
struts给的所有控件标签,主要目的用于回显数据.
12.struts2中进行基本的校验,需要Validateable和ValidationAware两个接口的支持,同时还需要
workflow拦截器的支持.
13.在struts2中,默认的表达式语言是OGNL,Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts 2框架使用OGNL作为默认的表达式语言。相对EL表达式,它提供了平时我们需要的一些功能,如:支持对象方法调用,支持类静态方法调用和值访问,操作集合对象。例如():
访问值栈中的action的普通属性: username = <s:property value="username"/>
由于下午有公司来面试,耽误了一些时间,老徐说明天再多补一个小时,太敬业了啊,佩服!
posted @
2010-03-25 16:39 d66380022|
编辑 收藏
在线支付时针对易宝支付的网关而开设的,众所周知,易宝已是三大网上支付平台之一,正好明天易宝来招聘,正好练练,呵呵,下午是面试技巧
好好学习,毕业倒计时:15天
1.支付宝是不能立即拿到现钱的,这也是商家喜欢在线支付的一个原因吧,在线支付是直接和银行对接的,这样商家可以立即拿到现钱,避免了和买家产生矛盾后,钱被封的风险。
下面就来实现在线支付吧,首先提出请求,如果通过,会得到账号和密钥
我们要做的事有两项
1.向支付网关发起请求,就是向http发起请求,所谓请求就是我们平常使用Http请求(http://www.xxx.cn/xxx.do),请求方式:Get/Post
<form method=”” action=”https://www.yeepay.com/app-method”>
<input name=”?”,value=””/>
<input name=”url” value=”http://www.itcast.cn/re.do”/>
<input name=”hmac” value=””>
…..
Hmac
= MD5(pd_FrpId + p0_Cmd + p8_Url,key);32位,不可逆
支付网关:其实就是一个http路径
2.接收支付网关返回的支付结果信息
第一次使用浏览器重定向技术,
把支付信息发送给:http://www.itcast.cn/re.do?id=21432&result=1
2.相关知识点:
一.接入方式:两种接入方案
①.直接与银行对接
优点:因为直接与银行进行财务结算,交易资金结算比较安全
缺点:开发工作量比较大,而且银行会不定期升级交易系统,随着银行系统的升级,企业也需要作相应改动,所以维护工作量也是比较大的,而且企业每年还需要向银行交纳一定数量的接口使用费。
②.通过中间公司间接与银行对接
优点:开发工作量较少,因为使用的是中间企业提供的接入规范,所以银行升级系统,不需要企业作相应修改,除非中间企业的接入规范发生了改变,企业才作相应修改。
缺点:因为是与中间企业进行资金结算,目前所有中间企业都是私企,资金安全是个大问题。
3. 对支付数据进行Md5加密
4. 支付流程:
通过http请求方式向易宝支付网关发起一个支付请求,请求可以是get或post方式提交。
易宝支付网关对企业发来的数据使用用户的密钥生成MD5-hmac码,然后跟企业发来的MD5-hmac码(即上面表单由hmac字段提供的值)比较是否相同,如果相同即把请求转发到银行网关,当用户支付完成后,银行网关会引导用户的浏览器重定向到易宝支付网关,然后易宝支付网关再引导用户的浏览器重定向到企业提供的url(即上面表单由p8_Url提供的地址)
接下来我们只需处理结果了
下午是面试技巧:
薪水:一定要写 ,不要写区间 4000
心得体会:具体点
责任描述:多写,详细,写开发难点,描述清楚,介绍清楚
对某一模块详细描述,对难点的解决过程
描述自己负责的模块,抓住一点,详细说,千千不要泛泛而谈
不能写培训经验
三好学生,写一等奖学金是应届的
爱好还是要写的
身份证不能造假,教育经历,通常企业筛选简历过程:
从1-2千封中选,让前台做/人力去筛选简历,要求:
A.只提取两年工作年限以上(一般企业都是这样干的)
B.连人力都能判断出造假的简历,排除
C.经过人力,剩下100封
D.面试官
88年可以写两年,教育经历:把毕业信息提前,证书造假
简历上可适当造假
薪水:
写2年,月薪(北京,上海,广州):
初级:3000 – 4000
4500 -- 8-12
中级:4500 – 6000
7K 其中5K-6K比较多 --
高级:7000 –
1.2W (3-5)
从事Android,薪水如下:
Android 2年 最长就2年
原来做过j2se开发3-4年 8K-1.2W
android 3个月(j2ee 1-2)6K
Android 干半年 4.5K
如果我们老老实实干半年,编码能力就会没问题
工作经验:
1.为什么离职?
我在深圳那边4K,也不知道北京这边的行情。
2.当公司准备要你的时候,会让你填以前公司的地址信息,联系人信息
造假呗,找个外地的同学
3.入职后,离职证明,随便盖个章,离职证明模版网上有一大堆
新公司不想惹麻烦,已经和原来公司解除了劳动合同
4.关于android面试
基础:
多线程(并发控制),Socket通信,http通信
android:
android的什么项目
项目针对那些手机型号的手机HTC
如何适应各个分辨率的问题,大分辨率靠前
ContentProvider(难度),共享数据
联系人(如何获取联系人)
Activity的生命周期:运行
暂停 停止
前台生命周期
完整生命周期
意图:隐式,显式
组件:广播,内容提供者,Service
四大组件
MMI:多媒体接口
SQLite:数据库访问
项目开发
3人
2-3月
公司部门 人员
研发部/技术部 50-60人
测试部 8-10人
客服部/售后服务
市场部
行政部
posted @
2010-03-24 00:49 d66380022|
编辑 收藏
今天学习从网上获得数据,以xml文件输出。流程:android客户端 -----已经开发好的CRM(220.113.15.23)
-----[客户资料]--à屏幕
用的是struts1.3的环境
1.搭建环境
1.web.xml配置struts servlet
2.添加jar
3.复制struts-config.xml
环境搞定后,新建jsp文件,写好文件头:<?xml
version="1.0"
encoding="UTF-8"?> 注意
接着新建一个ContactAction
最后配置struts-config.xml文件
新建contact.jsp文件,得到数据
访问路径,测试得到的数据,以验证是否正确,若正确的话,第一步完成
下面是Android应用
2 .①.设计要显示的界面
②.新建service,可以调用
关键代码:
String
path = “http://192.168.1.100:8080/crm/contacts.do”;
URL
url = new
URL(path);
HttpURLConnection
conn
= (HttpURLConnection)url.openConnection();
conn.setConnectionTimeout(5*1000);
conn.setRequestMethod(“POST”);
return
conn.getInputStream();
在该类中还应该新建sax解析方法,解析文档,保存数据
③.新建android测试类,以日志形式打印出xml文件中的数据
3.应用HttpURLConnection对象,我们可以向网络发送请求参数.下面以POST发送为例,写一些关键代码点
Map<String, String> params =
new
HashMap<String, String>();
params.put("age", "22");
params.put("name", "浪淘沙");
StringBuilder
params = new StringBuilder();
for(Map.Entry<String, String>
entry : params.entrySet()){
params.append(entry.getKey());
params.append("=").append(URLEncoder.encode(entry.getValue(),
"UTF-8"));
params.append("&");
}
if (params.length() > 0)
params.deleteCharAt(params.length() - 1);
byte[] data =
params.toString().getBytes();
4. 为应用添加新的Activity
①:新建一个继承Activity的类
②:需要在功能清单AndroidManifest.xml文件中添加进上面Activity配置代码
打开新的Activity,不传递参数
通过意图打开Activity,Intent,用于激活Activity的,在组件之间传递数据
在新的Activity中接收前面Activity传递过来的参数:
添加参数的另一种方法:Bundle
Bundle类用作携带数据,它类似于Map,用于存放key-value名值对形式的值。相对于Map,它提供了各种常用类型的putXxx()/getXxx()方法, putXxx()用于往Bundle对象放入数据,getXxx()方法用于从Bundle对象里获取数据。Bundle的内部实际上是使用了HashMap<String,
Object>类型的变量来存放putXxx()方法放入的值:
5. Intent过滤器
Android鼓励减少组件间的耦合,因此Android提供了Intent (意图) ,Intent提供了一种通用的消息系统,它允许在你的应用程序与其它的应用程序间传递Intent来执行动作和产生事件。使用Intent可以激活Android应用的三个核心组件:活动、
服务和广播接收器。
Intent可以划分成显式意图和隐式意图。
posted @
2010-03-20 23:30 d66380022|
编辑 收藏
使用SharedPreferences进行数据存储,是专门用来向用户提供软件参数设置功能,
1.
SharedPreferences类,它是适合用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据。
2. SharedPreferences使
用xml文件保存
数据,getSharedPreferences(name,mode)方法的第一个参数用于指定该文件的名称,不用带后缀,后缀由Android自动加上,方法的第二个参数指
定文件的操作模式,共有四种操作模式。
3.设置软件参数过程:
①.当用户点击保存按钮时,就激发保存事件
在Activity中的匿名内部类中,得到用户输入值
需要注意的是:android.content.SharedPreferences.Editor用于保存参数,该editor最后要commit一下。
最后要Toast.makeText(PreferencesActivity.this,
"保存参数成功",
1).show();用于提示保存参数成功
②.如果访问其他应用中的Preference,前提条件是:该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。如:有个<package
name>为cn.itcast的应用使用下面语句创建了preference。
getSharedPreferences("itcast",
Context.MODE_WORLD_READABLE);
其他应用要访问上面应用的preference,首先需要创建上面应用的Context,然后通过Context 访问preference ,访问preference时会在应用所在包下的shared_prefs目录找到preference :
Context
otherAppsContext = createPackageContext("cn.itcast",
Context.CONTEXT_IGNORE_SECURITY);
4. 使
用SQLite数
据库存储数据
在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,
SQLite最大的特点是你可以保存任何类型的数据到任何字段中,无论这
列声明的数据类型是什么。SQLite可以解析大部分标准SQL语句。
5.开发3g数据库
软件需求:
①.用户初次使用软件,初始化数据库
②.用户升级软件,自动更新数据库
为此,类对应提供了两个重要的方
法,分别是onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase
db, int oldVersion, int newVersion),
6.利用继承自SQLiteOpenHelper的DatabaseHelper类实现增删改查,和我们以前的用JDBC实现的增删改查语法基本一样,举例:
SQLiteDatabase
db
= ....;
db.execSQL("insert
into person(name,
age) values(?,?)", new Object[]{"传智播客",
4});
db.close();
需要注意的是SQLiteDatabase的rawQuery()
用于
执行select语句
7.将数据动态显示在手机屏幕上,用SimpleAparter实现
今天到此结束!
posted @
2010-03-17 23:53 d66380022|
编辑 收藏
3G的第二天,单元测试,老黎讲的东西当天基本可以掌握,很喜欢
1.单元测试步骤:
第一步:首先在AndroidManifest.xml中加入下面红色代码:
①.在<application>中加入:
<uses-library android:name="android.test.runner" />
②.在最后的标签前加上:
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="cn.itcast.action"
android:label="Tests for My App" />
注意:上面targetPackage指定的包要和应用的package相同。
第二步:编写单元测试代码(选择要测试的方法,右键点击“Run As”--“Android Junit Test” ):
2.数据存储和访问
软件需要对处理后的数据存储,Android为数据存储提供了5种方式:
文件,SharedPreferences,SQLite数据库,内容提供者(Content provider),网络
使用文件对数据进行存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中。openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为:
Context.MODE_PRIVATE
Context.MODE_APPEND
Context.MODE_WORLD_READABLE
Context.MODE_WORLD_WRITEABLE
3.SAX解析XML
SAX是一个解析速度快并且占用内存少的xml解析器,解析采用的是事件驱动,这些方法定义在ContentHandler接口中,下面是一些ContentHandler接口常用的方法:
startDocument()
当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。
endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。
startElement(String
namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。
endElement(String
uri, String localName, String name)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。
characters(char[]
ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
4.使用Pull解析器读取XML文件
如果需要生成一个XML文件,生成XML文件的方法有很多,如:可以只使用一个StringBuilder组拼XML内容,然后把内容写入到文件中;
或者使用DOM API生成XML文件,或者也可以使用pull解析器生成XML文件,推荐使用Pull解析器。
最后,
巴巴运动网,好好学习权限和优化等技术,面试特别有帮助
posted @
2010-03-16 00:29 d66380022|
编辑 收藏
今天是黎老师的3G应用开发Android项目的第一天,Android相当火,从事Android开发的人员不太多,接下来开始Android之旅。
1.3G简介:
3G,即3rd Generation,目前中国联通使用的是WCDMA,中国电信使用的是CDMA2000
中国,移动使用的是具有自主知识产权的TD-SCDMA,3G的最大优点:速度上的提升
2.wml标签,比Html标签简单,1-2天就可以搞定,工作中用到再学,因为他是2.5G中用到的技术,如果3G普及的话,这门技术就Over了。
3.智能手机操作平台
我们学Android操作系统,RIM BlackBerry,黑莓我比较喜欢,但在国内还不怎么流行,
Symbian手机硬件比较好,所以占用很大市场,在3G时代,手机基本上可以当做电脑,用户更看重手机是否能满足日常生活和工作的需要。这就好像大多电脑
用户都安装了Windows操作系统,很少安装Linux系统。开发Android的人员的增多,必然会带来手机软件的增多,软件的多少会决定以后操作系统的市场份额,windows mobile比Android,iPhone的界面差远了。我们关注:Android,iPhone,Symbian智能手机平台,因为我们搞Java开发,只能学Android了
4.Android资料
买Android手机就买多普达和Moto的,质量好。Android最好的资料是android.pdf。Google Android SDK开发范例大全提供了Android的全部功能,在学完课程之后可以看
5. 开发环境的搭建:
JDK
5 或 JDK 6
(仅有JRE不够)
Eclipse
3.5 (galileo)
①.下载ADT 的Eclipse 插件
http://dl.google.com/android/ADT-0.9.5.zip
②.安装 Eclipse
插件 (ADT)
启动 Eclipse,选择 Help >
Install New Software,在出现的对话框里,点击Add按钮,在对话框的name一栏输入“ADT”, 然后点击Archive...,浏览和选择已经下载的ADT插件压缩文件。
点击 OK.。返回可用软件的视图,你会看到这个插件,然后选择Developer Tools (会选中下
面的“Android Developer Tools”和
“Android Editors“),点击 Next,最后重启 Eclipse。
③.下载Android
SDK:
http://dl.google.com/android/android-sdk_r04-windows.zip
下载完SDK后,把.zip文件解压到你电脑上合适位置。启动
Eclipse,选择window->preferences,
在打开的视图左边点击android,在右边的SDK Location中选择Android
SDK所在位置。
6.开发Android的第一个应用:
1.点击工具栏上手机形状的虚拟设备管理器(简称“AVD“)
2.在打开的虚拟设备管理器中创建一个虚拟手机
3.在项目上右键点击run as
?Android application
OK,第一个应用就OK了
7.端口号:127.0.0.1:5554
8.Activity
就像struts中的Action,处理用户请求,除此之外,Activity还代表一个用户界面
9. Android应用程序架构
src/ java原代码存放目录
gen/
自动生成目录
gen
目录中存放所有由Android开发工具自动生成的文件。目录中最重要的就是R.java文件。 这个文件
由Android开
发工具自动产生的。Android开发工具会自动根据你放入res目录的xml界面文件、图标与常量,同步更新修改R.java文件。
res/
资源(Resource)目录
在这个目录中我们可以存放应用使用到的各种资源,如xml界面文件,图片或数据。具体请看ppt下方备注栏。
AndroidManifest.xml
功能清单文件
这个文件列出了应用程序所提供的功
能,相当于struts的config文件,只有配置好后,才能调用此Activity。
default.properties
项目环境信息,一般是不需要修改此文件中
10.短信发送器
注意:因为应用要使用手机的短信服务,所以要在清单文件AndroidManifest.xml中添
加短信服务权限:
11.通知
关于这些东西,还是看看文档,拷过来运行一下,测试测试看看效果就差不多了。
posted @
2010-03-16 00:20 d66380022|
编辑 收藏