Files
as-partner-frontend/src/views/modules/sys/components/user-add-or-update.vue

333 lines
11 KiB
Vue
Raw Normal View History

<template>
<el-dialog
:title="!dataForm.id ? '添加用户' : '修改'"
:close-on-click-modal="false"
v-model="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataFormRef" @keyup.enter="dataFormSubmit()" label-width="100px">
<template v-if="!dataForm.id">
<el-form-item label="归属方式" prop="bindScopeType">
<el-radio-group v-model="dataForm.bindScopeType" @change="onBindScopeChange">
<el-radio value="channel_agent">渠道 / 代理商</el-radio>
<el-radio value="store">门店</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="dataForm.bindScopeType === 'channel_agent'"
label="所属组织"
prop="selectedOrgId">
<org-tree-select
v-model="dataForm.selectedOrgId"
:hide-node-types="orgTreeHideTypes"
height="320px"
@change="onOrgTreeChange" />
</el-form-item>
<el-form-item v-else label="所属门店" prop="storeOrgId">
<store-select
v-model="dataForm.storeOrgId"
width="100%"
@change="onStoreSelectChange" />
</el-form-item>
</template>
<el-form-item label="用户名" prop="userName">
<el-input v-model="dataForm.userName" placeholder="登录帐号"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password" :class="{ 'is-required': !dataForm.id }">
<el-input
v-model="dataForm.password"
type="password"
:placeholder="dataForm.id ? '不改请留空' : '密码'"
autocomplete="new-password"></el-input>
<div v-if="!dataForm.id" class="form-tip" style="margin-top: 4px;">最短8位,且须同时包含小写、大写、数字、特殊字符4种</div>
<div v-else class="form-tip" style="margin-top: 4px;">不修改密码请留空</div>
</el-form-item>
<el-form-item label="确认密码" prop="comfirmPassword" :class="{ 'is-required': !dataForm.id }">
<el-input
v-model="dataForm.comfirmPassword"
type="password"
:placeholder="dataForm.id ? '不改请留空' : '确认密码'"
autocomplete="new-password"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="dataForm.email" placeholder="选填"></el-input>
</el-form-item>
<el-form-item label="手机号" prop="mobile">
<el-input v-model="dataForm.mobile" placeholder="选填"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, ref, nextTick } from 'vue'
import { getUserInfo, saveUser, type UserInfo } from '@/api/sys'
import { isEmail, isMobile } from '@/utils/validate'
import { ElMessage, type FormInstance, type FormItemRule } from 'element-plus'
import OrgTreeSelect from '@/components/org-tree-select/index.vue'
import StoreSelect from '@/components/store-select/index.vue'
const emit = defineEmits<{
refreshDataList: []
}>()
interface OrgTreePayload {
orgId: string | number | null
channelOrgId?: string | number | null
agentOrgId?: string | number | null
}
/** 组织树仅展示渠道/代理商,不包含门店与平台根 */
const orgTreeHideTypes = ['PLATFORM', 'STORE']
const visible = ref<boolean>(false)
const dataFormRef = ref<FormInstance | null>(null)
const orgBindings = reactive<OrgTreePayload>({
orgId: null,
channelOrgId: null,
agentOrgId: null
})
interface DataForm {
id: number
bindScopeType: 'channel_agent' | 'store'
selectedOrgId: string | number | undefined
storeOrgId: string | number | undefined
userName: string
password: string
comfirmPassword: string
salt: string
email: string
mobile: string
status: number
}
const dataForm = reactive<DataForm>({
id: 0,
bindScopeType: 'channel_agent',
selectedOrgId: undefined,
storeOrgId: undefined,
userName: '',
password: '',
comfirmPassword: '',
salt: '',
email: '',
mobile: '',
status: 1
})
function onOrgTreeChange (payload: OrgTreePayload): void {
orgBindings.orgId = payload.orgId
orgBindings.channelOrgId = payload.channelOrgId
orgBindings.agentOrgId = payload.agentOrgId
nextTick(() => {
dataFormRef.value?.validateField('selectedOrgId')
})
}
function onBindScopeChange (): void {
dataForm.selectedOrgId = undefined
dataForm.storeOrgId = undefined
orgBindings.orgId = null
orgBindings.channelOrgId = null
orgBindings.agentOrgId = null
nextTick(() => {
dataFormRef.value?.clearValidate(['selectedOrgId', 'storeOrgId'])
})
}
function onStoreSelectChange (): void {
nextTick(() => {
dataFormRef.value?.validateField('storeOrgId')
})
}
function validatePasswordStrength (password: string): { valid: boolean; message?: string } {
if (!password || password.length < 8) return { valid: false, message: '密码长度不能少于8位' }
const hasLower = /[a-z]/.test(password)
const hasUpper = /[A-Z]/.test(password)
const hasDigit = /[0-9]/.test(password)
const hasSpecial = /[^A-Za-z0-9]/.test(password)
if (!hasLower) return { valid: false, message: '密码须包含小写字母' }
if (!hasUpper) return { valid: false, message: '密码须包含大写字母' }
if (!hasDigit) return { valid: false, message: '密码须包含数字' }
if (!hasSpecial) return { valid: false, message: '密码须包含特殊字符' }
return { valid: true }
}
const validatePassword: FormItemRule['validator'] = (_rule, value, callback) => {
const v = typeof value === 'string' ? value.trim() : ''
if (!dataForm.id && !v) {
callback(new Error('密码不能为空'))
} else if (!dataForm.id && v) {
const r = validatePasswordStrength(v)
if (!r.valid) return callback(new Error(r.message))
callback()
} else if (dataForm.id && v) {
const r = validatePasswordStrength(v)
if (!r.valid) return callback(new Error(r.message))
callback()
} else {
callback()
}
}
const validateComfirmPassword: FormItemRule['validator'] = (_rule, value, callback) => {
const confirm = typeof value === 'string' ? value.trim() : ''
const pwd = typeof dataForm.password === 'string' ? dataForm.password.trim() : ''
if (!dataForm.id && !confirm) {
callback(new Error('确认密码不能为空'))
} else if (!pwd && confirm) {
callback(new Error('请先填写密码'))
} else if (pwd !== confirm) {
callback(new Error('确认密码与密码输入不一致'))
} else {
callback()
}
}
const validateEmail: FormItemRule['validator'] = (_rule, value, callback) => {
if (!value || !String(value).trim()) {
return callback()
}
if (!isEmail(value as string)) {
callback(new Error('邮箱格式错误'))
} else {
callback()
}
}
const validateMobile: FormItemRule['validator'] = (_rule, value, callback) => {
if (!value || !String(value).trim()) {
return callback()
}
if (!isMobile(value as string)) {
callback(new Error('手机号格式错误'))
} else {
callback()
}
}
const validateChannelAgentOrg: FormItemRule['validator'] = (_rule, value, callback) => {
if (dataForm.id || dataForm.bindScopeType !== 'channel_agent') return callback()
if (value == null || value === '') {
return callback(new Error('请选择渠道或代理商'))
}
callback()
}
const validateStoreOrg: FormItemRule['validator'] = (_rule, value, callback) => {
if (dataForm.id || dataForm.bindScopeType !== 'store') return callback()
if (value == null || value === '') {
return callback(new Error('请选择门店'))
}
callback()
}
const dataRule: Record<string, FormItemRule[]> = {
selectedOrgId: [{ validator: validateChannelAgentOrg, trigger: 'change' }],
storeOrgId: [{ validator: validateStoreOrg, trigger: 'change' }],
userName: [{ required: true, message: '用户名不能为空', trigger: 'blur' }],
password: [{ validator: validatePassword, trigger: 'blur' }],
comfirmPassword: [{ validator: validateComfirmPassword, trigger: 'blur' }],
email: [{ validator: validateEmail, trigger: 'blur' }],
mobile: [{ validator: validateMobile, trigger: 'blur' }]
}
function init (id?: number | string | null): void {
dataForm.id = id ? Number(id) : 0
visible.value = true
nextTick(() => {
if (dataFormRef.value && dataFormRef.value.clearValidate) {
dataFormRef.value.clearValidate()
}
dataForm.bindScopeType = 'channel_agent'
dataForm.selectedOrgId = undefined
dataForm.storeOrgId = undefined
orgBindings.orgId = null
orgBindings.channelOrgId = null
orgBindings.agentOrgId = null
dataForm.userName = ''
dataForm.password = ''
dataForm.comfirmPassword = ''
dataForm.salt = ''
dataForm.email = ''
dataForm.mobile = ''
dataForm.status = 1
})
if (id !== null && id !== undefined && id !== '') {
getUserInfo(Number(id)).then((body) => {
if (!body || body.code !== 0) return
const env = body as unknown as { data?: UserInfo; user?: UserInfo }
const user = env.data ?? env.user
if (!user || typeof user !== 'object') return
dataForm.id = user.id
dataForm.userName = user.username || ''
dataForm.salt = user.salt || ''
dataForm.email = user.email || ''
dataForm.mobile = user.mobile || ''
dataForm.status = user.status != null ? Number(user.status) : 1
dataForm.password = ''
dataForm.comfirmPassword = ''
})
}
}
function dataFormSubmit (): void {
if (!dataFormRef.value) return
dataFormRef.value.validate((valid) => {
if (!valid) return
const pwdTrim = typeof dataForm.password === 'string' ? dataForm.password.trim() : ''
const payload: Record<string, any> = {
id: dataForm.id || undefined,
username: dataForm.userName,
salt: dataForm.salt,
email: dataForm.email,
mobile: dataForm.mobile,
status: dataForm.status
}
if (dataForm.id) {
if (pwdTrim) payload.password = pwdTrim
} else {
payload.password = pwdTrim
}
if (!dataForm.id) {
if (dataForm.bindScopeType === 'channel_agent') {
payload.orgId = orgBindings.orgId ?? dataForm.selectedOrgId
if (orgBindings.channelOrgId != null) {
payload.channelOrgId = orgBindings.channelOrgId
}
if (orgBindings.agentOrgId) {
payload.agentOrgId = orgBindings.agentOrgId
}
} else {
payload.orgId = dataForm.storeOrgId
}
}
saveUser(payload).then((res) => {
if (res && res.code === 0) {
ElMessage.success({
message: '操作成功',
duration: 1500,
onClose: () => {
visible.value = false
emit('refreshDataList')
}
})
} else {
ElMessage.error(res?.msg || '请求失败')
}
})
})
}
defineExpose({ init })
</script>