@@ -1,732 +0,0 @@
< template >
< view class = "container" style = "paddingBottom:1rpx;" >
< u-navbar
leftIconSize = "40rpx"
leftIconColor = "#333333"
:title = "title"
>
< / u-navbar >
< view class = "section" >
< view class = "section-title" > { { title } } < / view >
< view class = "form-view" >
< u--form labelPosition = "left" :model = "form" :rules = "rules" ref = "uForm" labelWidth = "160rpx"
: labelStyle = "{ color: '#333333', fontSize: '30rpx' }" >
< u-form-item label = "用户昵称" prop = "nickName" required >
< u--input v-model = "form.nickName" placeholder="请输入用户昵称" maxlength="30"
inputAlign = "left" :customStyle = "getInputStyle('nickName')" > < / u--input >
< / u-form-item >
< u-form-item label = "用户名称" prop = "userName" required v-if = "!form.userId" >
< u - -input v-model = "form.userName" placeholder="请输入用户名称" maxlength="30"
inputAlign = "left" :customStyle = "getInputStyle('userName')" > < / u--input >
< / u-form-item >
< u-form-item label = "用户密码" prop = "password" required v-if = "!form.userId" >
< u - -input v-model = "form.password" type="password" placeholder="请输入用户密码" maxlength="20"
inputAlign = "left" :customStyle = "getInputStyle('password')" > < / u--input >
< / u-form-item >
< u-form-item label = "手机号码" prop = "phonenumber" required >
< u--input v-model = "form.phonenumber" placeholder="请输入手机号码" maxlength="11"
inputAlign = "left" :customStyle = "getInputStyle('phonenumber')" > < / u--input >
< / u-form-item >
< u-form-item label = "邮箱" prop = "email" >
< u--input v-model = "form.email" placeholder="请输入邮箱" maxlength="50"
inputAlign = "left" :customStyle = "getInputStyle('email')" > < / u--input >
< / u-form-item >
< u-form-item label = "用户性别" prop = "sexName" @click ="showSexPicker = true" class = "with-arrow" >
< u--input v-model = "sexName" disabled disabledColor="#ffffff" placeholder="请选择性别"
inputAlign = "left" :customStyle = "inputBaseStyle" > < / u--input >
< / u-form-item >
< u-form-item label = "归属部门" prop = "deptName" @click ="showDeptPicker = true" class = "with-arrow" >
< u--input v-model = "deptName" disabled disabledColor="#ffffff" placeholder="请选择归属部门"
inputAlign = "left" :customStyle = "inputBaseStyle" > < / u--input >
< / u-form-item >
< u-form-item label = "岗位" prop = "postNames" @click ="openPostPicker" class = "with-arrow" >
< u--input :value = "postNamesText" disabled disabledColor = "#ffffff" placeholder = "请选择岗位"
inputAlign = "left" :customStyle = "inputBaseStyle" > < / u--input >
< / u-form-item >
<!-- 已选岗位标签展示 -- >
< view v-if = "postNames.length > 0" class="selected-tags" >
< view class = "selected-title" > 已选择的岗位 : < / view >
< view class = "tag-list" >
< view v-for = "name in postNames" :key="name" class="tag-item" >
{{ name }}
< / view >
< / view >
< / view >
< u -form -item label = "角色" prop = "roleNames" @click ="openRolePicker" class = "with-arrow" >
< u--input :value = "roleNamesText" disabled disabledColor = "#ffffff" placeholder = "请选择角色"
inputAlign = "left" :customStyle = "inputBaseStyle" > < / u--input >
< / u-form-item >
<!-- 已选角色标签展示 -- >
< view v-if = "roleNames.length > 0" class="selected-tags" >
< view class = "selected-title" > 已选择的角色 : < / view >
< view class = "tag-list" >
< view v-for = "name in roleNames" :key="name" class="tag-item" >
{{ name }}
< / view >
< / view >
< / view >
< u -form -item label = "状态" prop = "statusName" @click ="showStatusPicker = true" class = "with-arrow" >
< u--input v-model = "statusName" disabled disabledColor="#ffffff" placeholder="请选择状态"
inputAlign = "left" :customStyle = "inputBaseStyle" > < / u--input >
< / u-form-item >
< u-form-item label = "备注" prop = "remark" labelPosition = "top" >
< u--input v-model = "form.remark" placeholder="请输入备注" maxlength="300"
inputAlign = "left" :customStyle = "getInputStyle('remark')" > < / u--input >
< / u-form-item >
< / u--form >
< view class = "form-btn" >
< u-button type = "primary" text = "提交" @click ="submit" > < / u -button >
< / view >
< / view >
< / view >
< u-picker itemHeight = "88" :show = "showSexPicker" :columns = "sexList" keyName = "dictLabel" @cancel ="showSexPicker = false"
@confirm ="handleSexConfirm" > < / u -picker >
< u-picker itemHeight = "88" :show = "showStatusPicker" :columns = "statusList" keyName = "dictLabel" @cancel ="showStatusPicker = false"
@confirm ="handleStatusConfirm" > < / u -picker >
< u-picker itemHeight = "88" :show = "showDeptPicker" :columns = "deptList" keyName = "deptName" @cancel ="showDeptPicker = false"
@confirm ="handleDeptConfirm" > < / u -picker >
<!-- 岗位多选弹窗 -- >
< u-popup :show = "showPostPicker" mode = "bottom" :round = "20" @close ="showPostPicker = false" >
< view class = "select-popup" >
< view class = "popup-header" >
< text class = "header-title" > 选择岗位 < / text >
< text class = "header-count" > 已选择 { { tempPostIds . length } } 项 < / text >
< / view >
< scroll-view class = "popup-content" scroll -y >
< u-checkbox-group v-model = "tempPostIds" >
< view v-for = "item in postList[0]" :key="item.postId" class="checkbox-item" >
< u -checkbox :name = "item.postId" :label = "item.postName" shape = "square"
activeColor = "#667eea" labelSize = "28rpx" > < / u-checkbox >
< / view >
< / u-checkbox-group >
< / scroll-view >
< view class = "popup-footer" >
< view class = "footer-btn cancel-btn" @click ="cancelPostSelect" > 取消 < / view >
< view class = "footer-btn confirm-btn" @click ="confirmPostSelect" > 确定 < / view >
< / view >
< / view >
< / u -popup >
<!-- 角色多选弹窗 -- >
< u-popup :show = "showRolePicker" mode = "bottom" :round = "20" @close ="showRolePicker = false" >
< view class = "select-popup" >
< view class = "popup-header" >
< text class = "header-title" > 选择角色 < / text >
< text class = "header-count" > 已选择 { { tempRoleIds . length } } 项 < / text >
< / view >
< scroll-view class = "popup-content" scroll -y >
< u-checkbox-group v-model = "tempRoleIds" >
< view v-for = "item in roleList[0]" :key="item.roleId" class="checkbox-item" >
< u -checkbox :name = "item.roleId" :label = "item.roleName" shape = "square"
activeColor = "#667eea" labelSize = "28rpx" > < / u-checkbox >
< / view >
< / u-checkbox-group >
< / scroll-view >
< view class = "popup-footer" >
< view class = "footer-btn cancel-btn" @click ="cancelRoleSelect" > 取消 < / view >
< view class = "footer-btn confirm-btn" @click ="confirmRoleSelect" > 确定 < / view >
< / view >
< / view >
< / u -popup >
< / view >
< / template >
< script setup >
import { ref , reactive , getCurrentInstance , computed } from 'vue'
import { getUser , addUser , updateUser } from '@/api/system/user'
import { listDept } from '@/api/system/dept'
import { listPost } from '@/api/system/post'
import { listRole } from '@/api/system/role'
import { getDicts } from "@/api/system/dict/data"
import { onLoad } from "@dcloudio/uni-app"
const { proxy } = getCurrentInstance ( )
const statusList = ref ( [ ] )
const sexList = ref ( [ ] )
const deptList = ref ( [ ] )
const postList = ref ( [ ] )
const roleList = ref ( [ ] )
const title = ref ( '添加用户' )
const showStatusPicker = ref ( false )
const showSexPicker = ref ( false )
const showDeptPicker = ref ( false )
const showPostPicker = ref ( false )
const showRolePicker = ref ( false )
const statusName = ref ( '' )
const sexName = ref ( '' )
const deptName = ref ( '' )
const postNames = ref ( [ ] )
const roleNames = ref ( [ ] )
const tempPostIds = ref ( [ ] )
const tempRoleIds = ref ( [ ] )
const form = reactive ( {
userId : undefined ,
deptId : undefined ,
nickName : undefined ,
userName : undefined ,
password : undefined ,
phonenumber : undefined ,
email : undefined ,
sex : '0' ,
status : '0' ,
postIds : [ ] ,
roleIds : [ ] ,
remark : undefined
} )
const postNamesText = computed ( ( ) => {
return postNames . value . join ( '、' ) || ''
} )
const roleNamesText = computed ( ( ) => {
return roleNames . value . join ( '、' ) || ''
} )
// 表单验证错误字段
const errorFields = ref ( [ ] )
// 输入框基础样式
const inputBaseStyle = {
background : '#ffffff' ,
border : '2rpx solid #dcdfe6' ,
borderRadius : '8rpx' ,
padding : '0 24rpx' ,
height : '68rpx' ,
width : '100%' ,
boxSizing : 'border-box'
}
// 输入框错误样式
const inputErrorStyle = {
background : '#fef0f0' ,
border : '2rpx solid #f56c6c' ,
borderRadius : '8rpx' ,
padding : '0 24rpx' ,
height : '68rpx' ,
width : '100%' ,
boxSizing : 'border-box'
}
// 根据字段名获取输入框样式
const getInputStyle = ( field ) => {
return errorFields . value . includes ( field ) ? inputErrorStyle : inputBaseStyle
}
const rules = {
nickName : [
{ required : true , message : '用户昵称不能为空' , trigger : [ 'blur' , 'change' ] }
] ,
userName : [
{ required : true , message : '用户名称不能为空' , trigger : [ 'blur' , 'change' ] }
] ,
password : [
{ required : true , message : '用户密码不能为空' , trigger : [ 'blur' , 'change' ] }
] ,
phonenumber : [
{ required : true , message : '手机号码不能为空' , trigger : [ 'blur' , 'change' ] } ,
{ pattern : /^1[3-9]\d{9}$/ , message : '请输入正确的手机号码' , trigger : [ 'blur' , 'change' ] }
] ,
email : [
{ type : 'email' , message : '请输入正确的邮箱地址' , trigger : [ 'blur' , 'change' ] }
]
}
onLoad ( ( option ) => {
getDicts ( 'sys_normal_disable' ) . then ( res => {
statusList . value = [ res . data ]
} )
getDicts ( 'sys_user_sex' ) . then ( res => {
sexList . value = [ res . data ]
} )
// 加载部门列表
loadDeptList ( )
if ( option . id ) {
title . value = '修改用户'
getDetail ( option . id )
} else {
title . value = '添加用户'
// 新增时直接获取完整的岗位和角色列表
loadPostList ( )
loadRoleList ( )
setTimeout ( ( ) => {
updateStatusName ( )
updateSexName ( )
} , 100 )
}
} )
// 加载部门列表
function loadDeptList ( ) {
listDept ( ) . then ( res => {
const listData = res . data || [ ]
deptList . value = [ listData ]
} )
}
// 加载岗位列表
function loadPostList ( ) {
listPost ( ) . then ( res => {
console . log ( '单独获取岗位列表:' , res )
const listData = res . rows || [ ]
postList . value = [ listData ]
} ) . catch ( err => {
console . error ( '获取岗位列表失败:' , err )
} )
}
// 加载角色列表
function loadRoleList ( ) {
listRole ( ) . then ( res => {
console . log ( '单独获取角色列表:' , res )
const listData = res . rows || [ ]
roleList . value = [ listData ]
} ) . catch ( err => {
console . error ( '获取角色列表失败:' , err )
} )
}
// 获取详情
function getDetail ( id ) {
getUser ( id ) . then ( res => {
console . log ( '编辑用户-获取详情数据:' , res )
const data = res . data || { }
console . log ( '用户信息:' , data . user )
console . log ( '已选岗位IDs:' , data . postIds )
console . log ( '已选角色IDs:' , data . roleIds )
// 用户数据在 data.user 中
if ( data . user ) {
Object . assign ( form , data . user )
}
// 单独获取完整的岗位和角色列表
loadPostList ( )
loadRoleList ( )
// 设置已选岗位和角色(从 data 根级别获取)
form . postIds = data . postIds || [ ]
form . roleIds = data . roleIds || [ ]
console . log ( 'form.postIds:' , form . postIds )
console . log ( 'form.roleIds:' , form . roleIds )
updateStatusName ( )
updateSexName ( )
updateDeptName ( )
// 延迟更新岗位和角色名称,等待列表加载完成
setTimeout ( ( ) => {
updatePostNames ( )
updateRoleNames ( )
} , 300 )
} ) . catch ( err => {
console . error ( '获取用户详情失败:' , err )
} )
}
// 更新状态显示名称
function updateStatusName ( ) {
if ( statusList . value [ 0 ] ) {
const item = statusList . value [ 0 ] . find ( v => v . dictValue === form . status )
statusName . value = item ? item . dictLabel : ''
}
}
// 更新性别显示名称
function updateSexName ( ) {
if ( sexList . value [ 0 ] ) {
const item = sexList . value [ 0 ] . find ( v => v . dictValue === form . sex )
sexName . value = item ? item . dictLabel : ''
}
}
// 状态选择确认
function handleStatusConfirm ( e ) {
form . status = e . value [ 0 ] . dictValue
statusName . value = e . value [ 0 ] . dictLabel
showStatusPicker . value = false
}
// 性别选择确认
function handleSexConfirm ( e ) {
form . sex = e . value [ 0 ] . dictValue
sexName . value = e . value [ 0 ] . dictLabel
showSexPicker . value = false
}
// 更新部门显示名称
function updateDeptName ( ) {
if ( form . deptId && deptList . value [ 0 ] ) {
const dept = deptList . value [ 0 ] . find ( v => v . deptId === form . deptId )
deptName . value = dept ? dept . deptName : ''
}
}
// 更新岗位显示名称
function updatePostNames ( ) {
if ( form . postIds && form . postIds . length > 0 && postList . value [ 0 ] ) {
postNames . value = postList . value [ 0 ]
. filter ( v => form . postIds . includes ( v . postId ) )
. map ( v => v . postName )
}
}
// 更新角色显示名称
function updateRoleNames ( ) {
if ( form . roleIds && form . roleIds . length > 0 && roleList . value [ 0 ] ) {
roleNames . value = roleList . value [ 0 ]
. filter ( v => form . roleIds . includes ( v . roleId ) )
. map ( v => v . roleName )
}
}
// 部门选择确认
function handleDeptConfirm ( e ) {
form . deptId = e . value [ 0 ] . deptId
deptName . value = e . value [ 0 ] . deptName
showDeptPicker . value = false
}
// 岗位选择确认
function openPostPicker ( ) {
tempPostIds . value = [ ... form . postIds ]
showPostPicker . value = true
}
function cancelPostSelect ( ) {
showPostPicker . value = false
}
function confirmPostSelect ( ) {
form . postIds = [ ... tempPostIds . value ]
// 更新显示名称
if ( postList . value [ 0 ] ) {
postNames . value = postList . value [ 0 ]
. filter ( v => form . postIds . includes ( v . postId ) )
. map ( v => v . postName )
}
showPostPicker . value = false
}
// 角色选择确认
function openRolePicker ( ) {
tempRoleIds . value = [ ... form . roleIds ]
showRolePicker . value = true
}
function cancelRoleSelect ( ) {
showRolePicker . value = false
}
function confirmRoleSelect ( ) {
form . roleIds = [ ... tempRoleIds . value ]
// 更新显示名称
if ( roleList . value [ 0 ] ) {
roleNames . value = roleList . value [ 0 ]
. filter ( v => form . roleIds . includes ( v . roleId ) )
. map ( v => v . roleName )
}
showRolePicker . value = false
}
// 提交
function submit ( ) {
errorFields . value = [ ] // 清空错误字段
proxy . $refs [ 'uForm' ] . validate ( ) . then ( ( ) => {
if ( form . userId ) {
updateUser ( form ) . then ( ( ) => {
proxy . $modal . msgSuccess ( '修改成功' )
setTimeout ( ( ) => {
uni . navigateTo ( { url : ` /pages_mine/pages/system/user/list ` } )
} , 1500 )
} )
} else {
addUser ( form ) . then ( ( ) => {
proxy . $modal . msgSuccess ( '新增成功' )
setTimeout ( ( ) => {
uni . navigateTo ( { url : ` /pages_mine/pages/system/user/list ` } )
} , 1500 )
} )
}
} ) . catch ( ( errors ) => {
// 记录验证失败的字段
if ( errors && Array . isArray ( errors ) ) {
errorFields . value = errors . map ( err => err . field || err . prop )
}
proxy . $modal . msgError ( '请填写完整信息' )
} )
}
< / script >
< style lang = "scss" scoped >
. container {
background - color : # f5f5f5 ;
}
. section {
margin : 24 rpx ;
padding : 0 ;
background - color : # fff ;
border - radius : 16 rpx ;
box - shadow : 0 2 rpx 12 rpx rgba ( 0 , 0 , 0 , 0.08 ) ;
overflow : hidden ;
. section - title {
padding : 16 rpx 24 rpx ;
background : linear - gradient ( 135 deg , # 667 eea 0 % , # 764 ba2 100 % ) ;
color : # ffffff ;
line - height : 1.2 ;
font - size : 28 rpx ;
font - weight : 600 ;
display : flex ;
align - items : center ;
& : : before {
content : '' ;
width : 6 rpx ;
height : 28 rpx ;
background : # ffffff ;
border - radius : 3 rpx ;
margin - right : 12 rpx ;
}
}
. form - view {
padding : 24 rpx ;
. remark - textarea {
width : 100 % ;
min - height : 160 rpx ;
background : # ffffff ;
border : 2 rpx solid # dcdfe6 ;
border - radius : 8 rpx ;
padding : 20 rpx ;
box - sizing : border - box ;
font - size : 28 rpx ;
line - height : 1.5 ;
}
. remark - count {
text - align : right ;
font - size : 24 rpx ;
color : # 909399 ;
margin - top : 8 rpx ;
}
. form - btn {
padding - top : 16 rpx ;
}
. selected - tags {
margin - top : 16 rpx ;
padding : 20 rpx ;
background : # f8f9fa ;
border - radius : 12 rpx ;
. selected - title {
font - size : 26 rpx ;
color : # 666 ;
margin - bottom : 12 rpx ;
}
. tag - list {
display : flex ;
flex - wrap : wrap ;
gap : 12 rpx ;
. tag - item {
padding : 8 rpx 16 rpx ;
background : linear - gradient ( 135 deg , rgba ( 102 , 126 , 234 , 0.1 ) 0 % , rgba ( 118 , 75 , 162 , 0.1 ) 100 % ) ;
border : 1 rpx solid # 667 eea ;
border - radius : 24 rpx ;
font - size : 24 rpx ;
color : # 667 eea ;
}
}
}
}
}
. select - popup {
background : # fff ;
max - height : 85 vh ;
display : flex ;
flex - direction : column ;
border - radius : 24 rpx 24 rpx 0 0 ;
. popup - header {
padding : 40 rpx 32 rpx 32 rpx ;
background : linear - gradient ( 135 deg , rgba ( 102 , 126 , 234 , 0.05 ) 0 % , rgba ( 118 , 75 , 162 , 0.05 ) 100 % ) ;
border - bottom : 2 rpx solid # f5f5f5 ;
position : relative ;
& : : after {
content : '' ;
position : absolute ;
top : 16 rpx ;
left : 50 % ;
transform : translateX ( - 50 % ) ;
width : 80 rpx ;
height : 6 rpx ;
background : # e0e0e0 ;
border - radius : 3 rpx ;
}
. header - title {
font - size : 34 rpx ;
font - weight : 600 ;
color : # 333 ;
margin - bottom : 12 rpx ;
display : block ;
letter - spacing : 1 rpx ;
}
. header - count {
font - size : 26 rpx ;
color : # 667 eea ;
display : block ;
font - weight : 500 ;
}
}
. popup - content {
flex : 1 ;
max - height : 55 vh ;
padding : 16 rpx 32 rpx 32 rpx ;
overflow - y : auto ;
. checkbox - item {
padding : 24 rpx 20 rpx ;
margin - bottom : 8 rpx ;
background : # fafafa ;
border - radius : 12 rpx ;
border : 2 rpx solid transparent ;
transition : all 0.3 s ease ;
& : hover {
background : rgba ( 102 , 126 , 234 , 0.05 ) ;
border - color : # 667 eea ;
}
& : last - child {
margin - bottom : 0 ;
}
}
}
. popup - footer {
padding : 24 rpx 32 rpx 32 rpx ;
background : # fafafa ;
display : flex ;
gap : 24 rpx ;
box - shadow : 0 - 4 rpx 12 rpx rgba ( 0 , 0 , 0 , 0.05 ) ;
. footer - btn {
flex : 1 ;
height : 92 rpx ;
display : flex ;
align - items : center ;
justify - content : center ;
border - radius : 20 rpx ;
font - size : 32 rpx ;
font - weight : 500 ;
transition : all 0.3 s ;
letter - spacing : 2 rpx ;
}
. cancel - btn {
background : # ffffff ;
color : # 666 ;
border : 2 rpx solid # e0e0e0 ;
& : active {
background : # f5f5f5 ;
border - color : # d0d0d0 ;
}
}
. confirm - btn {
background : linear - gradient ( 135 deg , # 667 eea 0 % , # 764 ba2 100 % ) ;
color : # ffffff ;
box - shadow : 0 6 rpx 20 rpx rgba ( 102 , 126 , 234 , 0.4 ) ;
border : none ;
& : active {
transform : translateY ( 2 rpx ) ;
box - shadow : 0 4 rpx 16 rpx rgba ( 102 , 126 , 234 , 0.3 ) ;
}
}
}
}
< / style >
< style lang = "scss" >
. form - btn . u - button {
background : linear - gradient ( 135 deg , # 667 eea 0 % , # 764 ba2 100 % ) ! important ;
border : none ! important ;
border - radius : 24 rpx ! important ;
height : 80 rpx ! important ;
box - shadow : 0 4 rpx 16 rpx rgba ( 102 , 126 , 234 , 0.4 ) ! important ;
}
. form - btn . u - button _ _text {
font - size : 30 rpx ! important ;
font - weight : 500 ! important ;
letter - spacing : 2 rpx ! important ;
}
/* 统一输入框宽度 */
. u - form - item _ _body {
flex : 1 ! important ;
}
. u - form - item _ _body . u -- input {
width : 100 % ! important ;
box - sizing : border - box ! important ;
}
// 备注框样式
. remark - textarea {
width : 100 % ! important ;
min - height : 160 rpx ! important ;
background : # ffffff ! important ;
border : 2 rpx solid # dcdfe6 ! important ;
border - radius : 8 rpx ! important ;
padding : 20 rpx ! important ;
box - sizing : border - box ! important ;
font - size : 28 rpx ! important ;
line - height : 1.5 ! important ;
}
/* 强制让错误状态的输入框边框变红 */
. u - form - item -- error . u -- input ,
. u - form - item -- error . u - input ,
. u - form - item -- error . u - input _ _content ,
. u - form - item -- error . u - input _ _content _ _field - wrapper {
border : 2 rpx solid # f56c6c ! important ;
background : # fef0f0 ! important ;
}
. u - form - item -- error . u -- textarea ,
. u - form - item -- error . u - textarea {
border : 2 rpx solid # f56c6c ! important ;
background : # fef0f0 ! important ;
}
< / style >