feat: workflow

This commit is contained in:
wangdan-fit2cloud 2025-07-01 21:22:21 +08:00
parent 3e3c40de76
commit c7214a6e56
6 changed files with 205 additions and 153 deletions

View File

@ -86,10 +86,10 @@ const ApplicationDetailRouter = {
path: 'chat-user', path: 'chat-user',
name: 'applicationChatUser', name: 'applicationChatUser',
meta: { meta: {
icon: 'app-document', icon: 'app-user-chat',
iconActive: 'app-document-active', iconActive: 'app-user-chat',
title: 'views.chatUser.title', title: 'views.chatUser.title',
active: 'chat-log', active: 'chat-user',
parentPath: '/application/:id/:type', parentPath: '/application/:id/:type',
parentName: 'ApplicationDetail', parentName: 'ApplicationDetail',
resourceType: SourceTypeEnum.APPLICATION, resourceType: SourceTypeEnum.APPLICATION,

View File

@ -75,8 +75,8 @@ const DocumentRouter = {
path: 'chat-user', path: 'chat-user',
name: 'KnowledgeChatUser', name: 'KnowledgeChatUser',
meta: { meta: {
icon: 'app-document', icon: 'app-user-chat',
iconActive: 'app-document-active', iconActive: 'app-user-chat',
title: 'views.chatUser.title', title: 'views.chatUser.title',
active: 'chat-log', active: 'chat-log',
parentPath: '/knowledge/:id/:folderId', parentPath: '/knowledge/:id/:folderId',
@ -98,14 +98,19 @@ const DocumentRouter = {
if (to.params.folderId == 'shared') { if (to.params.folderId == 'shared') {
return PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_READ return PermissionConst.SHARED_KNOWLEDGE_CHAT_USER_READ
} else { } else {
return PermissionConst.KNOWLEDGE_CHAT_USER_READ.getKnowledgeWorkspaceResourcePermission(to ? to.params.id : '',) return PermissionConst.KNOWLEDGE_CHAT_USER_READ.getKnowledgeWorkspaceResourcePermission(
to ? to.params.id : '',
)
} }
}, },
() => { () => {
const to: any = get_next_route() const to: any = get_next_route()
if (to.params.folder_id == 'shared') { if (to.params.folder_id == 'shared') {
return RoleConst.ADMIN return RoleConst.ADMIN
} else { return PermissionConst.KNOWLEDGE_CHAT_USER_READ.getWorkspacePermissionWorkspaceManageRole } } else {
return PermissionConst.KNOWLEDGE_CHAT_USER_READ
.getWorkspacePermissionWorkspaceManageRole
}
}, },
], ],
}, },

View File

@ -507,6 +507,16 @@ h5 {
border: none !important; border: none !important;
} }
/*
图标旋转90度
*/
.rotate-90 {
transform: rotateZ(90deg);
}
.rotate-180 {
transform: rotateZ(180deg);
}
/* /*
内容部分 自适应高度 内容部分 自适应高度
*/ */

View File

@ -1,9 +1,12 @@
<template> <template>
<div v-show="show" class="workflow-dropdown-menu border border-r-6"> <div v-show="show" class="workflow-dropdown-menu border border-r-6">
<el-tabs v-model="activeName" class="workflow-dropdown-tabs"> <el-tabs v-model="activeName" class="workflow-dropdown-tabs">
<div style="display: flex; width: 100%; justify-content: center;" class="mb-12"> <div style="display: flex; width: 100%; justify-content: center" class="mb-12">
<el-input v-model="search_text" class="mr-12 ml-12" <el-input
:placeholder="$t('views.applicationWorkflow.searchBar.placeholder')"> v-model="search_text"
class="mr-12 ml-12"
:placeholder="$t('views.applicationWorkflow.searchBar.placeholder')"
>
<template #suffix> <template #suffix>
<el-icon class="el-input__icon"><search /></el-icon> <el-icon class="el-input__icon"><search /></el-icon>
</template> </template>
@ -14,23 +17,39 @@
<el-scrollbar height="400"> <el-scrollbar height="400">
<div v-if="filter_menu_nodes.length > 0"> <div v-if="filter_menu_nodes.length > 0">
<template v-for="(node, index) in filter_menu_nodes" :key="index"> <template v-for="(node, index) in filter_menu_nodes" :key="index">
<el-text type="info" size="small" class="color-secondary ml-12">{{ node.label }}</el-text> <el-text type="info" size="small" class="color-secondary ml-12">{{
node.label
}}</el-text>
<div class="flex-wrap mt-8"> <div class="flex-wrap mt-8">
<template v-for="(item, index) in node.list" :key="index"> <template v-for="(item, index) in node.list" :key="index">
<el-popover placement="right" :width="280"> <el-popover placement="right" :width="280">
<template #reference> <template #reference>
<div class="flex align-center border border-r-6 mb-12 p-8-12 cursor ml-12" style="width: 39%; " @click.stop="clickNodes(item)" <div
@mousedown.stop="onmousedown(item)"> class="flex align-center border border-r-6 mb-12 p-8-12 cursor ml-12"
<component :is="iconComponent(`${item.type}-icon`)" class="mr-8" :size="32" /> style="width: 39%"
@click.stop="clickNodes(item)"
@mousedown.stop="onmousedown(item)"
>
<component
:is="iconComponent(`${item.type}-icon`)"
class="mr-8"
:size="32"
/>
<div class="lighter">{{ item.label }}</div> <div class="lighter">{{ item.label }}</div>
</div> </div>
</template> </template>
<template #default> <template #default>
<div class="flex align-center mb-8"> <div class="flex align-center mb-8">
<component :is="iconComponent(`${item.type}-icon`)" class="mr-8" :size="32" /> <component
:is="iconComponent(`${item.type}-icon`)"
class="mr-8"
:size="32"
/>
<div class="lighter color-text-primary">{{ item.label }}</div> <div class="lighter color-text-primary">{{ item.label }}</div>
</div> </div>
<el-text type="info" size="small" class="color-secondary lighter">{{ item.text }}</el-text> <el-text type="info" size="small" class="color-secondary lighter">{{
item.text
}}</el-text>
</template> </template>
</el-popover> </el-popover>
</template> </template>
@ -174,21 +193,21 @@ const filter_application_list = computed(() => {
}) })
const filter_menu_nodes = computed(() => { const filter_menu_nodes = computed(() => {
if (!search_text.value) return menuNodes; if (!search_text.value) return menuNodes
const searchTerm = search_text.value.toLowerCase(); const searchTerm = search_text.value.toLowerCase()
return menuNodes.reduce((result: any[], item) => { return menuNodes.reduce((result: any[], item) => {
const filteredList = item.list.filter(listItem => const filteredList = item.list.filter((listItem) =>
listItem.label.toLowerCase().includes(searchTerm) listItem.label.toLowerCase().includes(searchTerm),
); )
if (filteredList.length) { if (filteredList.length) {
result.push({ ...item, list: filteredList }); result.push({ ...item, list: filteredList })
} }
return result; return result
}, []); }, [])
}); })
function clickNodes(item: any, data?: any, type?: string) { function clickNodes(item: any, data?: any, type?: string) {
if (data) { if (data) {
item['properties']['stepName'] = data.name item['properties']['stepName'] = data.name
@ -301,7 +320,7 @@ onMounted(() => {
user-select: none; /* CSS3属性 */ user-select: none; /* CSS3属性 */
position: absolute; position: absolute;
top: 49px; top: 49px;
right: 122px; right: 16px;
z-index: 99; z-index: 99;
width: 400px; width: 400px;
box-shadow: 0px 4px 8px 0px var(--app-text-color-light-1); box-shadow: 0px 4px 8px 0px var(--app-text-color-light-1);

View File

@ -1,7 +1,6 @@
<template> <template>
<ContentContainer> <div class="group p-16-24">
<template #header> <div class="mb-16">
<div>
<h2>{{ $t('views.chatUser.title') }}</h2> <h2>{{ $t('views.chatUser.title') }}</h2>
<div class="color-secondary"> <div class="color-secondary">
{{ {{
@ -11,21 +10,21 @@
}} }}
</div> </div>
</div> </div>
</template> <el-card style="--el-card-padding: 0">
<el-card style="--el-card-padding: 0" class="user-card"> <div class="flex">
<div class="flex h-full">
<div class="user-left border-r p-16"> <div class="user-left border-r p-16">
<div class="user-left_title"> <div class="p-8 pb-0 mb-12">
<h4 class="medium">{{ $t('views.chatUser.group.title') }}</h4> <h4 class="medium">{{ $t('views.chatUser.group.title') }}</h4>
</div> </div>
<div class="p-8">
<el-input <el-input
v-model="filterText" v-model="filterText"
:placeholder="$t('common.search')" :placeholder="$t('common.search')"
prefix-icon="Search" prefix-icon="Search"
clearable clearable
class="mb-8"
/> />
</div>
<div class="list-height-left"> <div class="list-height-left">
<el-scrollbar v-loading="loading"> <el-scrollbar v-loading="loading">
<common-list :data="filterList" @click="clickUserGroup" :default-active="current?.id"> <common-list :data="filterList" @click="clickUserGroup" :default-active="current?.id">
@ -46,11 +45,8 @@
<div class="flex align-center"> <div class="flex align-center">
<h4 class="medium">{{ current?.name }}</h4> <h4 class="medium">{{ current?.name }}</h4>
<el-divider direction="vertical" class="mr-8 ml-8" /> <el-divider direction="vertical" class="mr-8 ml-8" />
<AppIcon
iconName="app-workspace" <el-icon class="color-input-placeholder"><UserFilled /></el-icon>
style="font-size: 16px"
class="color-input-placeholder"
></AppIcon>
<span class="color-input-placeholder ml-4"> <span class="color-input-placeholder ml-4">
{{ paginationConfig.total }} {{ paginationConfig.total }}
</span> </span>
@ -162,13 +158,12 @@
</div> </div>
</div> </div>
</el-card> </el-card>
</ContentContainer> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref, watch, reactive, computed } from 'vue' import { onMounted, ref, watch, reactive, computed } from 'vue'
import ChatUserApi from '@/api/chat-user/chat-user'
import SharedChatUserApi from '@/api/system-shared/chat-user'
import { t } from '@/locales' import { t } from '@/locales'
import type { ChatUserGroupItem, ChatUserGroupUserItem } from '@/api/type/workspaceChatUser' import type { ChatUserGroupItem, ChatUserGroupUserItem } from '@/api/type/workspaceChatUser'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
@ -367,38 +362,13 @@ async function handleSave() {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.content-container {
height: 100%;
display: flex;
flex-direction: column;
:deep(.content-container__main) {
flex: 1;
overflow: hidden;
}
}
:deep(.user-card) {
height: 100%;
overflow: hidden;
}
.user-left { .user-left {
box-sizing: border-box; box-sizing: border-box;
width: var(--setting-left-width); width: var(--setting-left-width);
min-width: var(--setting-left-width); min-width: var(--setting-left-width);
.user-left_title {
padding: 8px;
}
.list-height-left { .list-height-left {
height: calc(100vh - 271px); height: calc(100vh - 271px);
:deep(.common-list li) {
padding-right: 4px;
padding-left: 8px;
}
} }
} }

View File

@ -24,11 +24,17 @@
v-hasPermission=" v-hasPermission="
new ComplexPermission( new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE], [RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[PermissionConst.WORKSPACE_USER_GROUP_CREATE, PermissionConst.USER_GROUP_CREATE], [
[],'OR',)" PermissionConst.WORKSPACE_USER_GROUP_CREATE,
PermissionConst.USER_GROUP_CREATE,
],
[],
'OR',
)
"
> >
<el-icon :size="18"> <el-icon :size="18">
<Plus/> <Plus />
</el-icon> </el-icon>
</el-button> </el-button>
</el-tooltip> </el-tooltip>
@ -59,7 +65,7 @@
<el-dropdown :teleported="false"> <el-dropdown :teleported="false">
<el-button text> <el-button text>
<el-icon class="color-secondary"> <el-icon class="color-secondary">
<MoreFilled/> <MoreFilled />
</el-icon> </el-icon>
</el-button> </el-button>
<template #dropdown> <template #dropdown>
@ -67,26 +73,46 @@
<el-dropdown-item <el-dropdown-item
@click.stop="createOrUpdate(row)" @click.stop="createOrUpdate(row)"
class="p-8" class="p-8"
v-if="hasPermission(new ComplexPermission( v-if="
hasPermission(
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE], [RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[PermissionConst.WORKSPACE_USER_GROUP_EDIT, PermissionConst.USER_GROUP_EDIT], [
[],'OR',),'OR',)" PermissionConst.WORKSPACE_USER_GROUP_EDIT,
PermissionConst.USER_GROUP_EDIT,
],
[],
'OR',
),
'OR',
)
"
> >
<el-icon> <el-icon>
<EditPen/> <EditPen />
</el-icon> </el-icon>
{{ $t('common.rename') }} {{ $t('common.rename') }}
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item <el-dropdown-item
@click.stop="deleteGroup(row)" @click.stop="deleteGroup(row)"
class="border-t p-8" class="border-t p-8"
v-if="hasPermission(new ComplexPermission( v-if="
hasPermission(
new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE], [RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[PermissionConst.WORKSPACE_USER_GROUP_DELETE, PermissionConst.USER_GROUP_DELETE], [
[],'OR',),'OR',)" PermissionConst.WORKSPACE_USER_GROUP_DELETE,
PermissionConst.USER_GROUP_DELETE,
],
[],
'OR',
),
'OR',
)
"
> >
<el-icon> <el-icon>
<Delete/> <Delete />
</el-icon> </el-icon>
{{ $t('common.delete') }} {{ $t('common.delete') }}
</el-dropdown-item> </el-dropdown-item>
@ -108,7 +134,7 @@
<div class="user-right" v-loading="rightLoading"> <div class="user-right" v-loading="rightLoading">
<div class="flex align-center"> <div class="flex align-center">
<h4 class="medium">{{ current?.name }}</h4> <h4 class="medium">{{ current?.name }}</h4>
<el-divider direction="vertical" class="mr-8 ml-8"/> <el-divider direction="vertical" class="mr-8 ml-8" />
<AppIcon <AppIcon
iconName="app-workspace" iconName="app-workspace"
style="font-size: 16px" style="font-size: 16px"
@ -127,8 +153,14 @@
v-hasPermission=" v-hasPermission="
new ComplexPermission( new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE], [RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[PermissionConst.WORKSPACE_USER_GROUP_ADD_MEMBER, PermissionConst.USER_GROUP_ADD_MEMBER], [
[],'OR',)" PermissionConst.WORKSPACE_USER_GROUP_ADD_MEMBER,
PermissionConst.USER_GROUP_ADD_MEMBER,
],
[],
'OR',
)
"
> >
{{ t('views.role.member.add') }} {{ t('views.role.member.add') }}
</el-button> </el-button>
@ -138,17 +170,25 @@
v-hasPermission=" v-hasPermission="
new ComplexPermission( new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE], [RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[PermissionConst.WORKSPACE_USER_GROUP_REMOVE_MEMBER, PermissionConst.USER_GROUP_REMOVE_MEMBER], [
[],'OR',)" PermissionConst.WORKSPACE_USER_GROUP_REMOVE_MEMBER,
PermissionConst.USER_GROUP_REMOVE_MEMBER,
],
[],
'OR',
)
"
> >
{{ $t('common.remove') }} {{ $t('common.remove') }}
</el-button> </el-button>
</div> </div>
<div class="flex-between complex-search"> <div class="flex-between complex-search">
<el-select class="complex-search__left" v-model="searchType" style="width: 120px"> <el-select class="complex-search__left" v-model="searchType" style="width: 120px">
<el-option :label="$t('views.login.loginForm.username.label')" value="username"/> <el-option :label="$t('views.login.loginForm.username.label')" value="username" />
<el-option :label="$t('views.userManage.userForm.nick_name.label')" <el-option
value="nick_name"/> :label="$t('views.userManage.userForm.nick_name.label')"
value="nick_name"
/>
</el-select> </el-select>
<el-input <el-input
v-if="searchType === 'username'" v-if="searchType === 'username'"
@ -176,12 +216,12 @@
@changePage="getList" @changePage="getList"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" width="55"/> <el-table-column type="selection" width="55" />
<el-table-column <el-table-column
prop="nick_name" prop="nick_name"
:label="$t('views.userManage.userForm.nick_name.label')" :label="$t('views.userManage.userForm.nick_name.label')"
/> />
<el-table-column prop="username" :label="$t('views.login.loginForm.username.label')"/> <el-table-column prop="username" :label="$t('views.login.loginForm.username.label')" />
<el-table-column prop="source" :label="$t('views.userManage.source.label')"> <el-table-column prop="source" :label="$t('views.userManage.source.label')">
<template #default="{ row }"> <template #default="{ row }">
{{ {{
@ -209,11 +249,17 @@
v-hasPermission=" v-hasPermission="
new ComplexPermission( new ComplexPermission(
[RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE], [RoleConst.ADMIN, RoleConst.WORKSPACE_MANAGE],
[PermissionConst.WORKSPACE_USER_GROUP_REMOVE_MEMBER, PermissionConst.USER_GROUP_REMOVE_MEMBER], [
[],'OR',)" PermissionConst.WORKSPACE_USER_GROUP_REMOVE_MEMBER,
PermissionConst.USER_GROUP_REMOVE_MEMBER,
],
[],
'OR',
)
"
> >
<el-icon> <el-icon>
<EditPen/> <EditPen />
</el-icon> </el-icon>
</el-button> </el-button>
</el-tooltip> </el-tooltip>
@ -224,24 +270,24 @@
</div> </div>
</el-card> </el-card>
<CreateOrUpdateGroupDialog ref="createOrUpdateGroupDialogRef" @refresh="refresh"/> <CreateOrUpdateGroupDialog ref="createOrUpdateGroupDialogRef" @refresh="refresh" />
<CreateGroupUserDialog ref="createGroupUserDialogRef" @refresh="getList"/> <CreateGroupUserDialog ref="createGroupUserDialogRef" @refresh="getList" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {onMounted, ref, watch, reactive} from 'vue' import { onMounted, ref, watch, reactive } from 'vue'
import SystemGroupApi from '@/api/system/user-group' import SystemGroupApi from '@/api/system/user-group'
import {t} from '@/locales' import { t } from '@/locales'
import type {ChatUserGroupUserItem} from '@/api/type/systemChatUser' import type { ChatUserGroupUserItem } from '@/api/type/systemChatUser'
import CreateOrUpdateGroupDialog from './component/CreateOrUpdateGroupDialog.vue' import CreateOrUpdateGroupDialog from './component/CreateOrUpdateGroupDialog.vue'
import CreateGroupUserDialog from './component/CreateGroupUserDialog.vue' import CreateGroupUserDialog from './component/CreateGroupUserDialog.vue'
import type {ListItem} from '@/api/type/common' import type { ListItem } from '@/api/type/common'
import {MsgSuccess, MsgConfirm} from '@/utils/message' import { MsgSuccess, MsgConfirm } from '@/utils/message'
import {PermissionConst, RoleConst} from '@/utils/permission/data' import { PermissionConst, RoleConst } from '@/utils/permission/data'
import {ComplexPermission} from '@/utils/permission/type' import { ComplexPermission } from '@/utils/permission/type'
import {hasPermission} from '@/utils/permission/index' import { hasPermission } from '@/utils/permission/index'
import {loadPermissionApi} from "@/utils/dynamics-api/permission-api.ts"; import { loadPermissionApi } from '@/utils/dynamics-api/permission-api.ts'
const filterText = ref('') const filterText = ref('')
const loading = ref(false) const loading = ref(false)
@ -295,14 +341,15 @@ function deleteGroup(item: ListItem) {
}, },
) )
.then(() => { .then(() => {
loadPermissionApi('userGroup').delUserGroup(item.id as string, loading).then(async () => { loadPermissionApi('userGroup')
.delUserGroup(item.id as string, loading)
.then(async () => {
MsgSuccess(t('common.deleteSuccess')) MsgSuccess(t('common.deleteSuccess'))
await getUserGroupList() await getUserGroupList()
current.value = item.id === current.value?.id ? list.value[0] : current.value current.value = item.id === current.value?.id ? list.value[0] : current.value
}) })
}) })
.catch(() => { .catch(() => {})
})
} }
async function refresh(group?: ListItem) { async function refresh(group?: ListItem) {
@ -311,7 +358,7 @@ async function refresh(group?: ListItem) {
if (group) { if (group) {
current.value = group current.value = group
} else { } else {
current.value = list.value.find(item => item.id === current.value?.id) current.value = list.value.find((item) => item.id === current.value?.id)
} }
} }
@ -377,7 +424,7 @@ function handleDeleteUser(item?: ChatUserGroupUserItem) {
MsgConfirm( MsgConfirm(
item item
? `${t('views.workspace.member.delete.confirmTitle')}${item.nick_name} ?` ? `${t('views.workspace.member.delete.confirmTitle')}${item.nick_name} ?`
: t('views.chatUser.group.batchDeleteMember', {count: multipleSelection.value.length}), : t('views.chatUser.group.batchDeleteMember', { count: multipleSelection.value.length }),
'', '',
{ {
confirmButtonText: t('common.confirm'), confirmButtonText: t('common.confirm'),
@ -385,7 +432,8 @@ function handleDeleteUser(item?: ChatUserGroupUserItem) {
}, },
) )
.then(() => { .then(() => {
loadPermissionApi('userGroup').postRemoveMember( loadPermissionApi('userGroup')
.postRemoveMember(
current.value?.id as string, current.value?.id as string,
{ {
group_relation_ids: item group_relation_ids: item
@ -393,13 +441,13 @@ function handleDeleteUser(item?: ChatUserGroupUserItem) {
: multipleSelection.value.map((item) => item.user_group_relation_id), : multipleSelection.value.map((item) => item.user_group_relation_id),
}, },
loading, loading,
).then(async () => { )
.then(async () => {
MsgSuccess(t('common.removeSuccess')) MsgSuccess(t('common.removeSuccess'))
await getList() await getList()
}) })
}) })
.catch(() => { .catch(() => {})
})
} }
const mouseId = ref('') const mouseId = ref('')