北京著名网站设计公司,为企业做网站,基于html5的旅游网站的设计与实现,做特产的网站的分析一、新增的模块#xff1a;
在原项目基础上#xff0c;新增加了以下功能#xff1a; 1、增加AspectJ 框架的AOP 异常记录和事务管理模块。 2、增加SpringMVC的拦截器#xff0c;实现登录 控制页面访问权限。 3、增加 Logback日志框架#xff0c;记录日志。 4、增加动态验… 一、新增的模块
在原项目基础上新增加了以下功能 1、增加AspectJ 框架的AOP 异常记录和事务管理模块。 2、增加SpringMVC的拦截器实现登录 控制页面访问权限。 3、增加 Logback日志框架记录日志。 4、增加动态验证码CAPTCHA并验证功能。 5、增加所有页面的表单验证功能。 二、完善项目结构
前端项目结构无需改变。
更新后端结构
aop包aop包下是ExceptionLogAspect.java切面类实现了 异常记录和记录日志、事务管理等功能
interceptor包包含RoleInterceptor.java拦截器实现了根据登录身份进行权限检查从而实现控制访问不同登录角色的界面 三、新增模块实现
1、AspectJ 框架AOP 事务管理 和 记录日志
1pom.xml中添加依赖坐标AspectJ和Logback !-- AspectJ --
dependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactIdversion1.9.19/version
/dependency!-- 日志框架SLF4J 和 Logback --
dependencygroupIdorg.slf4j/groupIdartifactIdslf4j-api/artifactIdversion1.7.36/version
/dependency
dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.2.11/version
/dependency 2日志配置文件logback.xml ?xml version1.0 encodingUTF-8?
!--CONSOLE将日志输出到控制台。--!--FILE将日志输出到文件 logs/application.log并按天滚动生成新的日志文件。--!--root设置根日志级别为 INFO并绑定到 CONSOLE 和 FILE Appender。--!--logger为 ExceptionLogAspect 类单独设置日志级别为 ERROR仅记录错误日志。--
configuration!-- Console Appender --appender nameCONSOLE classch.qos.logback.core.ConsoleAppenderencoderpattern%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n/pattern/encoder/appender!-- 文件Appender --appender nameFILE classch.qos.logback.core.rolling.RollingFileAppenderfilelogs/application.log/filerollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy!-- 每天生成一个新的日志文件 --fileNamePatternlogs/application.%d{yyyy-MM-dd}.log/fileNamePattern!-- 保留30天的历史日志 --maxHistory30/maxHistory/rollingPolicyencoderpattern%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n/pattern/encoder/appender!-- 日志级别和Appender绑定 --root levelINFOappender-ref refCONSOLE /appender-ref refFILE //root!-- 为AOP切面单独设置日志级别--logger namecom.ssm.netctoss.aop.ExceptionLogAspect levelERROR additivityfalseappender-ref refFILE //logger/configuration3在spring-ioc.xml配置文件中新增配置SpringMVC拦截器 http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd !-- 启用AOP --
aop:aspectj-autoproxy/!-- 启用事务管理 --
tx:annotation-driven transaction-managertransactionManager/!-- 配置事务管理器 --
bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManagerproperty namedataSource refdataSource/
/bean 完整的spring-ioc配置代码如下 ?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:aophttp://www.springframework.org/schema/aopxmlns:txhttp://www.springframework.org/schema/txxmlns:contexthttp://www.springframework.org/schema/contextxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsd!-- 开启注解扫描比如扫描Dao, Service, AOP切面 --context:component-scan base-packagecom.ssm.netctoss/!-- 配置加载jdbc.properties文件 --context:property-placeholder locationclasspath:jdbc.properties/!-- 配置druid的连接池 --bean iddataSource classcom.alibaba.druid.pool.DruidDataSourceproperty namedriverClassName value${jdbc.driver}/propertyproperty nameurl value${jdbc.url}/property nameusername value${jdbc.username}/property namepassword value${jdbc.password}//bean!-- 配置SqlSessionFactory的bean --bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean!-- 绑定数据源 --property namedataSource refdataSource/property!-- 加载mybatis的核心配置文件 --property nameconfigLocation valueclasspath:mybatis-config.xml/property!-- 配置加载各个pojo对应的XXXXMapper.xml --property namemapperLocations valueclasspath:com/ssm/netctoss/mapper/*.xml/property nametypeAliasesPackage valuecom.ssm.netctoss.pojo//bean!-- 配置可以扫描mapper/dao接口的类型 --bean classorg.mybatis.spring.mapper.MapperScannerConfigurerproperty namesqlSessionFactoryBeanName valuesqlSessionFactory/propertyproperty namebasePackage valuecom.ssm.netctoss.mapper/property/bean!-- 启用AOP --aop:aspectj-autoproxy/!-- 启用事务管理 --tx:annotation-driven transaction-managertransactionManager/!-- 配置事务管理器 --bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManagerproperty namedataSource refdataSource//bean/beans 4举例用service层进行切面
建立aop包包中建立 ExceptionLogAspect.java切面类 /*** 异常记录切面* Aspect标记该类为切面。** Component将切面类注册为 Spring Bean。** AfterThrowing在目标方法抛出异常后执行通知。** pointcut匹配 com.ssm.netctoss.service 包及其子包中的所有方法。** logAfterThrowing记录异常信息包括方法名和异常详情。*/
Aspect
Component
public class ExceptionLogAspect {private static final Logger logger LoggerFactory.getLogger(ExceptionLogAspect.class);/*** 定义切点匹配 service 包及其子包中的所有方法*/AfterThrowing(pointcut execution(* com.ssm.netctoss.service..*(..)), throwing ex)public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {// 获取方法签名String methodName joinPoint.getSignature().toShortString();// 获取异常信息String errorMessage ex.getMessage();// 记录日志logger.error(Exception in {}: {}, methodName, errorMessage, ex);}
} 解释 使用Spring AOP实现的异常日志记录切面。它定义了一个切点匹配com.ssm.netctoss.service包及其子包中的所有方法。这些方法抛出异常时切面会记录异常日志。 切点定义使用了AfterThrowing注解它表示在方法抛出异常后执行。pointcut属性指定了切点用于匹配需要记录异常日志的方法。throwing属性指定了接收异常的参数名这里命名为ex。 logAfterThrowing方法是切面的通知方法它接收两个参数JoinPoint对象和异常对象。JoinPoint对象包含了方法签名等信息可以通过调用它的getSignature()方法获取方法签名。异常对象可以通过ex参数获取。 在logAfterThrowing方法中首先获取方法签名然后获取异常信息并将其记录到日志中。这里使用了LoggerFactory获取一个Logger对象用于记录日志。logger.error方法用于记录错误日志它接收三个参数日志前缀、日志信息和异常对象。 注意切面类需要使用Aspect和Component注解进行标注以便Spring容器能够扫描到并将其注册为Bean。 2、SpringMVC拦截器实现登录控制页面访问权限 。
1spring-mvc.xml添加 springmvc拦截器配置 !-- 配置拦截器 --
mvc:interceptorsmvc:interceptor!-- 指定需要拦截的URL路径 --mvc:mapping path/role/**/mvc:mapping path/admin/**/!-- 定义拦截器的Bean --bean classcom.ssm.netctoss.interceptor.RoleInterceptor//mvc:interceptor
/mvc:interceptors 完整的spring-mvc.xml文件如下 ?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:mvchttp://www.springframework.org/schema/mvcxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd!--开启SpringMVC的注解: 简化处理器映射器和处理器适配器的bean注册,以及JSON数据的传递--mvc:annotation-driven/!--静态资源的访问通过配置--mvc:default-servlet-handler/!--视图解析器的配置--bean classorg.springframework.web.servlet.view.InternalResourceViewResolverproperty nameprefix value/WEB-INF/pages//property namesuffix value.jsp //bean!-- 配置拦截器 --mvc:interceptorsmvc:interceptor!-- 指定需要拦截的URL路径 --mvc:mapping path/role/**/mvc:mapping path/admin/**/!-- 定义拦截器的Bean --bean classcom.ssm.netctoss.interceptor.RoleInterceptor//mvc:interceptor/mvc:interceptors/beans 2前端nav.jsp设置分权限页面 div idnaviul idmenulia href${pageContext.request.contextPath}/index classindex_off/a/li!-- 仅管理员可见的导航项 --c:if test${sessionScope.loggedInUser.adminCode admin}lia href${pageContext.request.contextPath}/role/list classrole_off/a/lilia href${pageContext.request.contextPath}/admin/list classadmin_off/a/li/c:if!-- 其他所有用户可见的导航项 --lia href${pageContext.request.contextPath}/fee/list classfee_off/a/lilia href${pageContext.request.contextPath}/account/list classaccount_off/a/lilia href${pageContext.request.contextPath}/service/searchService classservice_off/a/lilia href${pageContext.request.contextPath}/bill/list classbill_off/a/lilia href${pageContext.request.contextPath}/report/list classreport_off/a/li!-- 信息和密码修改仅管理员可见 --c:if test${sessionScope.loggedInUser.adminCode admin}lia href${pageContext.request.contextPath}/user/info?adminId${sessionScope.loggedInUser.adminId} classinformation_off/a/li/c:iflia href${pageContext.request.contextPath}/user/modipwd classpassword_off/a/li/ul
/div3创建拦截器实现角色权限访问
建立interceptor包包下创建RoleInterceptor.java拦截器实现了根据登录身份进行权限检查从而实现控制访问不同登录角色的界面。仅举例权限验证代码 package com.ssm.netctoss.interceptor;import com.ssm.netctoss.pojo.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class RoleInterceptor implements HandlerInterceptor {/*** 在请求处理之前进行调用Controller 方法调用之前* return true 表示继续流程如调用下一个拦截器或处理器* false 表示中断流程如重定向*/Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取请求的URLString uri request.getRequestURI();String contextPath request.getContextPath();// 定义需要管理员权限的URL路径if (uri.startsWith(contextPath /role/) || uri.startsWith(contextPath /admin/)) {// 获取用户信息User user (User) request.getSession().getAttribute(loggedInUser);if (user null) {// 用户未登录重定向到 nopower.jspresponse.sendRedirect(contextPath /nopower);return false;}if (!admin.equals(user.getAdminCode())) {// 无管理员权限重定向到 nopower.jspresponse.sendRedirect(contextPath /nopower);return false;}}// 其他请求或有权限继续处理请求return true;}/*** 在 Controller 方法调用之后视图渲染之前调用*/Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {// 添加日志记录}/*** 在整个请求结束之后调用即视图已经渲染完成*/Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {// 资源清理}
}解释 preHandle方法是HandlerInterceptor接口中的一个方法在请求处理之前进行调用Controller 方法调用之前。该方法的返回值决定了请求是否继续执行。如果返回true则表示继续流程如调用下一个拦截器或处理器如果返回false则表示中断流程如重定向。 首先通过request.getRequestURI()和request.getContextPath()获取请求的URL和项目路径。然后定义需要管理员权限的URL路径如以/role/或/admin/开头的路径。 通过request.getSession().getAttribute(loggedInUser)获取用户信息。如果用户未登录则重定向到“nopower.jsp”页面。如果用户登录了但不是管理员则同样重定向到“nopower.jsp”页面。 如果用户具有管理员权限或者请求的URL不需要管理员权限则继续处理请求。 4创建权限访问类CommonController确保 nopower.jsp 的映射 package com.ssm.netctoss.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;Controller
public class CommonController {RequestMapping(/nopower)public String noPower() {return nopower; // 确保与 nopower.jsp 的视图名匹配}
}5 LoginController中储存登录信息 sesson // 登录成功获取用户信息 User user userService.getUserByAdminCode(adminCode); // 将用户信息存储在会话中 session.setAttribute(loggedInUser, user); 6RoleController.java添加权限判断 package com.ssm.netctoss.controller; import com.ssm.netctoss.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpSession; Controller RequestMapping(/role) public class RoleController { RequestMapping(/list) public ModelAndView listRoles(HttpSession session) { User user (User) session.getAttribute(loggedInUser); ModelAndView mav new ModelAndView(); if (user null || !admin.equals(user.getAdminCode())) { mav.setViewName(redirect:/nopower); return mav; } // 添加获取角色列表的逻辑 // ListRole roles roleService.getAllRoles(); // mav.addObject(roles, roles); mav.setViewName(roleList); // 确保存在 roleList.jsp return mav; } } 7AdminController.java添加权限判断 package com.ssm.netctoss.controller; import com.ssm.netctoss.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpSession; Controller RequestMapping(/admin) public class AdminController { RequestMapping(/list) public ModelAndView listAdmins(HttpSession session) { User user (User) session.getAttribute(loggedInUser); ModelAndView mav new ModelAndView(); if (user null || !admin.equals(user.getAdminCode())) { mav.setViewName(redirect:/nopower); return mav; } // 添加获取管理员列表的逻辑 // ListAdmin admins adminService.getAllAdmins(); // mav.addObject(admins, admins); mav.setViewName(adminList); // 确保存在 adminList.jsp return mav; } } 3、增加动态验证码CAPTCHA并验证功能。
1修改 LoginController 控制器添加验证码逻辑
添加验证码生成方法并验证。完整LoginController.java代码如下 package com.ssm.netctoss.controller;import com.ssm.netctoss.pojo.User;
import com.ssm.netctoss.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;// 导入必要的类
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.Random;Controller
public class LoginController {Autowiredprivate UserService userService;// 映射到/toLogin的GET请求显示登录页面并生成验证码RequestMapping(/toLogin)public String toLogin() {return login; // 确保返回的视图名与您的视图解析器配置匹配}// 映射到/captcha的GET请求生成并返回验证码图片RequestMapping(/captcha)public void getCaptcha(HttpSession session, HttpServletResponse response) throws Exception {// 创建验证码图片int width 80;int height 30;BufferedImage image new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g image.getGraphics();Random random new Random();// 填充背景色g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));g.fillRect(0, 0, width, height);// 生成验证码字符串String captcha getRandomNumber(4);// 绘制验证码字符串g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));g.setFont(new Font(Times New Roman, Font.BOLD, 24));g.drawString(captcha, 15, 22);// 保存验证码到 session 中session.setAttribute(captcha, captcha);// 生成干扰线for (int i 0; i 4; i) {g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));g.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height));}g.dispose();// 设置响应的类型为图片response.setContentType(image/jpeg);OutputStream os response.getOutputStream();ImageIO.write(image, jpeg, os);os.close();}// 获取随机验证码private String getRandomNumber(int num) {char[] chars {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,0, 1, 2, 3, 4, 5, 6, 7, 8, 9};StringBuilder sb new StringBuilder();Random random new Random();for (int i 0; i num; i) {int index random.nextInt(chars.length);sb.append(chars[index]);}return sb.toString();}// 映射到/loginner的POST请求处理登录表单提交RequestMapping(/loginner)public ModelAndView login(ModelAndView modelAndView,RequestParam String adminCode,RequestParam String password,RequestParam String captcha, // 获取用户输入的验证码HttpSession session) {// 从 session 中获取生成的验证码String sessionCaptcha (String) session.getAttribute(captcha);// 验证验证码if (sessionCaptcha null || !sessionCaptcha.equalsIgnoreCase(captcha)) {// 验证码错误modelAndView.addObject(errorLogin, 验证码错误请重新输入);modelAndView.setViewName(login);return modelAndView;}// 验证用户名和密码if (userService.validateUser(adminCode, password)) {// 登录成功获取用户信息User user userService.getUserByAdminCode(adminCode);// 将用户信息存储在会话中可选session.setAttribute(loggedInUser, user);// 登录成功重定向到主页面modelAndView.setViewName(redirect:index);} else {// 登录失败返回登录页面并显示错误信息modelAndView.addObject(errorLogin, 用户名或密码错误请重试);modelAndView.setViewName(login);}return modelAndView;}
}2更新 login.jsp 前端页面
更新 login.jsp 以显示动态生成的验证码图片并确保用户可以刷新验证码。 % page contentTypetext/html;charsetUTF-8 languagejava %
% taglib urihttp://java.sun.com/jsp/jstl/core prefixc %
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title电信资费管理系统/titlelink typetext/css relstylesheet mediaall hrefstatics/styles/global.css /link typetext/css relstylesheet mediaall hrefstatics/styles/global_color.css /style/* 添加验证码图片样式 */.captcha-img {cursor: pointer;}/stylescript// JavaScript函数用于点击验证码图片时刷新function refreshCaptcha() {var captchaImg document.getElementById(captchaImage);// 添加一个随机参数以防止浏览器缓存captchaImg.src captcha? Math.random();}/script
/head
body classindex
div classlogin_boxform actionloginner methodposttabletrtd classlogin_info账号/tdtd colspan2input nameadminCode typetext classwidth150 required //tdtd classlogin_error_infospan classrequired30长度的字母、数字和下划线/span/td/trtrtd classlogin_info密码/tdtd colspan2input namepassword typepassword classwidth150 required //tdtdspan classrequired30长度的字母、数字和下划线/span/td/trtrtd classlogin_info验证码/tdtd classwidth70input namecaptcha typetext classwidth70 required //tdtd!-- 显示动态生成的验证码图片并添加点击事件以刷新 --img srccaptcha alt验证码 idcaptchaImage classcaptcha-img οnclickrefreshCaptcha() title点击更换 //td/trtrtd/tdtd classlogin_button colspan2input typesubmit value登录 classbtn_save styleborder: 1px solid orange//tdtd!-- 显示登录错误信息 --span classrequiredc:if test${not empty errorLogin}${errorLogin}/c:if/span/td/tr/table/form
/div
/body
/html4、增加页面表单验证功能。
1资费修改页面举例 fee_modi.jsp % page contentTypetext/html;charsetUTF-8 languagejava %
% taglib urihttp://java.sun.com/jsp/jstl/core prefixc %
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title电信资费管理系统/titlelink typetext/css relstylesheet mediaall href../statics/styles/global.css /link typetext/css relstylesheet mediaall href../statics/styles/global_color.css /script languagejavascript typetext/javascript//保存结果的提示function showResult() {showResultDiv(true);window.setTimeout(function() {showResultDiv(false);}, 3000);}function showResultDiv(flag) {var divResult document.getElementById(save_result_info);if (flag)divResult.style.display block;elsedivResult.style.display none;}//切换资费类型function feeTypeChange(type) {var inputArray document.getElementById(main).getElementsByTagName(input);if (type 1) {inputArray[4].readOnly true;inputArray[4].value ;inputArray[4].className readonly;inputArray[5].readOnly false;inputArray[5].className width100;inputArray[6].readOnly true;inputArray[6].className readonly;inputArray[6].value ;}else if (type 2) {inputArray[4].readOnly false;inputArray[4].className width100;inputArray[5].readOnly false;inputArray[5].className width100;inputArray[6].readOnly false;inputArray[6].className width100;}else if (type 3) {inputArray[4].readOnly true;inputArray[4].value ;inputArray[4].className readonly;inputArray[5].readOnly true;inputArray[5].value ;inputArray[5].className readonly;inputArray[6].readOnly false;inputArray[6].className width100;}}//表单验证function validateForm(formId) {const form document.getElementById(formId);const name form.querySelector(input[namename]);const baseDuration form.querySelector(input[namebaseDuration]);const baseCost form.querySelector(input[namebaseCost]);const unitCost form.querySelector(input[nameunitCost]);const descr form.querySelector(textarea[namedescr]);const privileges form.querySelectorAll(input[nameprivileges]:checked);let isValid true;let errorMessage ;// 验证资费名称if (name (name.value.length 0 || name.value.length 50)) {errorMessage 资费名称不能为空且必须在50字符以内。\n;isValid false;}// 验证基本时长if (baseDuration (baseDuration.value 1 || baseDuration.value 600)) {errorMessage 基本时长必须在1到600之间。\n;isValid false;}// 验证基本费用和单位费用if (baseCost (baseCost.value 0 || baseCost.value 99999.99)) {errorMessage 基本费用必须在0到99999.99之间。\n;isValid false;}if (unitCost (unitCost.value 0 || unitCost.value 99999.99)) {errorMessage 单位费用必须在0到99999.99之间。\n;isValid false;}// 验证描述if (descr descr.value.length 100) {errorMessage 资费说明长度不能超过100个字符。\n;isValid false;}// 验证权限仅在角色管理页面需要if (privileges privileges.length 0) {errorMessage 请至少选择一个权限。\n;isValid false;}// 如果有错误显示错误信息并阻止提交if (!isValid) {alert(errorMessage);}return isValid;}/script
/head
body
!--Logo区域开始--
div idheaderimg src../statics/images/logo.png altlogo classleft/a href${pageContext.request.contextPath}/toLogin[退出]/a
/div
!--Logo区域结束--
!-- 导航区域开始 --
jsp:include page../nav.jsp /
!-- 导航区域结束 --
!--主要区域开始--
div idmainform actionsaveModify methodPOST classmain_form οnsubmitreturn validateForm(formId);input typehidden namecostId value${cost.costId} /div classtext_info clearfixspan资费名称/span/divdiv classinput_infoinput typetext namename classwidth300 value${cost.name} required /span classrequired*/spandiv classvalidate_msg_short50长度的字母、数字、汉字和下划线的组合/div/divdiv classtext_info clearfixspan资费类型/span/divdiv classinput_info fee_typeinput typeradio namecostType value1 idmonthly οnclickfeeTypeChange(1); c:if test${cost.costType 1}checked/c:if /label formonthly包月/labelinput typeradio namecostType value2 idpackage οnclickfeeTypeChange(2); c:if test${cost.costType 2}checked/c:if /label forpackage套餐/labelinput typeradio namecostType value3 idtimeBased οnclickfeeTypeChange(3); c:if test${cost.costType 3}checked/c:if /label fortimeBased计时/label/divdiv classtext_info clearfixspan基本时长/span/divdiv classinput_infoinput typenumber namebaseDuration min1 max600 classwidth100 value${cost.baseDuration} required /span classinfo小时/spanspan classrequired*/spandiv classvalidate_msg_long1-600之间的整数/div/divdiv classtext_info clearfixspan基本费用/span/divdiv classinput_infoinput typenumber step0.01 namebaseCost classwidth100 value${cost.baseCost} required /span classinfo元/spanspan classrequired*/spandiv classvalidate_msg_long error_msg0-99999.99之间的数值/div/divdiv classtext_info clearfixspan单位费用/span/divdiv classinput_infoinput typenumber step0.01 nameunitCost classwidth100 value${cost.unitCost} required /span classinfo元/小时/spanspan classrequired*/spandiv classvalidate_msg_long error_msg0-99999.99之间的数值/div/divdiv classtext_info clearfixspan资费说明/span/divdiv classinput_info_hightextarea namedescr classwidth300 height70${cost.descr}/textareadiv classvalidate_msg_short error_msg100长度的字母、数字、汉字和下划线的组合/div/divdiv classbutton_info clearfixinput typesubmit value保存 classbtn_save οnclickshowResult(); /input typebutton value取消 classbtn_save οnclickwindow.location.hreflist; //div!-- 保存结果的提示 --div idsave_result_info classsave_fail styledisplay:none;保存成功/div/form
/div
!--主要区域结束--
div idfooterspan[Copyright © 1996-2024 NET Corporation, All Rights Reserved]/spanbr /span版权所有(C) Company NET/span
/div
/body
/html