feat: systemChatUserGroup

This commit is contained in:
teukkk 2025-06-17 16:44:17 +08:00
parent e4a8f371ab
commit 63b783c8f5
10 changed files with 454 additions and 231 deletions

View File

@ -19,12 +19,12 @@ const getUserGroupUserList: (
resource: ChatUserResourceParams,
user_group_id: string,
page: pageRequest,
param: any,
username_or_nickname: string,
loading?: Ref<boolean>,
) => Promise<Result<PageList<ChatUserGroupUserItem[]>>> = (resource, user_group_id, page, param, loading) => {
) => Promise<Result<PageList<ChatUserGroupUserItem[]>>> = (resource, user_group_id, page, username_or_nickname, loading) => {
return get(
`${prefix}/${resource.resource_type}/${resource.resource_id}/user_group_id/${user_group_id}/${page.current_page}/${page.page_size}`,
param,
username_or_nickname ? { username_or_nickname } : undefined,
loading,
)
}

View File

@ -1,9 +1,18 @@
import { Result } from '@/request/Result'
import { get, put, post, del } from '@/request/index'
import type { pageRequest } from '@/api/type/common'
import type { ChatUserItem } from '@/api/type/systemChatUser'
import type { Ref } from 'vue'
const prefix = '/system/chat_user'
/**
*
*/
const getChatUserList: (loading?: Ref<boolean>) => Promise<Result<ChatUserItem[]>> = (loading) => {
return get(`${prefix}/list`, undefined, loading)
}
/**
*
* @query
@ -71,4 +80,5 @@ export default {
delUserManage,
postUserManage,
putUserManagePassword,
getChatUserList
}

View File

@ -1,13 +1,15 @@
import {Result} from '@/request/Result'
import {get, post, del, put} from '@/request/index'
import type {Ref} from 'vue'
import { Result } from '@/request/Result'
import { get, post, del } from '@/request/index'
import type { Ref } from 'vue'
import type { ChatUserGroupUserItem, } from '@/api/type/systemChatUser'
import type { pageRequest, PageList, ListItem } from '@/api/type/common'
const prefix = '/system/group'
/**
*
*/
const getUserGroup: (loading?: Ref<boolean>) => Promise<Result<any[]>> = () => {
const getUserGroup: (loading?: Ref<boolean>) => Promise<Result<ListItem[]>> = () => {
return get(`${prefix}`)
}
@ -19,7 +21,7 @@ const getUserGroup: (loading?: Ref<boolean>) => Promise<Result<any[]>> = () => {
"name": "string"
}
*/
const postUserGroup: (data: any, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
const postUserGroup: (data: ListItem, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
data,
loading,
) => {
@ -30,7 +32,7 @@ const postUserGroup: (data: any, loading?: Ref<boolean>) => Promise<Result<boole
*
* @param user_group_id
*/
const delUserGroup: (user_group_id: String, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
const delUserGroup: (user_group_id: string, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
user_group_id,
loading,
) => {
@ -48,7 +50,7 @@ const delUserGroup: (user_group_id: String, loading?: Ref<boolean>) => Promise<R
*/
const postAddMember: (
user_group_id: string,
body: any,
body: Record<string, any>,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (user_group_id, body, loading) => {
return post(`${prefix}/${user_group_id}/add_member`, body, {}, loading)
@ -64,16 +66,32 @@ const postAddMember: (
*/
const postRemoveMember: (
user_group_id: string,
body: any,
body: Record<string, any>,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (user_group_id, body, loading) => {
return post(`${prefix}/${user_group_id}`, body, {}, loading)
return post(`${prefix}/${user_group_id}/remove_member`, body, {}, loading)
}
/**
*
*/
const getUserListByGroup: (
user_group_id: string,
page: pageRequest,
username: string,
loading?: Ref<boolean>,
) => Promise<Result<PageList<ChatUserGroupUserItem[]>>> = (user_group_id, page, username, loading) => {
return get(
`${prefix}/${user_group_id}/user_list/${page.current_page}/${page.page_size}`,
username ? { username } : undefined,
loading,
)
}
export default {
getUserGroup,
postUserGroup,
delUserGroup,
postAddMember,
postRemoveMember,
getUserListByGroup
}

View File

@ -18,4 +18,8 @@ interface PageList<T> {
records: T
}
export type { KeyValue, Dict, pageRequest, PageList }
interface ListItem {
name: string,
id?: string,
}
export type { KeyValue, Dict, pageRequest, PageList, ListItem }

View File

@ -0,0 +1,29 @@
interface ChatUserItem {
create_time: string,
email: string,
id: string,
nick_name: string,
phone: string,
source: string,
update_time: string,
username: string,
is_active: boolean,
user_group_ids?: string[],
user_group_names?: string[],
}
// TODO
interface ChatUserGroupUserItem {
id: string,
is_auth: boolean,
email: string,
phone: string,
nick_name: string,
username: string,
password: string,
source: string,
is_active: boolean,
create_time: string,
update_time: string,
}
export type { ChatUserGroupUserItem, ChatUserItem }

View File

@ -6,5 +6,10 @@ export default {
authorization: '授权',
group: {
title: '用户组',
name: '用户组名称',
delete: {
confirmTitle: '是否删除用户组:',
confirmMessage: '删除后,该用户组下的成员将全部移除,请谨慎操作!',
},
}
}

View File

@ -37,7 +37,7 @@
<el-divider direction="vertical" class="mr-8 ml-8" />
<AppIcon iconName="app-wordspace" style="font-size: 16px" class="color-input-placeholder"></AppIcon>
<span class="color-input-placeholder ml-4">
{{ current?.user_count }}
{{ paginationConfig.total }}
</span>
</div>
<el-button type="primary" @click="handleSave">
@ -48,9 +48,9 @@
<div class="flex-between mb-16" style="margin-top: 18px;">
<div class="flex complex-search">
<el-select class="complex-search__left" v-model="searchType" style="width: 120px">
<el-option :label="$t('views.login.loginForm.username.label')" value="username_or_nickname" />
<el-option :label="$t('views.login.loginForm.username.label')" value="name" />
</el-select>
<el-input v-if="searchType === 'username_or_nickname'" v-model="searchForm.username_or_nickname"
<el-input v-if="searchType === 'name'" v-model="searchForm.name"
@change="getList" :placeholder="$t('common.inputPlaceholder')" style="width: 220px" clearable />
</div>
<div class="flex align-center">
@ -60,7 +60,7 @@
</div>
<app-table :data="tableData" :pagination-config="paginationConfig" @sizeChange="handleSizeChange"
@changePage="getList" v-loading="rightLoading">
@changePage="getList">
<el-table-column prop="nick_name" :label="$t('views.userManage.userForm.nick_name.label')" />
<el-table-column prop="username" :label="$t('views.login.loginForm.username.label')" />
<el-table-column prop="source" :label="$t('views.userManage.source.label')">
@ -150,9 +150,9 @@ function clickUserGroup(item: ChatUserGroupItem) {
const rightLoading = ref(false)
const searchType = ref('username_or_nickname')
const searchType = ref('name')
const searchForm = ref<Record<string, any>>({
username_or_nickname: '',
name: '',
})
const automaticAuthorization = ref(false)
const paginationConfig = reactive({
@ -166,10 +166,7 @@ const tableData = ref<ChatUserGroupUserItem[]>([])
async function getList() {
if (!current.value?.id) return
try {
const params = {
[searchType.value]: searchForm.value[searchType.value],
}
const res = await ChatUserApi.getUserGroupUserList(resource, current.value?.id, paginationConfig, params, rightLoading)
const res = await ChatUserApi.getUserGroupUserList(resource, current.value?.id, paginationConfig, searchForm.value.name, rightLoading)
tableData.value = res.data.records
paginationConfig.total = res.data.total
} catch (error) {

View File

@ -1,122 +0,0 @@
<template>
<el-dialog
v-model="dialogVisible"
:close-on-press-escape="false"
:close-on-click-modal="false"
:destroy-on-close="true"
width="600"
class="member-dialog"
>
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">{{ $t('views.team.addMember') }}</h4>
<div class="dialog-sub-title">{{ $t('views.team.addSubTitle') }}</div>
</template>
<el-form
ref="addMemberFormRef"
:model="memberForm"
label-position="top"
:rules="rules"
require-asterisk-position="right"
@submit.prevent
>
<el-form-item :label="$t('views.team.teamForm.form.userName.label')" prop="users">
<tags-input v-model:tags="memberForm.users" :placeholder="$t('views.team.teamForm.form.userName.placeholder')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
<el-button type="primary" @click="submitMember(addMemberFormRef)" :loading="loading">
{{ $t('common.add') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { MsgSuccess } from '@/utils/message'
import GroupApi from '@/api/system/user-group'
import { t } from '@/locales'
const emit = defineEmits(['refresh'])
const dialogVisible = ref<boolean>(false)
const memberForm = ref({
users: []
})
const addMemberFormRef = ref<FormInstance>()
const loading = ref<boolean>(false)
const rules = ref<FormRules>({
users: [
{
type: 'array',
required: true,
message: t('views.team.teamForm.form.userName.requiredMessage'),
trigger: 'change'
}
]
})
watch(dialogVisible, (bool) => {
if (!bool) {
memberForm.value = {
users: []
}
loading.value = false
}
})
const open = () => {
dialogVisible.value = true
}
const submitMember = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
loading.value = true
let idsArray = memberForm.value.users.map((obj: any) => obj.id)
// GroupApi.postCreatTeamMember(idsArray)
// .then((res) => {
// MsgSuccess(t('common.submitSuccess'))
// emit('refresh', idsArray)
// dialogVisible.value = false
// loading.value = false
// })
// .catch(() => {
// loading.value = false
// })
}
})
}
onMounted(() => {})
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.member-dialog {
.el-dialog__header {
padding-bottom: 19px;
}
}
.custom-select-multiple {
width: 200%;
.el-input {
min-height: 100px;
}
.el-select__tags {
top: 0;
transform: none;
padding-top: 8px;
}
.el-input__wrapper {
align-items: start;
}
}
</style>

View File

@ -0,0 +1,71 @@
<template>
<el-dialog :title="`${!form.id ? $t('common.create') : $t('common.rename')}${$t('views.chatUser.group.title')}`"
v-model="dialogVisible" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true">
<el-form label-position="top" ref="formRef" :rules="rules" :model="form" require-asterisk-position="right">
<el-form-item :label="$t('views.chatUser.group.name')" prop="name">
<el-input v-model="form.name" maxlength="64"
:placeholder="`${$t('common.inputPlaceholder')}${$t('views.chatUser.group.name')}`" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
<el-button type="primary" @click="submit(formRef)" :loading="loading">
{{ !form.id ? $t('common.create') : $t('common.save') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import type { FormInstance } from 'element-plus'
import { MsgSuccess } from '@/utils/message'
import { t } from '@/locales'
import SystemGroupApi from '@/api/system/user-group'
import type { ListItem } from '@/api/type/common'
const emit = defineEmits<{
(e: 'refresh', current: ListItem): void;
}>();
const dialogVisible = ref<boolean>(false)
const defaultForm = {
name: ''
}
const form = ref<ListItem>({
...defaultForm,
})
function open(item?: ListItem) {
if (item) {
form.value = { id: item.id, name: item.name }
} else {
form.value = { ...defaultForm }
}
dialogVisible.value = true
}
const formRef = ref<FormInstance>();
const rules = reactive({
name: [{ required: true, message: `${t('common.inputPlaceholder')}${t('views.chatUser.group.name')}`, trigger: 'blur' }],
})
const loading = ref<boolean>(false)
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
SystemGroupApi.postUserGroup(form.value, loading).then((res: any) => {
MsgSuccess(!form.value.id ? t('common.createSuccess') : t('common.renameSuccess'))
emit('refresh', res.data)
dialogVisible.value = false
})
}
})
}
defineExpose({ open })
</script>

View File

@ -1,117 +1,328 @@
<template>
<div class="group-manage p-16-24">
<h4 class="mb-16">{{ $t('views.system.group.title') }}</h4>
<ContentContainer>
<template #header>
<div class="shared-header">
<span class="title">{{ t('views.system.shared_resources') }}</span>
<el-icon size="12">
<rightOutlined></rightOutlined>
</el-icon>
<span class="sub-title">{{ t('views.knowledge.title') }}</span>
</div>
</template>
<el-card style="--el-card-padding: 0" class="user-card">
<div class="flex h-full">
<div class="user-left border-r p-16">
<div class="user-left_title flex-between">
<h4 class="medium">{{ $t('views.chatUser.group.title') }}</h4>
<el-tooltip effect="dark" :content="`${$t('common.create')}${$t('views.chatUser.group.title')}`"
placement="top">
<el-button type="primary" text @click="createOrUpdate()">
<AppIcon iconName="app-copy"></AppIcon>
</el-button>
</el-tooltip>
</div>
<div class="flex main-calc-height">
<div class="group-member p-8 border-r">
<div class="flex-between p-16">
<h4>{{ $t('views.system.group.member') }}</h4>
<el-button type="primary" link @click="addMember">
<AppIcon iconName="app-add-users" class="add-user-icon" />
</el-button>
</div>
<div class="group-member-input">
<el-input
v-model="filterText"
:placeholder="$t('views.system.group.searchBar.placeholder')"
prefix-icon="Search"
clearable
/>
</div>
<div class="list-height-left">
<el-scrollbar>
<common-list
:data="filterGroup"
class="mt-8"
v-loading="loading"
@click="clickMemberHandle"
:default-active="currentUser"
>
<template #default="{ row }">
<div class="flex-between">
<div>
<span class="mr-8">{{ row.username }}</span>
</div>
<div @click.stop style="margin-top: 5px">
<el-dropdown trigger="click" v-if="!isManage(row.type)">
<span class="cursor">
<el-icon class="rotate-90"><MoreFilled /></el-icon>
</span>
<div class="p-8">
<el-input v-model="filterText" :placeholder="$t('common.search')" prefix-icon="Search" clearable />
</div>
<div class="list-height-left">
<el-scrollbar v-loading="loading">
<common-list :data="filterList" @click="clickUserGroup" :default-active="current?.id">
<template #default="{ row }">
<div class="flex-between">
<span>{{ row.name }}</span>
<el-dropdown :teleported="false">
<el-button text>
<el-icon class="color-secondary">
<MoreFilled />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<!-- <el-dropdown-item @click.prevent="deleteMember(row)">{{
$t('views.system.group.delete.button')
}}</el-dropdown-item> -->
<el-dropdown-menu style="min-width: 80px">
<el-dropdown-item @click.stop="createOrUpdate(row)" class="p-8">
<AppIcon iconName="app-copy"></AppIcon>
{{
$t('common.rename')
}}
</el-dropdown-item>
<el-dropdown-item @click.stop="deleteGroup(row)" class="border-t p-8">
<AppIcon iconName="app-copy"></AppIcon>
{{
$t('common.delete')
}}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
<template #empty>
<span></span>
</template>
</common-list>
</el-scrollbar>
</div>
</div>
<!-- 右边 -->
<div class="user-right" v-loading="rightLoading">
<div class="flex align-center">
<h4 class="medium">{{ current?.name }}</h4>
<el-divider direction="vertical" class="mr-8 ml-8" />
<AppIcon iconName="app-wordspace" style="font-size: 16px" class="color-input-placeholder"></AppIcon>
<span class="color-input-placeholder ml-4">
{{ paginationConfig.total }}
</span>
</div>
<div class="flex-between mb-16" style="margin-top: 20px;">
<div>
<el-button type="primary" @click="createUser()">
{{ t('views.userManage.createUser') }}
</el-button>
<el-button :disabled="multipleSelection.length === 0" @click="handleDeleteUser()">
{{ $t('common.delete') }}
</el-button>
</div>
<div class="flex-between complex-search">
<el-select class="complex-search__left" v-model="searchType" style="width: 120px">
<el-option :label="$t('views.login.loginForm.username.label')" value="username" />
</el-select>
<el-input v-if="searchType === 'username'" v-model="searchForm.username" @change="getList"
:placeholder="$t('common.searchBar.placeholder')" style="width: 220px" clearable />
</div>
</div>
<app-table :data="tableData" :pagination-config="paginationConfig" @sizeChange="handleSizeChange"
@changePage="getList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="nick_name" :label="$t('views.userManage.userForm.nick_name.label')" />
<el-table-column prop="username" :label="$t('views.login.loginForm.username.label')" />
<el-table-column prop="source" :label="$t('views.userManage.source.label')">
<template #default="{ row }">
{{
row.source === 'LOCAL'
? $t('views.userManage.source.local')
: row.source === 'wecom'
? $t('views.userManage.source.wecom')
: row.source === 'lark'
? $t('views.userManage.source.lark')
: row.source === 'dingtalk'
? $t('views.userManage.source.dingtalk')
: row.source === 'OAUTH2' || row.source === 'OAuth2'
? 'OAuth2'
: row.source
}}
</template>
</common-list>
</el-scrollbar>
</el-table-column>
<el-table-column :label="$t('common.operation')" width="100" fixed="right">
<template #default="{ row }">
<el-tooltip effect="dark" :content="`${$t('views.role.member.delete.button')}`" placement="top">
<el-button type="primary" text @click.stop="handleDeleteUser([row.id])">
<el-icon>
<EditPen />
</el-icon>
</el-button>
</el-tooltip>
</template>
</el-table-column>
</app-table>
</div>
</div>
<div class="permission-setting flex" v-loading="rLoading">
<div class="group-manage__table">
<h4 class="p-24 pb-0 mb-4">{{ $t('views.system.group.permissionSetting') }}</h4>
</div>
</div>
</div>
<CreateGroupDialog ref="CreateGroupRef" @refresh="refresh" />
</div>
</el-card>
</ContentContainer>
<CreateOrUpdateGroupDialog ref="createOrUpdateGroupDialogRef" @refresh="refresh" />
<!-- <CreateGroupUserDialog ref="createGroupUserDialogRef" @refresh="getUserGroupList" /> -->
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive, watch } from 'vue'
import GroupApi from '@/api/system/user-group'
import CreateGroupDialog from './component/CreateGroupDialog.vue'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { onMounted, ref, watch, reactive, onBeforeMount } from 'vue'
import SystemGroupApi from '@/api/system/user-group'
import userManageApi from '@/api/system/chat-user'
import { t } from '@/locales'
const CreateGroupRef = ref<InstanceType<typeof CreateGroupDialog>>()
const loading = ref(false)
const rLoading = ref(false)
const groupList = ref([]) //
const filterGroup = ref([]) //
const currentUser = ref<String>('')
const currentType = ref<String>('')
import type { ChatUserGroupUserItem } from '@/api/type/systemChatUser'
import iconMap from '@/components/app-icon/icons/common'
import CreateOrUpdateGroupDialog from './component/CreateOrUpdateGroupDialog.vue'
// import CreateGroupUserDialog from './component/CreateGroupUserDialog.vue'
import type { ListItem } from '@/api/type/common'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import type { ChatUserItem } from '@/api/type/systemChatUser'
const filterText = ref('')
const loading = ref(false)
const list = ref<ListItem[]>([])
const filterList = ref<ListItem[]>([]) //
const current = ref<ListItem>()
const tableHeight = ref(0)
const rightOutlined = iconMap['right-outlined'].iconReader()
watch(filterText, (val) => {
if (val) {
// filterGroup.value = groupList.value.filter((v) =>
// v.name.toLowerCase().includes(val.toLowerCase()),
// )
} else {
filterGroup.value = groupList.value
async function getUserGroupList() {
try {
const res = await SystemGroupApi.getUserGroup(loading)
list.value = res.data
filterList.value = filter(list.value, filterText.value)
} catch (error) {
console.error(error)
}
}
onMounted(async () => {
await getUserGroupList()
current.value = list.value[0]
})
function isManage(type: String) {
return type === 'manage'
function filter(list: ListItem[], filterText: string) {
if (!filterText.length) {
return list
}
return list.filter((v: ListItem) =>
v.name.toLowerCase().includes(filterText.toLowerCase()),
)
}
function clickMemberHandle(item: any) {
currentUser.value = item.id
currentType.value = item.type
}
function addMember() {
CreateGroupRef.value?.open()
watch(filterText, (val: string) => {
filterList.value = filter(list.value, val)
})
function clickUserGroup(item: ListItem) {
current.value = item
}
function getMember(id?: string) {
loading.value = true
const createOrUpdateGroupDialogRef = ref<InstanceType<typeof CreateOrUpdateGroupDialog>>()
function createOrUpdate(item?: ListItem) {
createOrUpdateGroupDialogRef.value?.open(item);
}
function refresh(data?: string[]) {}
function deleteGroup(item: ListItem) {
MsgConfirm(
`${t('views.chatUser.group.delete.confirmTitle')}${item.name} ?`,
t('views.chatUser.group.delete.confirmMessage'),
{
confirmButtonText: t('common.confirm'),
confirmButtonClass: 'danger',
},
)
.then(() => {
SystemGroupApi.delUserGroup(item.id as string, loading).then(async () => {
MsgSuccess(t('common.deleteSuccess'))
await getUserGroupList()
current.value = item.id === current.value?.id ? list.value[0] : current.value
})
})
.catch(() => {
})
}
onMounted(() => {})
async function refresh(group?: ListItem) {
await getUserGroupList();
//
current.value = group ? group : current.value
}
const rightLoading = ref(false)
const searchType = ref('username')
const searchForm = ref<Record<string, any>>({
username: '',
})
const paginationConfig = reactive({
current_page: 1,
page_size: 20,
total: 0,
})
const tableData = ref<ChatUserGroupUserItem[]>([])
async function getList() {
if (!current.value?.id) return
try {
const res = await SystemGroupApi.getUserListByGroup(current.value?.id, paginationConfig, searchForm.value.username, rightLoading)
tableData.value = res.data.records
paginationConfig.total = res.data.total
} catch (error) {
console.error(error)
}
}
function handleSizeChange() {
paginationConfig.current_page = 1
getList()
}
watch(() => current.value?.id, () => {
getList()
})
const chatUserList = ref<ChatUserItem[]>([])
async function getChatUserList() {
try {
const res = await userManageApi.getChatUserList()
chatUserList.value = res.data
} catch (e) {
console.error(e)
}
}
onBeforeMount(() => {
getChatUserList()
})
// const createGroupUserDialogRef = ref<InstanceType<typeof CreateGroupUserDialog>>()
function createUser() {
// createGroupUserDialogRef.value?.open();
}
const multipleSelection = ref<string[]>([])
function handleSelectionChange(val: string[]) {
multipleSelection.value = val
}
function handleDeleteUser(ids?: string[]) {
// TODO
}
</script>
<style lang="scss" scoped>
.group-manage {
.content-container {
height: 100%;
display: flex;
flex-direction: column;
:deep(.content-container__main) {
flex: 1;
overflow: hidden;
}
}
</style>
:deep(.user-card) {
height: 100%;
overflow: hidden;
}
.user-left {
box-sizing: border-box;
width: var(--setting-left-width);
min-width: var(--setting-left-width);
.user-left_title {
padding: 8px;
}
.list-height-left {
height: calc(100vh - 271px);
:deep(.common-list li) {
padding-right: 4px;
padding-left: 8px;
}
}
}
.user-right {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
padding: 24px;
}
</style>