Browse Source

1.新增行政区划管理功能

2.新增根据行政计划管理用户功能
3.新增矢量图标
master
ma-zhongxu 1 month ago
parent
commit
c941194dea
  1. 123
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAreaController.java
  2. 41
      chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysUserController.java
  3. 177
      chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysArea.java
  4. 24
      chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUser.java
  5. 61
      chenhai-system/src/main/java/com/chenhai/system/mapper/SysAreaMapper.java
  6. 2
      chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserMapper.java
  7. 61
      chenhai-system/src/main/java/com/chenhai/system/service/ISysAreaService.java
  8. 9
      chenhai-system/src/main/java/com/chenhai/system/service/ISysUserService.java
  9. 96
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAreaServiceImpl.java
  10. 6
      chenhai-system/src/main/java/com/chenhai/system/service/impl/SysUserServiceImpl.java
  11. 116
      chenhai-system/src/main/resources/mapper/system/SysAreaMapper.xml
  12. 45
      chenhai-system/src/main/resources/mapper/system/SysUserMapper.xml
  13. 63
      chenhai-ui/src/api/system/area.js
  14. 29
      chenhai-ui/src/api/system/user.js
  15. 1
      chenhai-ui/src/assets/icons/svg/userinfo.svg
  16. 426
      chenhai-ui/src/views/system/area/index.vue
  17. 736
      chenhai-ui/src/views/system/userInfo/index.vue

123
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysAreaController.java

@ -0,0 +1,123 @@
package com.chenhai.web.controller.system;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController;
import com.chenhai.common.core.domain.AjaxResult;
import com.chenhai.common.enums.BusinessType;
import com.chenhai.common.core.domain.entity.SysArea;
import com.chenhai.system.service.ISysAreaService;
import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.common.core.page.TableDataInfo;
/**
* 行政区划Controller
*
* @author ruoyi
* @date 2026-01-09
*/
@RestController
@RequestMapping("/system/area")
public class SysAreaController extends BaseController
{
@Autowired
private ISysAreaService sysAreaService;
/**
* 查询行政区划列表
*/
@PreAuthorize("@ss.hasPermi('system:area:list')")
@GetMapping("/list")
public TableDataInfo list(SysArea sysArea)
{
startPage();
List<SysArea> list = sysAreaService.selectSysAreaList(sysArea);
return getDataTable(list);
}
/**
* 导出行政区划列表
*/
@PreAuthorize("@ss.hasPermi('system:area:export')")
@Log(title = "行政区划", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, SysArea sysArea)
{
List<SysArea> list = sysAreaService.selectSysAreaList(sysArea);
ExcelUtil<SysArea> util = new ExcelUtil<SysArea>(SysArea.class);
util.exportExcel(response, list, "行政区划数据");
}
/**
* 获取行政区划详细信息
*/
@PreAuthorize("@ss.hasPermi('system:area:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(sysAreaService.selectSysAreaById(id));
}
/**
* 新增行政区划
*/
@PreAuthorize("@ss.hasPermi('system:area:add')")
@Log(title = "行政区划", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysArea sysArea)
{
return toAjax(sysAreaService.insertSysArea(sysArea));
}
/**
* 修改行政区划
*/
@PreAuthorize("@ss.hasPermi('system:area:edit')")
@Log(title = "行政区划", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysArea sysArea)
{
return toAjax(sysAreaService.updateSysArea(sysArea));
}
/**
* 删除行政区划
*/
@PreAuthorize("@ss.hasPermi('system:area:remove')")
@Log(title = "行政区划", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(sysAreaService.deleteSysAreaByIds(ids));
}
/**
* 懒加载查询子节点
*/
@PreAuthorize("@ss.hasPermi('system:area:list')")
@GetMapping("/children")
public AjaxResult getChildren(@RequestParam(value = "parentCode", required = false, defaultValue = "152900") String parentCode) {
SysArea query = new SysArea();
query.setParentCode(parentCode);
query.setDeleted(0);
List<SysArea> list = sysAreaService.selectSysAreaList(query);
return success(list);
}
/**
* 获取所有省级节点type=2的就是省级
*/
@PreAuthorize("@ss.hasPermi('system:area:list')")
@GetMapping("/provinces")
public AjaxResult getProvinces() {
SysArea query = new SysArea();
query.setType(2); // 省级
query.setDeleted(0);
List<SysArea> list = sysAreaService.selectSysAreaList(query);
return success(list);
}
}

41
chenhai-admin/src/main/java/com/chenhai/web/controller/system/SysUserController.java

@ -2,19 +2,15 @@ package com.chenhai.web.controller.system;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.chenhai.common.core.domain.entity.SysArea;
import com.chenhai.system.service.*;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.chenhai.common.annotation.Log; import com.chenhai.common.annotation.Log;
import com.chenhai.common.core.controller.BaseController; import com.chenhai.common.core.controller.BaseController;
@ -27,10 +23,6 @@ import com.chenhai.common.enums.BusinessType;
import com.chenhai.common.utils.SecurityUtils; import com.chenhai.common.utils.SecurityUtils;
import com.chenhai.common.utils.StringUtils; import com.chenhai.common.utils.StringUtils;
import com.chenhai.common.utils.poi.ExcelUtil; import com.chenhai.common.utils.poi.ExcelUtil;
import com.chenhai.system.service.ISysDeptService;
import com.chenhai.system.service.ISysPostService;
import com.chenhai.system.service.ISysRoleService;
import com.chenhai.system.service.ISysUserService;
/** /**
* 用户信息 * 用户信息
@ -53,6 +45,9 @@ public class SysUserController extends BaseController
@Autowired @Autowired
private ISysPostService postService; private ISysPostService postService;
@Autowired
private ISysAreaService sysAreaService;
/** /**
* 获取用户列表 * 获取用户列表
*/ */
@ -65,6 +60,15 @@ public class SysUserController extends BaseController
return getDataTable(list); return getDataTable(list);
} }
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/listByAreaCode")
public TableDataInfo listByAreaCode(SysUser user)
{
startPage();
List<SysUser> list = userService.selectUserListByAreaCode(user);
return getDataTable(list);
}
@Log(title = "用户管理", businessType = BusinessType.EXPORT) @Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')") @PreAuthorize("@ss.hasPermi('system:user:export')")
@PostMapping("/export") @PostMapping("/export")
@ -253,4 +257,17 @@ public class SysUserController extends BaseController
{ {
return success(deptService.selectDeptTreeList(dept)); return success(deptService.selectDeptTreeList(dept));
} }
/**
* 获取区划树列表
*/
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/areaChildren")
public AjaxResult getAreaChildren(@RequestParam(value = "parentCode", required = false, defaultValue = "152900") String parentCode) {
SysArea query = new SysArea();
query.setParentCode(parentCode);
query.setDeleted(0);
List<SysArea> list = sysAreaService.selectSysAreaList(query);
return success(list);
}
} }

177
chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysArea.java

@ -0,0 +1,177 @@
package com.chenhai.common.core.domain.entity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.chenhai.common.annotation.Excel;
import com.chenhai.common.core.domain.BaseEntity;
/**
* 行政区划对象 sys_area
*
* @author ruoyi
* @date 2026-01-09
*/
public class SysArea extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 自增ID */
private Long id;
/** 名称 */
@Excel(name = "名称")
private String name;
/** 编码 */
@Excel(name = "编码")
private String code;
/** 父节点编码 */
@Excel(name = "父节点编码")
private String parentCode;
/** 节点类型(1:国;2:省;3:市;4:县;5:街道;6:村) */
@Excel(name = "节点类型", readConverterExp = "1=:国;2:省;3:市;4:县;5:街道;6:村")
private Integer type;
/** 显示顺序 */
@Excel(name = "显示顺序")
private Long sort;
/** 行政区划节点状态(0:开启,1:关闭) */
@Excel(name = "行政区划节点状态", readConverterExp = "0=:开启,1:关闭")
private Integer status;
/** 全名称 */
@Excel(name = "全名称")
private String namePath;
/** 全编码 */
@Excel(name = "全编码")
private String codePath;
/** 是否删除 */
@Excel(name = "是否删除")
private Integer deleted;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setCode(String code)
{
this.code = code;
}
public String getCode()
{
return code;
}
public void setParentCode(String parentCode)
{
this.parentCode = parentCode;
}
public String getParentCode()
{
return parentCode;
}
public void setType(Integer type)
{
this.type = type;
}
public Integer getType()
{
return type;
}
public void setSort(Long sort)
{
this.sort = sort;
}
public Long getSort()
{
return sort;
}
public void setStatus(Integer status)
{
this.status = status;
}
public Integer getStatus()
{
return status;
}
public void setNamePath(String namePath)
{
this.namePath = namePath;
}
public String getNamePath()
{
return namePath;
}
public void setCodePath(String codePath)
{
this.codePath = codePath;
}
public String getCodePath()
{
return codePath;
}
public void setDeleted(Integer deleted)
{
this.deleted = deleted;
}
public Integer getDeleted()
{
return deleted;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("name", getName())
.append("code", getCode())
.append("parentCode", getParentCode())
.append("type", getType())
.append("sort", getSort())
.append("status", getStatus())
.append("namePath", getNamePath())
.append("codePath", getCodePath())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("deleted", getDeleted())
.append("remark", getRemark())
.toString();
}
}

24
chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUser.java

@ -39,7 +39,7 @@ public class SysUser extends BaseEntity
private String nickName; private String nickName;
/** 用户类型 */ /** 用户类型 */
@Excel(name = "用户类型", readConverterExp = "00=系统用户,01=注册用户")
@Excel(name = "用户类型", readConverterExp = "00=系统用户,01=兽医用户,02=牧户用户")
private String userType; private String userType;
/** 用户邮箱 */ /** 用户邮箱 */
@ -78,6 +78,10 @@ public class SysUser extends BaseEntity
/** 密码最后更新时间 */ /** 密码最后更新时间 */
private Date pwdUpdateDate; private Date pwdUpdateDate;
/** 行政区划 */
@Excel(name = "行政区划")
private String areaCode;
/** 部门对象 */ /** 部门对象 */
@Excels({ @Excels({
@Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
@ -85,6 +89,8 @@ public class SysUser extends BaseEntity
}) })
private SysDept dept; private SysDept dept;
private SysArea area;
/** 角色对象 */ /** 角色对象 */
private List<SysRole> roles; private List<SysRole> roles;
@ -274,6 +280,14 @@ public class SysUser extends BaseEntity
this.pwdUpdateDate = pwdUpdateDate; this.pwdUpdateDate = pwdUpdateDate;
} }
public String getAreaCode() {
return areaCode;
}
public void setAreaCode(String areaCode) {
this.areaCode = areaCode;
}
public SysDept getDept() public SysDept getDept()
{ {
return dept; return dept;
@ -284,6 +298,14 @@ public class SysUser extends BaseEntity
this.dept = dept; this.dept = dept;
} }
public SysArea getArea() {
return area;
}
public void setArea(SysArea area) {
this.area = area;
}
public List<SysRole> getRoles() public List<SysRole> getRoles()
{ {
return roles; return roles;

61
chenhai-system/src/main/java/com/chenhai/system/mapper/SysAreaMapper.java

@ -0,0 +1,61 @@
package com.chenhai.system.mapper;
import java.util.List;
import com.chenhai.common.core.domain.entity.SysArea;
/**
* 行政区划Mapper接口
*
* @author ruoyi
* @date 2026-01-09
*/
public interface SysAreaMapper
{
/**
* 查询行政区划
*
* @param id 行政区划主键
* @return 行政区划
*/
public SysArea selectSysAreaById(Long id);
/**
* 查询行政区划列表
*
* @param sysArea 行政区划
* @return 行政区划集合
*/
public List<SysArea> selectSysAreaList(SysArea sysArea);
/**
* 新增行政区划
*
* @param sysArea 行政区划
* @return 结果
*/
public int insertSysArea(SysArea sysArea);
/**
* 修改行政区划
*
* @param sysArea 行政区划
* @return 结果
*/
public int updateSysArea(SysArea sysArea);
/**
* 删除行政区划
*
* @param id 行政区划主键
* @return 结果
*/
public int deleteSysAreaById(Long id);
/**
* 批量删除行政区划
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteSysAreaByIds(Long[] ids);
}

2
chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserMapper.java

@ -20,6 +20,8 @@ public interface SysUserMapper
*/ */
public List<SysUser> selectUserList(SysUser sysUser); public List<SysUser> selectUserList(SysUser sysUser);
List<SysUser> selectUserListByAreaCode(SysUser user);
/** /**
* 根据条件分页查询已配用户角色列表 * 根据条件分页查询已配用户角色列表
* *

61
chenhai-system/src/main/java/com/chenhai/system/service/ISysAreaService.java

@ -0,0 +1,61 @@
package com.chenhai.system.service;
import java.util.List;
import com.chenhai.common.core.domain.entity.SysArea;
/**
* 行政区划Service接口
*
* @author ruoyi
* @date 2026-01-09
*/
public interface ISysAreaService
{
/**
* 查询行政区划
*
* @param id 行政区划主键
* @return 行政区划
*/
public SysArea selectSysAreaById(Long id);
/**
* 查询行政区划列表
*
* @param sysArea 行政区划
* @return 行政区划集合
*/
public List<SysArea> selectSysAreaList(SysArea sysArea);
/**
* 新增行政区划
*
* @param sysArea 行政区划
* @return 结果
*/
public int insertSysArea(SysArea sysArea);
/**
* 修改行政区划
*
* @param sysArea 行政区划
* @return 结果
*/
public int updateSysArea(SysArea sysArea);
/**
* 批量删除行政区划
*
* @param ids 需要删除的行政区划主键集合
* @return 结果
*/
public int deleteSysAreaByIds(Long[] ids);
/**
* 删除行政区划信息
*
* @param id 行政区划主键
* @return 结果
*/
public int deleteSysAreaById(Long id);
}

9
chenhai-system/src/main/java/com/chenhai/system/service/ISysUserService.java

@ -19,6 +19,14 @@ public interface ISysUserService
*/ */
public List<SysUser> selectUserList(SysUser user); public List<SysUser> selectUserList(SysUser user);
/**
* 根据条件分页查询用户列表(关联行政区划表)
*
* @param user 用户信息
* @return 用户信息集合信息
*/
List<SysUser> selectUserListByAreaCode(SysUser user);
/** /**
* 根据条件分页查询已分配用户角色列表 * 根据条件分页查询已分配用户角色列表
* *
@ -222,4 +230,5 @@ public interface ISysUserService
* @return 结果 * @return 结果
*/ */
public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName); public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
} }

96
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysAreaServiceImpl.java

@ -0,0 +1,96 @@
package com.chenhai.system.service.impl;
import java.util.List;
import com.chenhai.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.chenhai.system.mapper.SysAreaMapper;
import com.chenhai.common.core.domain.entity.SysArea;
import com.chenhai.system.service.ISysAreaService;
/**
* 行政区划Service业务层处理
*
* @author ruoyi
* @date 2026-01-09
*/
@Service
public class SysAreaServiceImpl implements ISysAreaService
{
@Autowired
private SysAreaMapper sysAreaMapper;
/**
* 查询行政区划
*
* @param id 行政区划主键
* @return 行政区划
*/
@Override
public SysArea selectSysAreaById(Long id)
{
return sysAreaMapper.selectSysAreaById(id);
}
/**
* 查询行政区划列表
*
* @param sysArea 行政区划
* @return 行政区划
*/
@Override
public List<SysArea> selectSysAreaList(SysArea sysArea)
{
return sysAreaMapper.selectSysAreaList(sysArea);
}
/**
* 新增行政区划
*
* @param sysArea 行政区划
* @return 结果
*/
@Override
public int insertSysArea(SysArea sysArea)
{
sysArea.setCreateTime(DateUtils.getNowDate());
return sysAreaMapper.insertSysArea(sysArea);
}
/**
* 修改行政区划
*
* @param sysArea 行政区划
* @return 结果
*/
@Override
public int updateSysArea(SysArea sysArea)
{
sysArea.setUpdateTime(DateUtils.getNowDate());
return sysAreaMapper.updateSysArea(sysArea);
}
/**
* 批量删除行政区划
*
* @param ids 需要删除的行政区划主键
* @return 结果
*/
@Override
public int deleteSysAreaByIds(Long[] ids)
{
return sysAreaMapper.deleteSysAreaByIds(ids);
}
/**
* 删除行政区划信息
*
* @param id 行政区划主键
* @return 结果
*/
@Override
public int deleteSysAreaById(Long id)
{
return sysAreaMapper.deleteSysAreaById(id);
}
}

6
chenhai-system/src/main/java/com/chenhai/system/service/impl/SysUserServiceImpl.java

@ -79,6 +79,12 @@ public class SysUserServiceImpl implements ISysUserService
return userMapper.selectUserList(user); return userMapper.selectUserList(user);
} }
@Override
@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserListByAreaCode(SysUser user) {
return userMapper.selectUserListByAreaCode(user);
}
/** /**
* 根据条件分页查询已分配用户角色列表 * 根据条件分页查询已分配用户角色列表
* *

116
chenhai-system/src/main/resources/mapper/system/SysAreaMapper.xml

@ -0,0 +1,116 @@
<?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.chenhai.system.mapper.SysAreaMapper">
<resultMap type="SysArea" id="SysAreaResult">
<result property="id" column="id" />
<result property="name" column="name" />
<result property="code" column="code" />
<result property="parentCode" column="parent_code" />
<result property="type" column="type" />
<result property="sort" column="sort" />
<result property="status" column="status" />
<result property="namePath" column="name_path" />
<result property="codePath" column="code_path" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="deleted" column="deleted" />
<result property="remark" column="remark" />
</resultMap>
<sql id="selectSysAreaVo">
select id, name, code, parent_code, type, sort, status, name_path, code_path, create_by, create_time, update_by, update_time, deleted, remark from sys_area
</sql>
<select id="selectSysAreaList" parameterType="SysArea" resultMap="SysAreaResult">
<include refid="selectSysAreaVo"/>
<where>
<if test="name != null and name != ''"> and name like concat('%', #{name}, '%')</if>
<if test="code != null and code != ''"> and code = #{code}</if>
<if test="parentCode != null and parentCode != ''"> and parent_code = #{parentCode}</if>
<if test="type != null "> and type = #{type}</if>
<if test="sort != null "> and sort = #{sort}</if>
<if test="status != null "> and status = #{status}</if>
<if test="namePath != null and namePath != ''"> and name_path = #{namePath}</if>
<if test="codePath != null and codePath != ''"> and code_path = #{codePath}</if>
<if test="deleted != null "> and deleted = #{deleted}</if>
</where>
</select>
<select id="selectSysAreaById" parameterType="Long" resultMap="SysAreaResult">
<include refid="selectSysAreaVo"/>
where id = #{id}
</select>
<insert id="insertSysArea" parameterType="SysArea" useGeneratedKeys="true" keyProperty="id">
insert into sys_area
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null and name != ''">name,</if>
<if test="code != null and code != ''">code,</if>
<if test="parentCode != null and parentCode != ''">parent_code,</if>
<if test="type != null">type,</if>
<if test="sort != null">sort,</if>
<if test="status != null">status,</if>
<if test="namePath != null">name_path,</if>
<if test="codePath != null">code_path,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
<if test="deleted != null">deleted,</if>
<if test="remark != null">remark,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="name != null and name != ''">#{name},</if>
<if test="code != null and code != ''">#{code},</if>
<if test="parentCode != null and parentCode != ''">#{parentCode},</if>
<if test="type != null">#{type},</if>
<if test="sort != null">#{sort},</if>
<if test="status != null">#{status},</if>
<if test="namePath != null">#{namePath},</if>
<if test="codePath != null">#{codePath},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="deleted != null">#{deleted},</if>
<if test="remark != null">#{remark},</if>
</trim>
</insert>
<update id="updateSysArea" parameterType="SysArea">
update sys_area
<trim prefix="SET" suffixOverrides=",">
<if test="name != null and name != ''">name = #{name},</if>
<if test="code != null and code != ''">code = #{code},</if>
<if test="parentCode != null and parentCode != ''">parent_code = #{parentCode},</if>
<if test="type != null">type = #{type},</if>
<if test="sort != null">sort = #{sort},</if>
<if test="status != null">status = #{status},</if>
<if test="namePath != null">name_path = #{namePath},</if>
<if test="codePath != null">code_path = #{codePath},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="deleted != null">deleted = #{deleted},</if>
<if test="remark != null">remark = #{remark},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteSysAreaById" parameterType="Long">
delete from sys_area where id = #{id}
</delete>
<delete id="deleteSysAreaByIds" parameterType="String">
delete from sys_area where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

45
chenhai-system/src/main/resources/mapper/system/SysUserMapper.xml

@ -9,6 +9,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="deptId" column="dept_id" /> <result property="deptId" column="dept_id" />
<result property="userName" column="user_name" /> <result property="userName" column="user_name" />
<result property="nickName" column="nick_name" /> <result property="nickName" column="nick_name" />
<result property="userType" column="user_type" />
<result property="email" column="email" /> <result property="email" column="email" />
<result property="phonenumber" column="phonenumber" /> <result property="phonenumber" column="phonenumber" />
<result property="sex" column="sex" /> <result property="sex" column="sex" />
@ -19,12 +20,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="loginIp" column="login_ip" /> <result property="loginIp" column="login_ip" />
<result property="loginDate" column="login_date" /> <result property="loginDate" column="login_date" />
<result property="pwdUpdateDate" column="pwd_update_date" /> <result property="pwdUpdateDate" column="pwd_update_date" />
<result property="areaCode" column="area_code" />
<result property="createBy" column="create_by" /> <result property="createBy" column="create_by" />
<result property="createTime" column="create_time" /> <result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" /> <result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" /> <result property="updateTime" column="update_time" />
<result property="remark" column="remark" /> <result property="remark" column="remark" />
<association property="dept" javaType="SysDept" resultMap="deptResult" /> <association property="dept" javaType="SysDept" resultMap="deptResult" />
<association property="area" javaType="SysArea" resultMap="areaResult" />
<collection property="roles" javaType="java.util.List" resultMap="RoleResult" /> <collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
</resultMap> </resultMap>
@ -46,6 +49,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="dataScope" column="data_scope" /> <result property="dataScope" column="data_scope" />
<result property="status" column="role_status" /> <result property="status" column="role_status" />
</resultMap> </resultMap>
<resultMap id="areaResult" type="sysArea">
<id property="id" column="sys_area_id" />
<result property="name" column="sys_area_name" />
<result property="code" column="sys_area_code" />
<result property="parentCode" column="parent_code" />
<result property="type" column="type" />
<result property="sort" column="sort" />
<result property="status" column="status" />
<result property="namePath" column="name_path" />
<result property="codePath" column="code_path" />
<result property="deleted" column="deleted" />
</resultMap>
<sql id="selectUserVo"> <sql id="selectUserVo">
select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.remark, select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_by, u.create_time, u.remark,
@ -85,6 +101,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<!-- 数据范围过滤 --> <!-- 数据范围过滤 -->
${params.dataScope} ${params.dataScope}
</select> </select>
<select id="selectUserListByAreaCode" parameterType="SysUser" resultMap="SysUserResult">
select u.user_id, u.dept_id, u.nick_name, u.user_type, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date,u.area_code, u.create_by, u.create_time, u.remark, a.id as sys_area_id, a.name as sys_area_name, a.code as sys_area_code from sys_user u
left join sys_area a on u.area_code = a.code
where u.del_flag = '0'
<if test="userId != null and userId != 0">
AND u.user_id = #{userId}
</if>
<if test="userName != null and userName != ''">
AND u.user_name like concat('%', #{userName}, '%')
</if>
<if test="status != null and status != ''">
AND u.status = #{status}
</if>
<if test="phonenumber != null and phonenumber != ''">
AND u.phonenumber like concat('%', #{phonenumber}, '%')
</if>
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
AND date_format(u.create_time,'%Y%m%d') &gt;= date_format(#{params.beginTime},'%Y%m%d')
</if>
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
AND date_format(u.create_time,'%Y%m%d') &lt;= date_format(#{params.endTime},'%Y%m%d')
</if>
<if test="areaCode != null and areaCode != ''">
AND (u.area_code = #{areaCode} OR u.area_code IN ( SELECT a.code FROM sys_area a WHERE find_in_set(#{areaCode}, code_path) ))
</if>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult"> <select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">
select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time

63
chenhai-ui/src/api/system/area.js

@ -0,0 +1,63 @@
import request from '@/utils/request'
// 查询行政区划列表
export function listArea(query) {
return request({
url: '/system/area/list',
method: 'get',
params: query
})
}
// 查询行政区划详细
export function getArea(id) {
return request({
url: '/system/area/' + id,
method: 'get'
})
}
// 新增行政区划
export function addArea(data) {
return request({
url: '/system/area',
method: 'post',
data: data
})
}
// 修改行政区划
export function updateArea(data) {
return request({
url: '/system/area',
method: 'put',
data: data
})
}
// 删除行政区划
export function delArea(id) {
return request({
url: '/system/area/' + id,
method: 'delete'
})
}
// ========== 新增的接口 ==========
// 懒加载查询子节点
export function listChildren(parentCode) {
return request({
url: '/system/area/children',
method: 'get',
params: { parentCode }
})
}
// 查询所有省级节点
export function listProvinces() {
return request({
url: '/system/area/provinces',
method: 'get'
})
}

29
chenhai-ui/src/api/system/user.js

@ -134,3 +134,32 @@ export function deptTreeSelect() {
method: 'get' method: 'get'
}) })
} }
// ================ 以下是新增的行政区划相关接口 ================
// 查询行政区划子节点(懒加载)
export function getAreaChildren(parentCode) {
return request({
url: '/system/user/areaChildren',
method: 'get',
params: { parentCode }
})
}
// 查询用户列表
export function listUserByArea(query) {
return request({
url: '/system/user/listByAreaCode',
method: 'get',
params: query
})
}
// 查询行政区划树(用于表单选择)
export function areaTreeSelect(query) {
return request({
url: '/system/area/tree',
method: 'get',
params: query
})
}

1
chenhai-ui/src/assets/icons/svg/userinfo.svg

@ -0,0 +1 @@
<svg t="1768374664236" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7455" width="200" height="200"><path d="M484.751579 529.237872c4.782757 0 9.621781 0.393874 14.517074 0.393874a264.795926 264.795926 0 1 0-145.227005-43.607489 471.861163 471.861163 0 0 0-172.404317 106.627344C95.435172 675.927837 38.661035 789.869986 21.724449 914.109128a99.593878 99.593878 0 0 0 23.238572 78.774819 88.509135 88.509135 0 0 0 66.452186 30.834714h345.315044a33.760637 33.760637 0 0 0 0-67.521273H111.358939a21.156666 21.156666 0 0 1-15.58616-7.708678 32.128872 32.128872 0 0 1-7.202269-25.545549c29.934431-219.162799 198.906417-385.884076 396.181069-393.705289zM303.850835 265.173426A195.474086 195.474086 0 1 1 499.043582 460.647512 195.699156 195.699156 0 0 1 303.850835 265.173426z" fill="#6CAB86" p-id="7456"></path><path d="M991.83634 613.47066l-34.15451-34.041975a36.348952 36.348952 0 0 0-51.2599 0l-42.763473 42.538402 85.526946 85.133071 42.707205-42.482134a35.955078 35.955078 0 0 0-0.056268-51.147364zM615.292707 869.151214l85.526946 85.133072 232.385715-229.516061-85.470678-85.133072-232.441983 229.516061zM546.758615 1023.831197v0.168803l136.67431-53.848215-84.739197-84.345324-51.935113 138.024736z" fill="#6CAB86" p-id="7457"></path></svg>

426
chenhai-ui/src/views/system/area/index.vue

@ -0,0 +1,426 @@
<template>
<div class="app-container">
<!-- 懒加载树 -->
<div class="tree-wrapper">
<el-tree
ref="areaTree"
:data="treeData"
:props="treeProps"
node-key="id"
highlight-current
:expand-on-click-node="false"
:load="loadNode"
lazy
@node-click="handleNodeClick"
:render-content="renderContent"
v-loading="loading"
>
</el-tree>
</div>
<!-- 对话框 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="450px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="父节点">
<el-input v-model="form.parentName" disabled size="small" />
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" size="small" />
</el-form-item>
<el-form-item label="编码" prop="code">
<el-input v-model="form.code" placeholder="请输入编码" size="small" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择" size="small" style="width: 100%">
<el-option label="省级" :value="2" />
<el-option label="市级" :value="3" />
<el-option label="区县级" :value="4" />
<el-option label="街道/乡镇" :value="5" />
<el-option label="村/社区" :value="6" />
</el-select>
</el-form-item>
<el-form-item label="显示顺序">
<el-input-number v-model="form.sort" :min="0" size="small" style="width: 100%" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.status" size="small">
<el-radio :label="0">开启</el-radio>
<el-radio :label="1">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="2" size="small" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="dialogVisible = false" size="small">取消</el-button>
<el-button type="primary" @click="submitForm" size="small">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listArea, getArea, addArea, updateArea, listChildren, listProvinces } from "@/api/system/area"
export default {
name: "Area",
data() {
return {
loading: false,
dialogVisible: false,
dialogTitle: "",
currentNode: null,
treeData: [],
treeProps: {
label: 'name',
children: 'children',
isLeaf: 'leaf'
},
form: {
id: null,
parentCode: "",
parentName: "",
name: "",
code: "",
type: 2,
sort: 0,
status: 0,
remark: ""
},
rules: {
name: [
{ required: true, message: "名称不能为空", trigger: "blur" }
],
code: [
{ required: true, message: "编码不能为空", trigger: "blur" }
]
}
}
},
created() {
this.loadProvinces()
},
methods: {
//
async loadProvinces() {
this.loading = true
try {
const response = await listProvinces()
this.treeData = (response.data || response).map(item => ({
...item,
leaf: item.type >= 6
}))
} catch (error) {
console.error("加载失败:", error)
this.$modal.msgError("加载数据失败")
} finally {
this.loading = false
}
},
//
async loadNode(node, resolve) {
if (node.level === 0) return resolve(this.treeData)
try {
const response = await listChildren(node.data.code)
const children = (response.data || response).map(item => ({
...item,
leaf: item.type >= 6
}))
resolve(children)
} catch (error) {
console.error("加载子节点失败:", error)
resolve([])
}
},
// -
renderContent(h, { node, data }) {
return h('div', {
class: 'tree-node',
style: {
display: 'flex',
alignItems: 'center',
width: '100%',
fontSize: '12px',
padding: '4px 0'
}
}, [
// /
h('span', {
style: { flex: 1 },
on: {
click: (e) => {
e.stopPropagation()
this.handleNodeClick(data)
this.handleUpdate(data)
}
}
}, [
h('span', {
style: {
marginRight: '8px',
cursor: 'pointer',
color: '#606266',
textDecoration: 'none'
},
class: 'node-label'
}, node.label),
h('el-tag', {
props: {
size: 'mini',
type: data.status === 0 ? 'success' : 'danger'
},
style: { marginRight: '5px' }
}, data.status === 0 ? '开启' : '关闭'),
h('el-tag', {
props: {
size: 'mini',
type: this.getTypeTag(data.type)
}
}, this.getTypeName(data.type))
]),
//
h('span', {
style: {
display: 'flex',
gap: '8px',
opacity: 0.7,
transition: 'opacity 0.3s'
},
on: {
mouseenter: (e) => { e.currentTarget.style.opacity = 1 },
mouseleave: (e) => { e.currentTarget.style.opacity = 0.7 }
}
}, [
//
data.type < 6 && h('el-button', {
props: {
type: 'text',
size: 'mini',
icon: 'el-icon-plus',
title: '添加子节点'
},
style: {
color: '#409EFF',
padding: '2px'
},
on: {
click: (e) => {
e.stopPropagation()
this.handleAdd(data)
}
}
})
])
])
},
//
handleNodeClick(data) {
this.currentNode = data
this.$refs.areaTree.setCurrentKey(data.id)
},
//
handleAdd(parentNode) {
this.resetForm()
this.dialogTitle = "新增行政区划"
if (parentNode) {
this.form.parentCode = parentNode.code
this.form.parentName = parentNode.name
this.form.type = Math.min(parentNode.type + 1, 6)
} else {
//
this.form.parentCode = ""
this.form.parentName = ""
this.form.type = 2
}
this.dialogVisible = true
},
//
async handleUpdate(node) {
if (!node) node = this.currentNode
if (!node) {
this.$modal.msgWarning("请先选择一个节点")
return
}
this.resetForm()
this.dialogTitle = "修改行政区划"
this.currentNode = node
//
let parentName = ""
if (node.parentCode) {
try {
const response = await listArea({ code: node.parentCode })
const parentList = response.rows || response.data || response
if (parentList.length > 0) parentName = parentList[0].name
} catch (error) {
console.error("获取父节点失败:", error)
}
}
this.form = {
id: node.id,
parentCode: node.parentCode || "",
parentName: parentName || "无",
name: node.name,
code: node.code,
type: node.type,
sort: node.sort || 0,
status: node.status || 0,
remark: node.remark || ""
}
this.dialogVisible = true
},
//
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
const promise = this.form.id ? updateArea(this.form) : addArea(this.form)
promise.then(response => {
this.$modal.msgSuccess(this.form.id ? "修改成功" : "新增成功")
this.dialogVisible = false
this.refreshTree()
}).catch(error => {
console.error("保存失败:", error)
this.$modal.msgError("保存失败:" + (error.message || "请检查数据"))
})
}
})
},
//
resetForm() {
this.form = {
id: null,
parentCode: "",
parentName: "",
name: "",
code: "",
type: 2,
sort: 0,
status: 0,
remark: ""
}
if (this.$refs.form) {
this.$refs.form.clearValidate()
}
},
//
refreshTree() {
this.loadProvinces()
this.currentNode = null
if (this.$refs.areaTree) {
this.$refs.areaTree.setCurrentKey(null)
}
},
//
getTypeTag(type) {
const map = {
1: '', //
2: 'info', //
3: 'primary', //
4: 'warning', //
5: 'success', //
6: 'danger' //
}
return map[type] || ''
},
//
getTypeName(type) {
const map = {
1: '国家',
2: '省',
3: '市',
4: '区县',
5: '街道',
6: '村社'
}
return map[type] || '未知'
}
}
}
</script>
<style scoped>
.app-container {
padding: 15px;
background: #f5f7fa;
min-height: calc(100vh - 50px);
}
.tree-wrapper {
background: white;
border-radius: 4px;
padding: 15px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
min-height: 600px;
}
/* 紧凑样式 */
:deep(.el-tree) {
font-size: 12px !important;
}
:deep(.el-tree-node__content) {
height: 36px !important;
margin: 2px 0;
border-radius: 4px;
}
:deep(.el-tree-node__content:hover) {
background-color: #f5f7fa;
}
:deep(.el-tree-node__label) {
font-size: 12px !important;
}
:deep(.el-tree-node__expand-icon) {
font-size: 12px !important;
padding: 6px !important;
}
:deep(.el-tag) {
height: 18px !important;
line-height: 16px !important;
padding: 0 5px !important;
font-size: 10px !important;
}
:deep(.el-button--mini) {
padding: 3px 6px !important;
font-size: 10px !important;
}
/* 节点标签样式 */
.node-label {
transition: color 0.3s;
}
.node-label:hover {
color: #409EFF !important;
text-decoration: underline !important;
}
/* 当前选中节点样式 */
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
background-color: #ecf5ff !important;
border-left: 3px solid #409EFF;
}
</style>

736
chenhai-ui/src/views/system/userInfo/index.vue

@ -0,0 +1,736 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<splitpanes :horizontal="this.$store.getters.device === 'mobile'" class="default-theme">
<!--行政区划数据-->
<pane size="16">
<el-col>
<div class="head-container">
<el-input v-model="areaName" placeholder="请输入行政区划名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px" @keyup.enter.native="handleAreaSearch" />
</div>
<div class="head-container">
<el-tree
:data="areaOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
:load="loadNode"
lazy
:highlight-current="true"
node-key="code"
ref="tree"
@node-click="handleNodeClick"
class="area-tree">
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
<span v-if="node.childNodes && node.childNodes.length > 0" class="tree-expand-icon">
<i v-if="node.expanded" class="el-icon-caret-bottom"></i>
<i v-else class="el-icon-caret-right"></i>
</span>
</span>
</el-tree>
</div>
</el-col>
</pane>
<!--用户数据-->
<pane size="84">
<el-col>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable style="width: 240px" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="用户类型" prop="userType">
<el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable style="width: 240px">
<el-option
v-for="dict in dict.type.sys_user_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<!-- <el-form-item label="行政区划" prop="areaName">-->
<!-- <el-input v-model="currentAreaName" placeholder="点击左侧选择行政区划" readonly style="width: 240px" />-->
<!-- </el-form-item>-->
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px">
<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:user:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" v-hasPermi="['system:user:edit']">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:user:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['system:user:import']">导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['system:user:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns.userId.visible" />
<el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns.userName.visible" :show-overflow-tooltip="true" />
<el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns.nickName.visible" :show-overflow-tooltip="true" />
<el-table-column label="用户类型" align="center" key="userType" prop="userType" v-if="columns.userType.visible">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_user_type" :value="scope.row.userType"/>
</template>
</el-table-column>
<el-table-column label="行政区划" align="center" key="areaName" prop="area.name" v-if="columns.areaName.visible" :show-overflow-tooltip="true" />
<el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns.phonenumber.visible" width="120" />
<el-table-column label="状态" align="center" key="status" v-if="columns.status.visible">
<template slot-scope="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" v-if="columns.createTime.visible" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope" v-if="scope.row.userId !== 1">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']">删除</el-button>
<el-dropdown size="mini" @command="(command) => handleCommand(command, scope.row)" v-hasPermi="['system:user:resetPwd', 'system:user:edit']">
<el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="handleResetPwd" icon="el-icon-key" v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
<el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check" v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
</el-col>
</pane>
</splitpanes>
</el-row>
<!-- 添加或修改用户配置对话框 -->
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户类型" prop="userType">
<el-select v-model="form.userType" placeholder="请选择用户类型">
<el-option
v-for="dict in dict.type.sys_user_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="归属行政区划" prop="areaCode">
<treeselect
v-model="form.areaCode"
:options="areaSelectOptions"
:load-options="loadAreaOptions"
:auto-load-root-options="false"
placeholder="请选择归属行政区划" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择性别">
<el-option v-for="dict in dict.type.sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择岗位">
<el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId" :disabled="item.status == 1" ></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="角色">
<el-select v-model="form.roleIds" multiple placeholder="请选择角色">
<el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId" :disabled="item.status == 1"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" style="width: 100%;"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url + '?updateSupport=' + upload.updateSupport" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">下载模板</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listUserByArea, getUser, delUser, addUser, updateUser, resetUserPwd, changeUserStatus, getAreaChildren, areaTreeSelect } from "@/api/system/user"
import { getToken } from "@/utils/auth"
import Treeselect from "@riophae/vue-treeselect"
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import { Splitpanes, Pane } from "splitpanes"
import "splitpanes/dist/splitpanes.css"
export default {
name: "User",
dicts: ['sys_normal_disable', 'sys_user_sex', 'sys_user_type'],
components: { Treeselect, Splitpanes, Pane },
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
userList: null,
//
title: "",
//
areaOptions: [],
//
areaSelectOptions: [],
//
open: false,
//
areaName: undefined,
//
currentAreaName: "",
//
initPassword: undefined,
//
dateRange: [],
//
postOptions: [],
//
roleOptions: [],
//
form: {},
defaultProps: {
children: "children",
label: "name",
isLeaf: (data, node) => {
// hasChildrenhasChildrenfalse
return !data.hasChildren
}
},
//
upload: {
//
open: false,
//
title: "",
//
isUploading: false,
//
updateSupport: 0,
//
headers: { Authorization: "Bearer " + getToken() },
//
url: process.env.VUE_APP_BASE_API + "/system/user/importData"
},
//
queryParams: {
pageNum: 1,
pageSize: 10,
userName: undefined,
phonenumber: undefined,
userType: undefined,
status: undefined,
areaCode: undefined,
areaName: undefined
},
//
columns: {
userId: { label: '用户编号', visible: true },
userName: { label: '用户名称', visible: true },
nickName: { label: '用户昵称', visible: true },
userType: { label: '用户类型', visible: true },
areaName: { label: '行政区划', visible: true },
phonenumber: { label: '手机号码', visible: true },
status: { label: '状态', visible: true },
createTime: { label: '创建时间', visible: true }
},
//
rules: {
userName: [
{ required: true, message: "用户名称不能为空", trigger: "blur" },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
],
nickName: [
{ required: true, message: "用户昵称不能为空", trigger: "blur" }
],
password: [
{ required: true, message: "用户密码不能为空", trigger: "blur" },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
{ pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
],
email: [
{
type: "email",
message: "请输入正确的邮箱地址",
trigger: ["blur", "change"]
}
],
phonenumber: [
{
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
message: "请输入正确的手机号码",
trigger: "blur"
}
],
areaCode: [
{ required: true, message: "归属行政区划不能为空", trigger: "blur" }
],
userType: [
{ required: true, message: "用户类型不能为空", trigger: "blur" }
]
}
}
},
watch: {
//
areaName(val) {
this.$refs.tree.filter(val)
}
},
created() {
this.getList()
// el-tree
this.getConfigKey("sys.user.initPassword").then(response => {
this.initPassword = response.msg
})
},
methods: {
/** 查询用户列表 */
getList() {
this.loading = true
listUserByArea(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
if (response.code === 200) {
this.userList = response.rows
this.total = response.total
this.loading = false
}
})
},
/** 懒加载行政区划节点 */
loadNode(node, resolve) {
if (node.level === 0) {
//
getAreaChildren('152900').then(response => {
if (response.code === 200) {
const uniqueAreas = this.removeDuplicates(response.data, 'code')
// hasChildrenel-tree
uniqueAreas.forEach(item => {
item.hasChildren = this.shouldHaveChildren(item.code)
})
resolve(uniqueAreas)
} else {
resolve([])
}
}).catch(error => {
console.error('加载行政区划失败:', error)
resolve([])
})
} else {
//
getAreaChildren(node.data.code).then(response => {
if (response.code === 200) {
const uniqueAreas = this.removeDuplicates(response.data, 'code')
// hasChildren
uniqueAreas.forEach(item => {
item.hasChildren = this.shouldHaveChildren(item.code)
})
resolve(uniqueAreas)
} else {
resolve([])
}
}).catch(error => {
console.error('加载行政区划子节点失败:', error)
resolve([])
})
}
},
/** 去重方法 */
removeDuplicates(array, key) {
const seen = new Set()
return array.filter(item => {
const value = item[key]
if (!seen.has(value)) {
seen.add(value)
return true
}
return false
})
},
/** 判断行政区划是否有子节点 */
shouldHaveChildren(code) {
//
// 6
// true
return true
},
/** 行政区划搜索 */
handleAreaSearch() {
if (this.areaName) {
this.$refs.tree.filter(this.areaName)
}
},
//
filterNode(value, data) {
if (!value) return true
return data.name && data.name.indexOf(value) !== -1
},
//
handleNodeClick(data) {
this.queryParams.areaCode = data.code
this.currentAreaName = data.name
this.handleQuery()
},
/** 懒加载行政区划选项(用于表单中的treeselect) */
loadAreaOptions({ action, parentNode, callback }) {
if (action === LOAD_CHILDREN_OPTIONS) {
//
const parentCode = parentNode.id || '152900'
getAreaChildren(parentCode).then(response => {
if (response.code === 200) {
const uniqueAreas = this.removeDuplicates(response.data, 'code')
parentNode.children = uniqueAreas.map(item => ({
id: item.code,
label: item.name,
children: item.hasChildren ? [] : null
}))
callback()
} else {
callback()
}
}).catch(() => {
callback()
})
}
},
/** 初始化行政区划选择框 */
initAreaSelectOptions() {
//
this.areaSelectOptions = [{
id: '152900',
label: '阿拉善盟',
children: []
}]
},
//
handleStatusChange(row) {
let text = row.status === "0" ? "启用" : "停用"
this.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function() {
return changeUserStatus(row.userId, row.status)
}).then(() => {
this.$modal.msgSuccess(text + "成功")
}).catch(function() {
row.status = row.status === "0" ? "1" : "0"
})
},
//
cancel() {
this.open = false
this.reset()
},
//
reset() {
this.form = {
userId: undefined,
areaCode: undefined,
userName: undefined,
nickName: undefined,
userType: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: "0",
remark: undefined,
postIds: [],
roleIds: []
}
this.resetForm("form")
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = []
this.resetForm("queryForm")
this.queryParams.areaCode = undefined
this.currentAreaName = ""
if (this.$refs.tree) {
this.$refs.tree.setCurrentKey(null)
}
this.handleQuery()
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.userId)
this.single = selection.length != 1
this.multiple = !selection.length
},
//
handleCommand(command, row) {
switch (command) {
case "handleResetPwd":
this.handleResetPwd(row)
break
case "handleAuthRole":
this.handleAuthRole(row)
break
default:
break
}
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
getUser().then(response => {
if (response.code === 200) {
this.postOptions = response.posts
this.roleOptions = response.roles
this.open = true
this.title = "添加用户"
this.form.password = this.initPassword
//
this.initAreaSelectOptions()
}
})
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const userId = row.userId || this.ids
getUser(userId).then(response => {
if (response.code === 200) {
this.form = response.data
this.postOptions = response.posts
this.roleOptions = response.roles
this.$set(this.form, "postIds", response.postIds)
this.$set(this.form, "roleIds", response.roleIds)
this.open = true
this.title = "修改用户"
this.form.password = ""
//
this.initAreaSelectOptions()
}
})
},
/** 重置密码按钮操作 */
handleResetPwd(row) {
this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
closeOnClickModal: false,
inputPattern: /^.{5,20}$/,
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
inputValidator: (value) => {
if (/<|>|"|'|\||\\/.test(value)) {
return "不能包含非法字符:< > \" ' \\\ |"
}
},
}).then(({ value }) => {
resetUserPwd(row.userId, value).then(response => {
this.$modal.msgSuccess("修改成功,新密码是:" + value)
})
}).catch(() => {})
},
/** 分配角色操作 */
handleAuthRole: function(row) {
const userId = row.userId
this.$router.push("/system/user-auth/role/" + userId)
},
/** 提交按钮 */
submitForm: function() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.userId != undefined) {
updateUser(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addUser(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const userIds = row.userId || this.ids
this.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?').then(function() {
return delUser(userIds)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('system/user/export', {
...this.queryParams
}, `user_${new Date().getTime()}.xlsx`)
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = "用户导入"
this.upload.open = true
},
/** 下载模板操作 */
importTemplate() {
this.download('system/user/importTemplate', {
}, `user_template_${new Date().getTime()}.xlsx`)
},
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false
this.upload.isUploading = false
this.$refs.upload.clearFiles()
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true })
this.getList()
},
//
submitFileForm() {
const file = this.$refs.upload.uploadFiles
if (!file || file.length === 0 || (!file[0].name.toLowerCase().endsWith('.xls') && !file[0].name.toLowerCase().endsWith('.xlsx'))) {
this.$modal.msgError("请选择后缀为 \"xls\"或\"xlsx\"的文件。")
return
}
this.$refs.upload.submit()
}
}
}
</script>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.tree-expand-icon {
margin-left: 8px;
color: #909399;
}
.area-tree {
min-height: 400px;
}
</style>
Loading…
Cancel
Save