UnisKB/ui/src/views/system-chat-user/group/index.vue

483 lines
16 KiB
Vue
Raw Normal View History

2025-06-12 08:09:06 +00:00
<template>
2025-06-26 09:36:36 +00:00
<div class="group p-24">
<el-breadcrumb separator-icon="ArrowRight" class="mb-16">
<el-breadcrumb-item>{{ t('views.chatUser.title') }}</el-breadcrumb-item>
<el-breadcrumb-item>
<h5 class="ml-4 color-text-primary">{{ t('views.chatUser.group.title') }}</h5>
</el-breadcrumb-item>
</el-breadcrumb>
<el-card style="--el-card-padding: 0">
<div class="flex">
2025-07-08 09:10:26 +00:00
<div class="user-left border-r">
<div class="p-24 pb-0">
<div class="flex-between mb-12">
2025-06-18 12:02:58 +00:00
<h4 class="medium">{{ $t('views.chatUser.group.title') }}</h4>
2025-06-26 09:36:36 +00:00
<el-tooltip
effect="dark"
:content="`${$t('common.create')}${$t('views.chatUser.group.title')}`"
placement="top"
>
<el-button
type="primary"
text
@click="createOrUpdate()"
v-hasPermission="
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
2025-07-01 13:22:21 +00:00
[
PermissionConst.WORKSPACE_USER_GROUP_CREATE,
PermissionConst.USER_GROUP_CREATE,
],
[],
'OR',
)
"
2025-06-26 09:36:36 +00:00
>
2025-07-01 06:32:29 +00:00
<el-icon :size="18">
2025-07-01 13:22:21 +00:00
<Plus />
2025-07-01 06:32:29 +00:00
</el-icon>
2025-06-18 12:02:58 +00:00
</el-button>
</el-tooltip>
</div>
2025-06-17 08:44:17 +00:00
2025-06-26 09:36:36 +00:00
<el-input
v-model="filterText"
:placeholder="$t('common.search')"
prefix-icon="Search"
clearable
2025-07-01 03:35:25 +00:00
filterable
2025-06-26 09:36:36 +00:00
/>
</div>
<div class="list-height-left">
<el-scrollbar v-loading="loading">
2025-07-08 09:10:26 +00:00
<div class="p-8-16">
<common-list
:data="filterList"
@click="clickUserGroup"
:default-active="current?.id"
@mouseenter="mouseenter"
@mouseleave="mouseId = ''"
>
<template #default="{ row }">
<div class="flex-between">
2025-07-09 08:12:02 +00:00
<span class="ellipsis" :title="row.name">{{ row.name }}</span>
2025-07-08 09:10:26 +00:00
<div @click.stop v-show="mouseId === row.id">
<el-dropdown :teleported="false" trigger="click">
<el-button text>
<el-icon class="color-secondary">
<MoreFilled />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu style="min-width: 80px">
<el-dropdown-item
@click.stop="createOrUpdate(row)"
class="p-8"
v-if="
hasPermission(
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[
PermissionConst.WORKSPACE_USER_GROUP_EDIT,
PermissionConst.USER_GROUP_EDIT,
],
[],
'OR',
),
2025-07-01 13:22:21 +00:00
'OR',
2025-07-08 09:10:26 +00:00
)
"
>
<el-icon>
<EditPen />
</el-icon>
{{ $t('common.rename') }}
</el-dropdown-item>
<el-dropdown-item
@click.stop="deleteGroup(row)"
class="border-t p-8"
v-if="
hasPermission(
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[
PermissionConst.WORKSPACE_USER_GROUP_DELETE,
PermissionConst.USER_GROUP_DELETE,
],
[],
'OR',
),
2025-07-01 13:22:21 +00:00
'OR',
2025-07-08 09:10:26 +00:00
)
"
>
<el-icon>
<Delete />
</el-icon>
{{ $t('common.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
2025-06-18 12:02:58 +00:00
</div>
2025-07-08 09:10:26 +00:00
</template>
<template #empty>
<span></span>
</template>
</common-list>
</div>
2025-06-26 09:36:36 +00:00
</el-scrollbar>
2025-06-17 08:44:17 +00:00
</div>
2025-06-26 09:36:36 +00:00
</div>
2025-06-17 08:44:17 +00:00
2025-06-26 09:36:36 +00:00
<!-- 右边 -->
<div class="user-right" v-loading="rightLoading">
<div class="flex align-center">
2025-07-09 08:12:02 +00:00
<h4 class="medium ellipsis" :title="current?.name">{{ current?.name }}</h4>
2025-07-01 13:22:21 +00:00
<el-divider direction="vertical" class="mr-8 ml-8" />
2025-06-26 09:36:36 +00:00
<AppIcon
iconName="app-workspace"
style="font-size: 16px"
class="color-input-placeholder"
></AppIcon>
<span class="color-input-placeholder ml-4">
{{ paginationConfig.total }}
</span>
</div>
2025-06-18 12:02:58 +00:00
2025-06-26 09:36:36 +00:00
<div class="flex-between mb-16" style="margin-top: 20px">
<div>
<el-button
type="primary"
@click="createUser()"
v-hasPermission="
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
2025-07-01 13:22:21 +00:00
[
PermissionConst.WORKSPACE_USER_GROUP_ADD_MEMBER,
PermissionConst.USER_GROUP_ADD_MEMBER,
],
[],
'OR',
)
"
2025-06-26 09:36:36 +00:00
>
{{ t('views.role.member.add') }}
</el-button>
<el-button
:disabled="multipleSelection.length === 0"
@click="handleDeleteUser()"
v-hasPermission="
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
2025-07-01 13:22:21 +00:00
[
PermissionConst.WORKSPACE_USER_GROUP_REMOVE_MEMBER,
PermissionConst.USER_GROUP_REMOVE_MEMBER,
],
[],
'OR',
)
"
2025-06-26 09:36:36 +00:00
>
{{ $t('common.remove') }}
</el-button>
</div>
<div class="flex-between complex-search">
<el-select class="complex-search__left" v-model="searchType" style="width: 120px">
2025-07-01 13:22:21 +00:00
<el-option :label="$t('views.login.loginForm.username.label')" value="username" />
<el-option
:label="$t('views.userManage.userForm.nick_name.label')"
value="nick_name"
/>
2025-06-26 09:36:36 +00:00
</el-select>
<el-input
v-if="searchType === 'username'"
v-model="searchForm.username"
@change="getList"
:placeholder="$t('common.searchBar.placeholder')"
style="width: 220px"
clearable
/>
2025-07-01 06:32:29 +00:00
<el-input
v-else-if="searchType === 'nick_name'"
v-model="searchForm.nick_name"
@change="getList"
:placeholder="$t('common.searchBar.placeholder')"
style="width: 220px"
clearable
/>
2025-06-17 08:44:17 +00:00
</div>
2025-06-18 12:02:58 +00:00
</div>
2025-06-26 09:36:36 +00:00
<app-table
:data="tableData"
:pagination-config="paginationConfig"
@sizeChange="handleSizeChange"
@changePage="getList"
@selection-change="handleSelectionChange"
2025-07-09 08:12:02 +00:00
:maxTableHeight="330"
2025-06-26 09:36:36 +00:00
>
2025-07-01 13:22:21 +00:00
<el-table-column type="selection" width="55" />
2025-06-26 09:36:36 +00:00
<el-table-column
prop="nick_name"
:label="$t('views.userManage.userForm.nick_name.label')"
2025-07-09 08:12:02 +00:00
show-overflow-tooltip
2025-06-26 09:36:36 +00:00
/>
2025-07-01 13:22:21 +00:00
<el-table-column prop="username" :label="$t('views.login.loginForm.username.label')" />
2025-06-26 09:36:36 +00:00
<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>
</el-table-column>
<el-table-column :label="$t('common.operation')" width="100" fixed="right">
<template #default="{ row }">
<el-tooltip effect="dark" :content="`${$t('common.remove')}`" placement="top">
<el-button
type="primary"
text
@click.stop="handleDeleteUser(row)"
v-hasPermission="
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
2025-07-01 13:22:21 +00:00
[
PermissionConst.WORKSPACE_USER_GROUP_REMOVE_MEMBER,
PermissionConst.USER_GROUP_REMOVE_MEMBER,
],
[],
'OR',
)
"
2025-06-26 09:36:36 +00:00
>
<el-icon>
2025-07-01 13:22:21 +00:00
<EditPen />
2025-06-26 09:36:36 +00:00
</el-icon>
</el-button>
</el-tooltip>
</template>
</el-table-column>
</app-table>
2025-06-12 08:09:06 +00:00
</div>
2025-06-26 09:36:36 +00:00
</div>
</el-card>
2025-07-01 13:22:21 +00:00
<CreateOrUpdateGroupDialog ref="createOrUpdateGroupDialogRef" @refresh="refresh" />
<CreateGroupUserDialog ref="createGroupUserDialogRef" @refresh="getList" />
2025-06-18 12:02:58 +00:00
</div>
2025-06-12 08:09:06 +00:00
</template>
<script lang="ts" setup>
2025-07-01 13:22:21 +00:00
import { onMounted, ref, watch, reactive } from 'vue'
2025-06-17 08:44:17 +00:00
import SystemGroupApi from '@/api/system/user-group'
2025-07-01 13:22:21 +00:00
import { t } from '@/locales'
import type { ChatUserGroupUserItem } from '@/api/type/systemChatUser'
2025-06-17 08:44:17 +00:00
import CreateOrUpdateGroupDialog from './component/CreateOrUpdateGroupDialog.vue'
2025-06-17 09:57:59 +00:00
import CreateGroupUserDialog from './component/CreateGroupUserDialog.vue'
2025-07-01 13:22:21 +00:00
import type { ListItem } from '@/api/type/common'
import { MsgSuccess, MsgConfirm } from '@/utils/message'
import { PermissionConst, RoleConst } from '@/utils/permission/data'
import { ComplexPermission } from '@/utils/permission/type'
import { hasPermission } from '@/utils/permission/index'
import { loadPermissionApi } from '@/utils/dynamics-api/permission-api.ts'
2025-06-12 08:09:06 +00:00
const filterText = ref('')
2025-06-17 08:44:17 +00:00
const loading = ref(false)
const list = ref<ListItem[]>([])
const filterList = ref<ListItem[]>([]) // 搜索过滤后列表
const current = ref<ListItem>()
2025-06-12 08:09:06 +00:00
2025-06-17 08:44:17 +00:00
async function getUserGroupList() {
try {
2025-07-01 09:38:02 +00:00
const res = await loadPermissionApi('userGroup').getUserGroup(loading)
2025-06-17 08:44:17 +00:00
list.value = res.data
filterList.value = filter(list.value, filterText.value)
} catch (error) {
console.error(error)
2025-06-12 08:09:06 +00:00
}
2025-06-17 08:44:17 +00:00
}
onMounted(async () => {
await getUserGroupList()
current.value = list.value[0]
2025-06-12 08:09:06 +00:00
})
2025-06-17 08:44:17 +00:00
function filter(list: ListItem[], filterText: string) {
if (!filterText.length) {
return list
}
2025-06-26 09:36:36 +00:00
return list.filter((v: ListItem) => v.name.toLowerCase().includes(filterText.toLowerCase()))
2025-06-12 08:09:06 +00:00
}
2025-06-17 08:44:17 +00:00
watch(filterText, (val: string) => {
filterList.value = filter(list.value, val)
})
function clickUserGroup(item: ListItem) {
current.value = item
2025-06-12 08:09:06 +00:00
}
2025-06-17 08:44:17 +00:00
const createOrUpdateGroupDialogRef = ref<InstanceType<typeof CreateOrUpdateGroupDialog>>()
2025-06-17 11:49:59 +00:00
2025-06-17 08:44:17 +00:00
function createOrUpdate(item?: ListItem) {
2025-06-26 09:36:36 +00:00
createOrUpdateGroupDialogRef.value?.open(item)
2025-06-17 08:44:17 +00:00
}
function deleteGroup(item: ListItem) {
MsgConfirm(
`${t('views.chatUser.group.delete.confirmTitle')}${item.name} ?`,
t('views.chatUser.group.delete.confirmMessage'),
{
confirmButtonText: t('common.confirm'),
2025-07-03 06:28:57 +00:00
confirmButtonClass: 'danger',
2025-06-17 08:44:17 +00:00
},
)
.then(() => {
2025-07-01 13:22:21 +00:00
loadPermissionApi('userGroup')
.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
})
2025-07-01 06:32:29 +00:00
})
2025-07-01 13:22:21 +00:00
.catch(() => {})
2025-06-12 08:09:06 +00:00
}
2025-06-17 08:44:17 +00:00
async function refresh(group?: ListItem) {
2025-06-26 09:36:36 +00:00
await getUserGroupList()
2025-07-01 03:04:53 +00:00
// 创建后选中新建的
2025-07-01 13:22:21 +00:00
if (group) {
2025-07-01 03:04:53 +00:00
current.value = group
} else {
2025-07-01 13:22:21 +00:00
current.value = list.value.find((item) => item.id === current.value?.id)
2025-07-01 03:04:53 +00:00
}
2025-06-12 08:09:06 +00:00
}
2025-06-17 08:44:17 +00:00
const rightLoading = ref(false)
const searchType = ref('username')
const searchForm = ref<Record<string, any>>({
username: '',
2025-07-01 06:32:29 +00:00
nick_name: '',
2025-06-17 08:44:17 +00:00
})
const paginationConfig = reactive({
current_page: 1,
page_size: 20,
total: 0,
})
const tableData = ref<ChatUserGroupUserItem[]>([])
2025-06-12 08:09:06 +00:00
2025-06-17 08:44:17 +00:00
async function getList() {
if (!current.value?.id) return
try {
2025-07-01 06:32:29 +00:00
const params = {
[searchType.value]: searchForm.value[searchType.value as keyof typeof searchForm.value],
}
2025-07-01 09:38:02 +00:00
const res = await loadPermissionApi('userGroup').getUserListByGroup(
2025-06-26 09:36:36 +00:00
current.value?.id,
paginationConfig,
2025-07-01 06:32:29 +00:00
params,
2025-06-26 09:36:36 +00:00
rightLoading,
)
2025-06-17 08:44:17 +00:00
tableData.value = res.data.records
paginationConfig.total = res.data.total
} catch (error) {
console.error(error)
}
}
function handleSizeChange() {
paginationConfig.current_page = 1
getList()
}
2025-06-26 09:36:36 +00:00
watch(
() => current.value?.id,
() => {
getList()
},
)
2025-06-17 08:44:17 +00:00
2025-06-17 09:57:59 +00:00
const createGroupUserDialogRef = ref<InstanceType<typeof CreateGroupUserDialog>>()
2025-06-17 11:49:59 +00:00
2025-06-17 08:44:17 +00:00
function createUser() {
2025-06-26 09:36:36 +00:00
createGroupUserDialogRef.value?.open(current.value?.id as string)
2025-06-17 08:44:17 +00:00
}
2025-06-17 11:37:43 +00:00
const multipleSelection = ref<any[]>([])
2025-06-17 11:49:59 +00:00
2025-06-17 11:37:43 +00:00
function handleSelectionChange(val: any[]) {
2025-06-17 08:44:17 +00:00
multipleSelection.value = val
}
2025-06-17 09:57:59 +00:00
function handleDeleteUser(item?: ChatUserGroupUserItem) {
MsgConfirm(
2025-06-26 09:36:36 +00:00
item
? `${t('views.workspace.member.delete.confirmTitle')}${item.nick_name} ?`
2025-07-01 13:22:21 +00:00
: t('views.chatUser.group.batchDeleteMember', { count: multipleSelection.value.length }),
2025-06-17 11:37:43 +00:00
'',
2025-06-17 09:57:59 +00:00
{
confirmButtonText: t('common.confirm'),
2025-07-03 06:28:57 +00:00
confirmButtonClass: 'danger',
2025-06-17 09:57:59 +00:00
},
)
.then(() => {
2025-07-01 13:22:21 +00:00
loadPermissionApi('userGroup')
.postRemoveMember(
current.value?.id as string,
{
group_relation_ids: item
? [item.user_group_relation_id]
: multipleSelection.value.map((item) => item.user_group_relation_id),
},
loading,
)
.then(async () => {
MsgSuccess(t('common.removeSuccess'))
await getList()
})
2025-07-01 06:32:29 +00:00
})
2025-07-01 13:22:21 +00:00
.catch(() => {})
2025-06-17 12:16:49 +00:00
}
2025-07-01 06:32:29 +00:00
2025-06-26 09:36:36 +00:00
const mouseId = ref('')
2025-06-17 12:16:49 +00:00
2025-06-26 09:36:36 +00:00
function mouseenter(row: any) {
mouseId.value = row.id
2025-06-17 08:44:17 +00:00
}
2025-06-26 09:36:36 +00:00
</script>
2025-06-17 08:44:17 +00:00
2025-06-26 09:36:36 +00:00
<style lang="scss" scoped>
2025-06-17 08:44:17 +00:00
.user-left {
box-sizing: border-box;
width: var(--setting-left-width);
min-width: var(--setting-left-width);
.list-height-left {
2025-07-09 08:12:02 +00:00
height: calc(100vh - 231px);
2025-06-17 08:44:17 +00:00
}
}
.user-right {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
padding: 24px;
2025-06-12 08:09:06 +00:00
}
2025-06-17 11:49:59 +00:00
</style>