提交 b3b9ff80 编写于 作者: 许雪里's avatar 许雪里

用户管理:支持在线维护系统用户

上级 b32791b0
......@@ -1469,7 +1469,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
- 1、[规划中] 移除quartz:精简底层实现,优化已知问题;
- 触发:单节点周期性触发,运行事件如delayqueue;
- 调度:集群竞争,负载方式协同处理,竞争-加入时间轮-释放-竞争;
- 2、[规划中] 用户管理:支持在线维护系统用户;
- 2、用户管理:支持在线维护系统用户;
- 3、[规划中] 权限管理:执行器为粒度分配权限,核心操作校验权限,暂定管理员、普通用户两种角色;
- 4、调度日志优化:支持设置日志保留天数,过期日志天维度记录报表,并清理;调度报表汇总实时数据和报表;
- 5、调度线程池参数调优;
......
......@@ -224,10 +224,20 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_GROUP` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `XXL_JOB_QRTZ_USER` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '账号',
`password` varchar(50) NOT NULL COMMENT '密码',
`role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员',
`permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割',
PRIMARY KEY (`id`),
UNIQUE KEY `i_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `XXL_JOB_QRTZ_TRIGGER_GROUP`(`id`, `app_name`, `title`, `order`, `address_type`, `address_list`) VALUES (1, 'xxl-job-executor-sample', '示例执行器', 1, 0, NULL);
INSERT INTO `XXL_JOB_QRTZ_TRIGGER_INFO`(`id`, `job_group`, `job_cron`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '0 0 0 * * ? *', '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '');
INSERT INTO `XXL_JOB_QRTZ_USER`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', '49ba59abbe56e057', 1, NULL);
commit;
package com.xxl.job.admin.controller;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobUser;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.dao.XxlJobGroupDao;
import com.xxl.job.admin.dao.XxlJobUserDao;
import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author xuxueli 2019-05-04 16:39:50
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Resource
private XxlJobUserDao xxlJobUserDao;
@Resource
private XxlJobGroupDao xxlJobGroupDao;
@RequestMapping
public String index(Model model) {
// 执行器列表
List<XxlJobGroup> groupList = xxlJobGroupDao.findAll();
model.addAttribute("groupList", groupList);
return "user/user.index";
}
@RequestMapping("/pageList")
@ResponseBody
public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,
@RequestParam(required = false, defaultValue = "10") int length,
String username) {
// page list
List<XxlJobUser> list = xxlJobUserDao.pageList(start, length, username);
int list_count = xxlJobUserDao.pageListCount(start, length, username);
// package result
Map<String, Object> maps = new HashMap<String, Object>();
maps.put("recordsTotal", list_count); // 总记录数
maps.put("recordsFiltered", list_count); // 过滤后的总记录数
maps.put("data", list); // 分页列表
return maps;
}
@RequestMapping("/add")
@ResponseBody
public ReturnT<String> add(XxlJobUser xxlJobUser) {
// valid username
if (!StringUtils.hasText(xxlJobUser.getUsername())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_please_input")+I18nUtil.getString("user_username") );
}
xxlJobUser.setUsername(xxlJobUser.getUsername().trim());
if (!(xxlJobUser.getUsername().length()>=4 && xxlJobUser.getUsername().length()<=20)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit")+"[4-20]" );
}
// valid password
if (!StringUtils.hasText(xxlJobUser.getPassword())) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_please_input")+I18nUtil.getString("user_password") );
}
xxlJobUser.setPassword(xxlJobUser.getPassword().trim());
if (!(xxlJobUser.getPassword().length()>=4 && xxlJobUser.getPassword().length()<=20)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit")+"[4-20]" );
}
// md5 password
xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes()));
// check repeat
XxlJobUser existUser = xxlJobUserDao.loadByUserName(xxlJobUser.getUsername());
if (existUser != null) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("user_username_repeat") );
}
// write
xxlJobUserDao.save(xxlJobUser);
return ReturnT.SUCCESS;
}
@RequestMapping("/update")
@ResponseBody
public ReturnT<String> update(XxlJobUser xxlJobUser) {
// valid password
if (StringUtils.hasText(xxlJobUser.getPassword())) {
xxlJobUser.setPassword(xxlJobUser.getPassword().trim());
if (!(xxlJobUser.getPassword().length()>=4 && xxlJobUser.getPassword().length()<=20)) {
return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("system_lengh_limit")+"[4-20]" );
}
// md5 password
xxlJobUser.setPassword(DigestUtils.md5DigestAsHex(xxlJobUser.getPassword().getBytes()));
} else {
xxlJobUser.setPassword(null);
}
// write
xxlJobUserDao.update(xxlJobUser);
return ReturnT.SUCCESS;
}
@RequestMapping("/remove")
@ResponseBody
public ReturnT<String> remove(int id) {
xxlJobUserDao.delete(id);
return ReturnT.SUCCESS;
}
}
package com.xxl.job.admin.core.model;
/**
* @author xuxueli 2019-05-04 16:43:12
*/
public class XxlJobUser {
private int id;
private String username; // 账号
private String password; // 密码
private int role; // 角色:0-普通用户、1-管理员
private String permission; // 权限:执行器ID列表,多个逗号分割
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
}
package com.xxl.job.admin.dao;
import com.xxl.job.admin.core.model.XxlJobUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author xuxueli 2019-05-04 16:44:59
*/
@Mapper
public interface XxlJobUserDao {
public List<XxlJobUser> pageList(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("username") String username);
public int pageListCount(@Param("offset") int offset,
@Param("pagesize") int pagesize,
@Param("username") String username);
public XxlJobUser loadByUserName(@Param("username") String username);
public int save(XxlJobUser xxlJobUser);
public int update(XxlJobUser xxlJobUser);
public int delete(@Param("id") int id);
}
......@@ -31,6 +31,7 @@ system_unvalid=非法
system_not_found=不存在
system_nav=导航
system_digits=整数
system_lengh_limit=长度限制
## daterangepicker
daterangepicker_ranges_recent_hour=最近一小时
......@@ -229,6 +230,19 @@ jobconf_trigger_type_parent=父任务触发
jobconf_trigger_type_api=API触发
jobconf_trigger_type_retry=失败重试触发
## user
user_manage=用户管理
user_username=账号
user_password=密码
user_role=角色
user_role_admin=管理员
user_role_normal=普通用户
user_permission=权限
user_add=新增用户
user_update=更新用户
user_username_repeat=账号重复
user_password_update_placeholder=请输入新密码,为空则不更新密码
## help
job_help=使用教程
job_help_document=官方文档
\ No newline at end of file
......@@ -31,6 +31,7 @@ system_unvalid=illegal
system_not_found=not exist
system_nav=Navigation
system_digits=digits
system_lengh_limit=Length limit
## daterangepicker
daterangepicker_ranges_recent_hour=recent one hour
......@@ -229,6 +230,19 @@ jobconf_trigger_type_parent=Parent job trigger
jobconf_trigger_type_api=Api trigger
jobconf_trigger_type_retry=Fail retry trigger
## user
user_manage==User Manage
user_username=Username
user_password=Password
user_role=Role
user_role_admin=Admin User
user_role_normal=Normal User
user_permission=Permission
user_add=Add User
user_update=Edit User
user_username_repeat=Username Repeat
user_password_update_placeholder=Please input password, empty means not update
## help
job_help=Tutorial
job_help_document=Official Document
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxl.job.admin.dao.XxlJobUserDao">
<resultMap id="XxlJobUser" type="com.xxl.job.admin.core.model.XxlJobUser" >
<result column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="role" property="role" />
<result column="permission" property="permission" />
</resultMap>
<sql id="Base_Column_List">
t.id,
t.username,
t.password,
t.role,
t.permission
</sql>
<select id="pageList" parameterType="java.util.HashMap" resultMap="XxlJobUser">
SELECT <include refid="Base_Column_List" />
FROM XXL_JOB_QRTZ_USER AS t
<trim prefix="WHERE" prefixOverrides="AND | OR" >
<if test="username != null and username != ''">
AND t.username like CONCAT(CONCAT('%', #{username}), '%')
</if>
</trim>
ORDER BY username ASC
LIMIT #{offset}, #{pagesize}
</select>
<select id="pageListCount" parameterType="java.util.HashMap" resultType="int">
SELECT count(1)
FROM XXL_JOB_QRTZ_USER AS t
<trim prefix="WHERE" prefixOverrides="AND | OR" >
<if test="username != null and username != ''">
AND t.username like CONCAT(CONCAT('%', #{username}), '%')
</if>
</trim>
</select>
<select id="loadByUserName" parameterType="java.util.HashMap" resultMap="XxlJobUser">
SELECT <include refid="Base_Column_List" />
FROM XXL_JOB_QRTZ_USER AS t
WHERE t.username = #{username}
</select>
<insert id="save" parameterType="com.xxl.job.admin.core.model.XxlJobUser" useGeneratedKeys="true" keyProperty="id" >
INSERT INTO XXL_JOB_QRTZ_USER (
username,
password,
role,
permission
) VALUES (
#{username},
#{password},
#{role},
#{permission}
);
</insert>
<update id="update" parameterType="com.xxl.job.admin.core.model.XxlJobUser" >
UPDATE XXL_JOB_QRTZ_USER
SET
<if test="password != null and password != ''">
password = #{password},
</if>
role = #{role},
permission = #{permission}
WHERE id = #{id}
</update>
<delete id="delete" parameterType="java.util.HashMap">
DELETE
FROM XXL_JOB_QRTZ_USER
WHERE id = #{id}
</delete>
</mapper>
\ No newline at end of file
$(function() {
// init date tables
var userListTable = $("#user_list").dataTable({
"deferRender": true,
"processing" : true,
"serverSide": true,
"ajax": {
url: base_url + "/user/pageList",
type:"post",
data : function ( d ) {
var obj = {};
obj.username = $('#username').val();
obj.start = d.start;
obj.length = d.length;
return obj;
}
},
"searching": false,
"ordering": false,
//"scrollX": true, // scroll x,close self-adaption
"columns": [
{
"data": 'id',
"visible" : false,
"width":'10%'
},
{
"data": 'username',
"visible" : true,
"width":'20%'
},
{
"data": 'password',
"visible" : true,
"width":'20%'
},
{
"data": 'role',
"visible" : true,
"width":'10%',
"render": function ( data, type, row ) {
if (data == 1) {
return I18n.user_role_admin
} else {
return I18n.user_role_normal
}
}
},
{
"data": 'permission',
"width":'10%',
"visible" : true
},
{
"data": I18n.system_opt ,
"width":'15%',
"render": function ( data, type, row ) {
return function(){
// html
tableData['key'+row.id] = row;
var html = '<p id="'+ row.id +'" >'+
'<button class="btn btn-warning btn-xs update" type="button">'+ I18n.system_opt_edit +'</button> '+
'<button class="btn btn-danger btn-xs delete" type="button">'+ I18n.system_opt_del +'</button> '+
'</p>';
return html;
};
}
}
],
"language" : {
"sProcessing" : I18n.dataTable_sProcessing ,
"sLengthMenu" : I18n.dataTable_sLengthMenu ,
"sZeroRecords" : I18n.dataTable_sZeroRecords ,
"sInfo" : I18n.dataTable_sInfo ,
"sInfoEmpty" : I18n.dataTable_sInfoEmpty ,
"sInfoFiltered" : I18n.dataTable_sInfoFiltered ,
"sInfoPostFix" : "",
"sSearch" : I18n.dataTable_sSearch ,
"sUrl" : "",
"sEmptyTable" : I18n.dataTable_sEmptyTable ,
"sLoadingRecords" : I18n.dataTable_sLoadingRecords ,
"sInfoThousands" : ",",
"oPaginate" : {
"sFirst" : I18n.dataTable_sFirst ,
"sPrevious" : I18n.dataTable_sPrevious ,
"sNext" : I18n.dataTable_sNext ,
"sLast" : I18n.dataTable_sLast
},
"oAria" : {
"sSortAscending" : I18n.dataTable_sSortAscending ,
"sSortDescending" : I18n.dataTable_sSortDescending
}
}
});
// table data
var tableData = {};
// search btn
$('#searchBtn').on('click', function(){
userListTable.fnDraw();
});
// job operate
$("#user_list").on('click', '.delete',function() {
var id = $(this).parent('p').attr("id");
layer.confirm( I18n.system_ok + I18n.system_opt_del + '?', {
icon: 3,
title: I18n.system_tips ,
btn: [ I18n.system_ok, I18n.system_cancel ]
}, function(index){
layer.close(index);
$.ajax({
type : 'POST',
url : base_url + "/user/remove",
data : {
"id" : id
},
dataType : "json",
success : function(data){
if (data.code == 200) {
layer.msg( I18n.system_success );
userListTable.fnDraw(false);
} else {
layer.msg( data.msg || I18n.system_opt_del + I18n.system_fail );
}
}
});
});
});
// add
$(".add").click(function(){
$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');
});
var addModalValidate = $("#addModal .form").validate({
errorElement : 'span',
errorClass : 'help-block',
focusInvalid : true,
rules : {
username : {
required : true,
rangelength:[4, 20]
},
password : {
required : true,
rangelength:[4, 20]
}
},
messages : {
username : {
required : I18n.system_please_input + I18n.user_username,
rangelength: I18n.system_lengh_limit + "[4-20]"
},
password : {
required : I18n.system_please_input + I18n.user_password,
rangelength: I18n.system_lengh_limit + "[4-20]"
}
},
highlight : function(element) {
$(element).closest('.form-group').addClass('has-error');
},
success : function(label) {
label.closest('.form-group').removeClass('has-error');
label.remove();
},
errorPlacement : function(error, element) {
element.parent('div').append(error);
},
submitHandler : function(form) {
var permissionArr = [];
$("#addModal .form input[name=permission]:checked").each(function(){
permissionArr.push($(this).val());
});
var paramData = {
"username": $("#addModal .form input[name=username]").val(),
"password": $("#addModal .form input[name=password]").val(),
"role": $("#addModal .form input[name=role]:checked").val(),
"permission": permissionArr.join(',')
};
$.post(base_url + "/user/add", paramData, function(data, status) {
if (data.code == "200") {
$('#addModal').modal('hide');
layer.msg( I18n.system_add_suc );
userListTable.fnDraw();
} else {
layer.open({
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_add_fail),
icon: '2'
});
}
});
}
});
$("#addModal").on('hide.bs.modal', function () {
$("#addModal .form")[0].reset();
addModalValidate.resetForm();
$("#addModal .form .form-group").removeClass("has-error");
$(".remote_panel").show(); // remote
});
// update
$("#user_list").on('click', '.update',function() {
var id = $(this).parent('p').attr("id");
var row = tableData['key'+id];
// base data
$("#updateModal .form input[name='id']").val( row.id );
$("#updateModal .form input[name='username']").val( row.username );
$("#updateModal .form input[name='password']").val( '' );
$("#updateModal .form input[name='role']").each(function () {
if($(this).val() == row.role) {
$(this).prop("checked",true);
} else {
$(this).prop("checked",false);
}
});
var permissionArr = [];
if (row.permission) {
permissionArr = row.permission.split(",");
}
$("#updateModal .form input[name='permission']").removeProp('checked');
$("#updateModal .form input[name='permission']").each(function () {
if($.inArray($(this).val(), permissionArr) > -1) {
$(this).prop("checked",true);
} else {
$(this).prop("checked",false);
}
});
// show
$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
});
var updateModalValidate = $("#updateModal .form").validate({
errorElement : 'span',
errorClass : 'help-block',
focusInvalid : true,
highlight : function(element) {
$(element).closest('.form-group').addClass('has-error');
},
success : function(label) {
label.closest('.form-group').removeClass('has-error');
label.remove();
},
errorPlacement : function(error, element) {
element.parent('div').append(error);
},
submitHandler : function(form) {
var permissionArr =[];
$("#updateModal .form input[name=permission]:checked").each(function(){
permissionArr.push($(this).val());
});
var paramData = {
"id": $("#updateModal .form input[name=id]").val(),
"username": $("#updateModal .form input[name=username]").val(),
"password": $("#updateModal .form input[name=password]").val(),
"role": $("#updateModal .form input[name=role]:checked").val(),
"permission": permissionArr.join(',')
};
$.post(base_url + "/user/update", paramData, function(data, status) {
if (data.code == "200") {
$('#updateModal').modal('hide');
layer.msg( I18n.system_update_suc );
userListTable.fnDraw();
} else {
layer.open({
title: I18n.system_tips ,
btn: [ I18n.system_ok ],
content: (data.msg || I18n.system_update_fail),
icon: '2'
});
}
});
}
});
$("#updateModal").on('hide.bs.modal', function () {
$("#updateModal .form")[0].reset();
updateModalValidate.resetForm();
$("#updateModal .form .form-group").removeClass("has-error");
$(".remote_panel").show(); // remote
});
});
......@@ -103,6 +103,7 @@
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.jobinfo_name}</span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-green"></i><span>${I18n.joblog_name}</span></a></li>
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i><span>${I18n.jobgroup_name}</span></a></li>
<li class="nav-click <#if pageName == "user">active</#if>" ><a href="${request.contextPath}/user"><i class="fa fa-circle-o text-purple"></i><span>${I18n.user_manage}</span></a></li>
<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li>
</ul>
</section>
......
<!DOCTYPE html>
<html>
<head>
<#import "../common/common.macro.ftl" as netCommon>
<@netCommon.commonStyle />
<!-- DataTables -->
<link rel="stylesheet" href="${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css">
<title>${I18n.admin_name}</title>
</head>
<body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && cookieMap["xxljob_adminlte_settings"]?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if>">
<div class="wrapper">
<!-- header -->
<@netCommon.commonHeader />
<!-- left -->
<@netCommon.commonLeft "user" />
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Content Header (Page header) -->
<section class="content-header">
<h1>${I18n.user_manage}</h1>
</section>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-xs-3">
<div class="input-group">
<span class="input-group-addon">${I18n.user_username}</span>
<input type="text" class="form-control" id="username" autocomplete="on" >
</div>
</div>
<div class="col-xs-1">
<button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
</div>
<div class="col-xs-2">
<button class="btn btn-block btn-success add" type="button">${I18n.user_add}</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-body" >
<table id="user_list" class="table table-bordered table-striped" width="100%" >
<thead>
<tr>
<th name="id" >ID</th>
<th name="username" >${I18n.user_username}</th>
<th name="password" >${I18n.user_password}</th>
<th name="role" >${I18n.user_role}</th>
<th name="permission" >${I18n.user_permission}</th>
<th>${I18n.system_opt}</th>
</tr>
</thead>
<tbody></tbody>
<tfoot></tfoot>
</table>
</div>
</div>
</div>
</div>
</section>
</div>
<!-- footer -->
<@netCommon.commonFooter />
</div>
<!-- 新增.模态框 -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >${I18n.user_add}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_username}<font color="red">*</font></label>
<div class="col-sm-8"><input type="text" class="form-control" name="username" placeholder="${I18n.system_please_input}${I18n.user_username}" maxlength="50" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_password}<font color="red">*</font></label>
<div class="col-sm-8"><input type="text" class="form-control" name="password" placeholder="${I18n.system_please_input}${I18n.user_password}" maxlength="50" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_role}<font color="red">*</font></label>
<div class="col-sm-10">
<input type="radio" name="role" value="0" checked />${I18n.user_role_normal}
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" name="role" value="1" />${I18n.user_role_admin}
</div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_permission}<font color="black">*</font></label>
<div class="col-sm-10">
<#if groupList?exists && groupList?size gt 0>
<#list groupList as item>
<input type="checkbox" name="permission" value="${item.id}" />${item.title}(${item.appName})<br>
</#list>
</#if>
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- 更新.模态框 -->
<div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" >${I18n.user_update}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal form" role="form" >
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_username}<font color="red">*</font></label>
<div class="col-sm-8"><input type="text" class="form-control" name="username" placeholder="${I18n.system_please_input}${I18n.user_username}" maxlength="50" readonly ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_password}<font color="red">*</font></label>
<div class="col-sm-8"><input type="text" class="form-control" name="password" placeholder="${I18n.user_password_update_placeholder}" maxlength="50" ></div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_role}<font color="red">*</font></label>
<div class="col-sm-10">
<input type="radio" name="role" value="0" />${I18n.user_role_normal}
&nbsp;&nbsp;&nbsp;&nbsp;
<input type="radio" name="role" value="1" />${I18n.user_role_admin}
</div>
</div>
<div class="form-group">
<label for="lastname" class="col-sm-2 control-label">${I18n.user_permission}<font color="black">*</font></label>
<div class="col-sm-10">
<#if groupList?exists && groupList?size gt 0>
<#list groupList as item>
<input type="checkbox" name="permission" value="${item.id}" />${item.title}(${item.appName})<br>
</#list>
</#if>
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-primary" >${I18n.system_save}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
<input type="hidden" name="id" >
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<@netCommon.commonScript />
<!-- DataTables -->
<script src="${request.contextPath}/static/adminlte/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="${request.contextPath}/static/adminlte/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<script src="${request.contextPath}/static/plugins/jquery/jquery.validate.min.js"></script>
<script src="${request.contextPath}/static/js/user.index.1.js"></script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册