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