数据权限
parent
435c2f7d2b
commit
66a7790dc7
|
|
@ -73,5 +73,6 @@ export const exportTenant = (params: TenantExportReqVO) => {
|
|||
}
|
||||
|
||||
export const getSimpleTenantList = () => {
|
||||
return request.get({ url: '/system/tenant/simple-list' })
|
||||
return request.get({ url: '/system/tenant/simple-total-list' })
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
<el-col :span="24" class="px-10px">
|
||||
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantId">
|
||||
<el-select
|
||||
ref="tenantSelectRef"
|
||||
v-model="loginData.loginForm.tenantId"
|
||||
:placeholder="t('login.tenantNamePlaceholder')"
|
||||
:prefix-icon="iconHouse"
|
||||
|
|
@ -126,6 +127,7 @@ const loginLoading = ref(false)
|
|||
const verify = ref()
|
||||
const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字 pictureWord 文字验证码
|
||||
const tenantList = ref<LoginApi.TenantInfo[]>([])
|
||||
const tenantSelectRef = ref()
|
||||
const isFetchingTenants = ref(false)
|
||||
|
||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
|
||||
|
|
@ -197,9 +199,12 @@ const fetchTenantList = async () => {
|
|||
|
||||
tenantList.value = res.tenants
|
||||
|
||||
// 默认选中第一个租户
|
||||
if (res.tenants.length > 0) {
|
||||
// 只有一个租户时,自动选中
|
||||
if (res.tenants.length === 1) {
|
||||
loginData.loginForm.tenantId = res.tenants[0].tenantId
|
||||
} else {
|
||||
// 多个租户时,不自动选中,让用户手动选择
|
||||
loginData.loginForm.tenantId = undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取租户列表失败:', error)
|
||||
|
|
@ -234,12 +239,13 @@ const getCode = async () => {
|
|||
if (tenantList.value.length === 1) {
|
||||
loginData.loginForm.tenantId = tenantList.value[0].tenantId
|
||||
// 单租户直接继续登录
|
||||
continueLogin()
|
||||
} else {
|
||||
// 如果有多个租户但未选择,提示用户选择
|
||||
if (!loginData.loginForm.tenantId) {
|
||||
message.error('请选择租户')
|
||||
return
|
||||
// 多个租户,自动展开下拉框让用户选择
|
||||
if (tenantSelectRef.value) {
|
||||
tenantSelectRef.value.visible = true
|
||||
}
|
||||
message.info('请选择要登录的租户')
|
||||
}
|
||||
} else if (loginData.tenantEnable === 'true' && tenantList.value.length > 0) {
|
||||
// 已有租户列表,验证是否已选择
|
||||
|
|
@ -247,17 +253,24 @@ const getCode = async () => {
|
|||
message.error('请选择租户')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 继续登录流程
|
||||
continueLogin()
|
||||
}
|
||||
}
|
||||
|
||||
// 继续登录流程(验证码或登录)
|
||||
const continueLogin = () => {
|
||||
// 情况一,未开启:则直接登录
|
||||
if (loginData.captchaEnable === 'false') {
|
||||
await handleLogin({})
|
||||
handleLogin({})
|
||||
} else {
|
||||
// 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录
|
||||
// 弹出验证码
|
||||
verify.value.show()
|
||||
}
|
||||
}
|
||||
|
||||
// 设置租户ID
|
||||
const setTenantId = () => {
|
||||
if (loginData.loginForm.tenantId) {
|
||||
|
|
|
|||
|
|
@ -36,17 +36,6 @@
|
|||
<el-input v-model="formData.propertyCompany" placeholder="请输入物业名称" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 用户名和密码:仅新增时显示 -->
|
||||
<div v-if="formType === 'create'" class="inline-row">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="formData.username" placeholder="请输入用户名(4-30位数字和字母)" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="formData.password" type="password" placeholder="请输入密码(4-16位)" show-password />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="inline-row">
|
||||
<el-form-item label="来访审核" prop="visitAudit">
|
||||
<el-radio-group v-model="formData.visitAudit">
|
||||
|
|
@ -106,8 +95,6 @@ const formData = ref({
|
|||
longitude: undefined,
|
||||
latitude: undefined,
|
||||
propertyCompany: undefined,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
visitAudit: undefined,
|
||||
gateAudit: undefined
|
||||
})
|
||||
|
|
@ -117,14 +104,6 @@ const formRules = reactive({
|
|||
streetName: [{ required: true, message: '街道名称不能为空', trigger: 'blur' }],
|
||||
districtName: [{ required: true, message: '社区名称不能为空', trigger: 'blur' }],
|
||||
communityAddress: [{ required: true, message: '小区地址不能为空', trigger: 'blur' }],
|
||||
username: [
|
||||
{ required: true, message: '用户名不能为空', trigger: 'blur' },
|
||||
{ pattern: /^[a-zA-Z0-9]{4,30}$/, message: '用户名由 4-30 位数字和字母组成', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '密码不能为空', trigger: 'blur' },
|
||||
{ min: 4, max: 16, message: '密码长度为 4-16 位', trigger: 'blur' }
|
||||
],
|
||||
visitAudit: [{ required: true, message: '来访审核不能为空', trigger: 'change' }],
|
||||
gateAudit: [{ required: true, message: '道闸申请不能为空', trigger: 'change' }]
|
||||
})
|
||||
|
|
@ -186,8 +165,6 @@ const resetForm = () => {
|
|||
longitude: undefined,
|
||||
latitude: undefined,
|
||||
propertyCompany: undefined,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
visitAudit: undefined,
|
||||
gateAudit: undefined
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-form-item label="手机号码" prop="mobile" required>
|
||||
<el-input v-model="formData.mobile" maxlength="11" placeholder="请输入手机号码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
@ -139,6 +139,7 @@ const formRules = reactive<FormRules>({
|
|||
}
|
||||
],
|
||||
mobile: [
|
||||
{ required: true, message: '手机号码不能为空', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-form-item label="手机号" prop="mobile" required>
|
||||
<el-input v-model="formData.mobile" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
|
|
@ -96,7 +96,14 @@ const formData = ref({
|
|||
groupId: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
mobile: [{ required: true, message: '手机号不能为空', trigger: 'blur' }],
|
||||
mobile: [
|
||||
{ required: true, message: '手机号不能为空', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3-9]\d{9}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
|
|
|||
|
|
@ -150,9 +150,9 @@
|
|||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="240" fixed="right">
|
||||
<el-table-column label="操作" align="center" width="320" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="action-buttons">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
|
|
@ -368,6 +368,15 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
:deep(.user-table) {
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,31 +22,30 @@
|
|||
<el-checkbox-group v-model="checkedTenantIds" class="tenant-list">
|
||||
<el-checkbox
|
||||
v-for="tenant in filteredAvailableTenants"
|
||||
:key="tenant.tenantId"
|
||||
:label="tenant.tenantId"
|
||||
:value="tenant.tenantId"
|
||||
:key="tenant.id"
|
||||
:label="tenant.id"
|
||||
:value="tenant.id"
|
||||
>
|
||||
{{ tenant.tenantName }}
|
||||
{{ tenant.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
|
||||
<div class="middle-panel">
|
||||
<el-button type="primary" :icon="Right" @click="handleAssign" :disabled="checkedTenantIds.length === 0" />
|
||||
<el-button type="info" :icon="Right" @click="handleRemove" :disabled="selectedAssignedIds.length === 0" />
|
||||
<el-button type="info" :icon="Back" @click="handleRemove" :disabled="selectedAssignedIds.length === 0" />
|
||||
</div>
|
||||
|
||||
<div class="right-panel">
|
||||
<div class="panel-header">已分配的小区</div>
|
||||
<el-checkbox-group v-model="selectedAssignedIds" class="tenant-list">
|
||||
<el-checkbox
|
||||
v-for="rel in assignedTenants"
|
||||
:key="rel.tenantId"
|
||||
:label="rel.tenantId"
|
||||
:value="rel.tenantId"
|
||||
v-for="tenant in assignedTenantList"
|
||||
:key="tenant.id"
|
||||
:label="tenant.id"
|
||||
:value="tenant.id"
|
||||
>
|
||||
{{ rel.tenantName }}
|
||||
<el-tag v-if="rel.isDefault" size="small" type="success" class="ml-5px">默认</el-tag>
|
||||
{{ tenant.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
|
|
@ -60,40 +59,52 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Right } from '@element-plus/icons-vue'
|
||||
import { Right, Back } from '@element-plus/icons-vue'
|
||||
import * as UserTenantApi from '@/api/system/userTenant'
|
||||
import * as TenantApi from '@/api/system/tenant'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import type { UserVO } from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'UserTenantAssignForm' })
|
||||
|
||||
const message = useMessage()
|
||||
const emit = defineEmits(['success'])
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
const currentUserInfo = ref<UserVO>({} as UserVO)
|
||||
|
||||
const allTenants = ref<any[]>([])
|
||||
const assignedTenants = ref<any[]>([])
|
||||
const assignedTenantList = ref<any[]>([])
|
||||
const checkedTenantIds = ref<number[]>([])
|
||||
const selectedAssignedIds = ref<number[]>([])
|
||||
const availableTenantSearch = ref('')
|
||||
|
||||
const filteredAvailableTenants = computed(() => {
|
||||
if (!availableTenantSearch.value) {
|
||||
return allTenants.value
|
||||
}
|
||||
return allTenants.value.filter(
|
||||
(t) => t.tenantName?.toLowerCase().includes(availableTenantSearch.value.toLowerCase())
|
||||
const assignedIds = new Set(assignedTenantList.value.map(t => t.id))
|
||||
let available = allTenants.value.filter(t => !assignedIds.has(t.id))
|
||||
|
||||
if (availableTenantSearch.value) {
|
||||
available = available.filter(
|
||||
(t) => t.name?.toLowerCase().includes(availableTenantSearch.value.toLowerCase())
|
||||
)
|
||||
}
|
||||
return available
|
||||
})
|
||||
|
||||
const open = (user: UserVO) => {
|
||||
const open = async (user: UserVO) => {
|
||||
visible.value = true
|
||||
currentUserInfo.value = user
|
||||
checkedTenantIds.value = []
|
||||
selectedAssignedIds.value = []
|
||||
loadTenants()
|
||||
loadAssignedTenants(user.id)
|
||||
|
||||
await loadTenants()
|
||||
|
||||
if (user.id) {
|
||||
const userData = await UserApi.getUser(user.id)
|
||||
currentUserInfo.value = userData
|
||||
|
||||
const assignedIds = userData.tenantList || []
|
||||
assignedTenantList.value = allTenants.value.filter(t => assignedIds.includes(t.id))
|
||||
}
|
||||
}
|
||||
|
||||
const loadTenants = async () => {
|
||||
|
|
@ -102,58 +113,49 @@ const loadTenants = async () => {
|
|||
allTenants.value = data || []
|
||||
} catch (error) {
|
||||
console.error('加载小区列表失败', error)
|
||||
message.error('加载小区列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
const loadAssignedTenants = async (userId: number) => {
|
||||
try {
|
||||
const data = await UserTenantApi.getUserTenants(userId)
|
||||
assignedTenants.value = data || []
|
||||
} catch (error) {
|
||||
console.error('加载已分配小区失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAssign = async () => {
|
||||
const handleAssign = () => {
|
||||
if (checkedTenantIds.value.length === 0) {
|
||||
message.warning('请选择要分配的小区')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await UserTenantApi.batchAssignUserToTenants({
|
||||
userId: currentUserInfo.value.id,
|
||||
tenantIds: checkedTenantIds.value
|
||||
})
|
||||
message.success('分配成功')
|
||||
const selectedTenants = allTenants.value.filter(t => checkedTenantIds.value.includes(t.id))
|
||||
assignedTenantList.value.push(...selectedTenants)
|
||||
checkedTenantIds.value = []
|
||||
loadAssignedTenants(currentUserInfo.value.id)
|
||||
} catch (error) {
|
||||
console.error('分配失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemove = async () => {
|
||||
const handleRemove = () => {
|
||||
if (selectedAssignedIds.value.length === 0) {
|
||||
message.warning('请选择要移除的小区')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
for (const tenantId of selectedAssignedIds.value) {
|
||||
await UserTenantApi.removeUserFromTenant(currentUserInfo.value.id, tenantId)
|
||||
}
|
||||
message.success('移除成功')
|
||||
assignedTenantList.value = assignedTenantList.value.filter(
|
||||
t => !selectedAssignedIds.value.includes(t.id)
|
||||
)
|
||||
selectedAssignedIds.value = []
|
||||
loadAssignedTenants(currentUserInfo.value.id)
|
||||
} catch (error) {
|
||||
console.error('移除失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const tenantIds = assignedTenantList.value.map(t => t.id)
|
||||
|
||||
await UserTenantApi.batchAssignUserToTenants({
|
||||
userId: currentUserInfo.value.id,
|
||||
tenantIds: tenantIds
|
||||
})
|
||||
|
||||
message.success('分配成功')
|
||||
visible.value = false
|
||||
emit('success')
|
||||
} catch (error) {
|
||||
console.error('保存失败', error)
|
||||
message.error('保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
|
|
|
|||
Loading…
Reference in New Issue