feat: resource authorization
This commit is contained in:
parent
b1a6c463d0
commit
1dfff30daf
43
ui/src/api/user/resource-authorization.ts
Normal file
43
ui/src/api/user/resource-authorization.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Result } from '@/request/Result'
|
||||||
|
import { get, put, post, del } from '@/request/index'
|
||||||
|
import type { pageRequest } from '@/api/type/common'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
|
const prefix = '/workspace'
|
||||||
|
/**
|
||||||
|
* 获取资源权限
|
||||||
|
* @query 参数
|
||||||
|
*/
|
||||||
|
const getResourceAuthorization: (workspace_id: String) => Promise<Result<any>> = (workspace_id) => {
|
||||||
|
return get(`${prefix}/${workspace_id}/user_resource_permission`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改成员权限
|
||||||
|
* @param 参数 member_id
|
||||||
|
* @param 参数 {
|
||||||
|
"team_resource_permission_list": [
|
||||||
|
{
|
||||||
|
"auth_target_type": "KNOWLEDGE",
|
||||||
|
"target_id": "string",
|
||||||
|
"auth_type": "ROLE",
|
||||||
|
"permission": {
|
||||||
|
"VIEW": true,
|
||||||
|
"MANAGE": true,
|
||||||
|
"ROLE": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const putResourceAuthorization: (workspace_id: String, body: any) => Promise<Result<any>> = (
|
||||||
|
workspace_id,
|
||||||
|
body,
|
||||||
|
) => {
|
||||||
|
return put(`${prefix}/${workspace_id}/user_resource_permission`, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getResourceAuthorization,
|
||||||
|
putResourceAuthorization,
|
||||||
|
}
|
||||||
6
ui/src/enums/system.ts
Normal file
6
ui/src/enums/system.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export enum AuthorizationEnum {
|
||||||
|
MANAGE = 'MANAGE',
|
||||||
|
USE = 'USE',
|
||||||
|
DATASET = 'DATASET',
|
||||||
|
APPLICATION = 'APPLICATION'
|
||||||
|
}
|
||||||
216
ui/src/locales/lang/zh-CN/views/application.ts
Normal file
216
ui/src/locales/lang/zh-CN/views/application.ts
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
export default {
|
||||||
|
title: '应用',
|
||||||
|
createApplication: '创建应用',
|
||||||
|
importApplication: '导入应用',
|
||||||
|
copyApplication: '复制应用',
|
||||||
|
workflow: '高级编排',
|
||||||
|
simple: '简单配置',
|
||||||
|
searchBar: {
|
||||||
|
placeholder: '按名称搜索'
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
demo: '演示'
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
confirmTitle: '是否删除应用:',
|
||||||
|
confirmMessage: '删除后该应用将不再提供服务,请谨慎操作。'
|
||||||
|
},
|
||||||
|
tip: {
|
||||||
|
ExportError: '导出失败',
|
||||||
|
professionalMessage: '社区版最多支持 5 个应用,如需拥有更多应用,请升级为专业版。',
|
||||||
|
saveErrorMessage: '保存失败,请检查输入或稍后再试',
|
||||||
|
loadingErrorMessage: '加载配置失败,请检查输入或稍后再试'
|
||||||
|
},
|
||||||
|
applicationForm: {
|
||||||
|
title: {
|
||||||
|
appTest: '调试预览',
|
||||||
|
copy: '副本'
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
appName: {
|
||||||
|
label: '名称',
|
||||||
|
placeholder: '请输入应用名称',
|
||||||
|
requiredMessage: '请输入应用名称'
|
||||||
|
},
|
||||||
|
appDescription: {
|
||||||
|
label: '描述',
|
||||||
|
placeholder: '描述该应用的应用场景及用途,如:XXX 小助手回答用户提出的 XXX 产品使用问题'
|
||||||
|
},
|
||||||
|
appType: {
|
||||||
|
label: '类型',
|
||||||
|
simplePlaceholder: '适合新手创建小助手',
|
||||||
|
workflowPlaceholder: '适合高级用户自定义小助手的工作流'
|
||||||
|
},
|
||||||
|
appTemplate: {
|
||||||
|
blankApp: '空白应用',
|
||||||
|
assistantApp: '知识库问答助手'
|
||||||
|
},
|
||||||
|
aiModel: {
|
||||||
|
label: 'AI 模型',
|
||||||
|
placeholder: '请选择 AI 模型'
|
||||||
|
},
|
||||||
|
roleSettings: {
|
||||||
|
label: '系统角色',
|
||||||
|
placeholder: '你是 xxx 小助手'
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
label: '提示词',
|
||||||
|
noReferences: ' (无引用知识库)',
|
||||||
|
references: ' (引用知识库)',
|
||||||
|
placeholder: '请输入提示词',
|
||||||
|
requiredMessage: '请输入提示词',
|
||||||
|
tooltip:
|
||||||
|
'通过调整提示词内容,可以引导大模型聊天方向,该提示词会被固定在上下文的开头,可以使用变量。',
|
||||||
|
noReferencesTooltip:
|
||||||
|
'通过调整提示词内容,可以引导大模型聊天方向,该提示词会被固定在上下文的开头。可以使用变量:{question} 是用户提出问题的占位符。',
|
||||||
|
referencesTooltip:
|
||||||
|
'通过调整提示词内容,可以引导大模型聊天方向,该提示词会被固定在上下文的开头。可以使用变量:{data} 是引用知识库中分段的占位符;{question} 是用户提出问题的占位符。',
|
||||||
|
defaultPrompt: `已知信息:{data}
|
||||||
|
用户问题:{question}
|
||||||
|
回答要求:
|
||||||
|
- 请使用中文回答用户问题`
|
||||||
|
},
|
||||||
|
historyRecord: {
|
||||||
|
label: '历史聊天记录'
|
||||||
|
},
|
||||||
|
relatedKnowledge: {
|
||||||
|
label: '关联知识库',
|
||||||
|
placeholder: '关联的知识库展示在这里'
|
||||||
|
},
|
||||||
|
multipleRoundsDialogue: '多轮对话',
|
||||||
|
|
||||||
|
prologue: '开场白',
|
||||||
|
defaultPrologue:
|
||||||
|
'您好,我是 XXX 小助手,您可以向我提出 XXX 使用问题。\n- XXX 主要功能有什么?\n- XXX 如何收费?\n- 需要转人工服务',
|
||||||
|
|
||||||
|
problemOptimization: {
|
||||||
|
label: '问题优化',
|
||||||
|
tooltip: '根据历史聊天优化完善当前问题,更利于匹配知识点。'
|
||||||
|
},
|
||||||
|
voiceInput: {
|
||||||
|
label: '语音输入',
|
||||||
|
placeholder: '请选择语音识别模型',
|
||||||
|
requiredMessage: '请选择语音输入模型',
|
||||||
|
autoSend: '自动发送'
|
||||||
|
},
|
||||||
|
voicePlay: {
|
||||||
|
label: '语音播放',
|
||||||
|
placeholder: '请选择语音合成模型',
|
||||||
|
requiredMessage: '请选择语音播放模型',
|
||||||
|
autoPlay: '自动播放',
|
||||||
|
browser: '浏览器播放(免费)',
|
||||||
|
tts: 'TTS模型',
|
||||||
|
listeningTest: '试听'
|
||||||
|
},
|
||||||
|
reasoningContent: {
|
||||||
|
label: '输出思考',
|
||||||
|
tooltip: '请根据模型返回的思考标签设置,标签中间的内容将会认定为思考过程',
|
||||||
|
start: '开始',
|
||||||
|
end: '结束'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
publish: '保存并发布',
|
||||||
|
|
||||||
|
addModel: '添加模型'
|
||||||
|
},
|
||||||
|
|
||||||
|
dialog: {
|
||||||
|
addDataset: '添加关联知识库',
|
||||||
|
addDatasetPlaceholder: '所选知识库必须使用相同的 Embedding 模型',
|
||||||
|
selected: '已选',
|
||||||
|
countDataset: '个知识库',
|
||||||
|
|
||||||
|
selectSearchMode: '检索模式',
|
||||||
|
vectorSearch: '向量检索',
|
||||||
|
vectorSearchTooltip: '向量检索是一种基于向量相似度的检索方式,适用于知识库中的大数据量场景。',
|
||||||
|
fullTextSearch: '全文检索',
|
||||||
|
fullTextSearchTooltip:
|
||||||
|
'全文检索是一种基于文本相似度的检索方式,适用于知识库中的小数据量场景。',
|
||||||
|
hybridSearch: '混合检索',
|
||||||
|
hybridSearchTooltip:
|
||||||
|
'混合检索是一种基于向量和文本相似度的检索方式,适用于知识库中的中等数据量场景。',
|
||||||
|
similarityThreshold: '相似度高于',
|
||||||
|
similarityTooltip: '相似度越高相关性越强。',
|
||||||
|
topReferences: '引用分段数 TOP',
|
||||||
|
maxCharacters: '最多引用字符数',
|
||||||
|
noReferencesAction: '无引用知识库分段时',
|
||||||
|
continueQuestioning: '继续向 AI 模型提问',
|
||||||
|
provideAnswer: '指定回答内容',
|
||||||
|
designated_answer:
|
||||||
|
'你好,我是 XXX 小助手,我的知识库只包含了 XXX 产品相关知识,请重新描述您的问题。',
|
||||||
|
defaultPrompt1:
|
||||||
|
'()里面是用户问题,根据上下文回答揣测用户问题({question}) 要求: 输出一个补全问题,并且放在',
|
||||||
|
defaultPrompt2: '标签中'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applicationAccess: {
|
||||||
|
title: '应用接入',
|
||||||
|
wecom: '企业微信应用',
|
||||||
|
wecomTip: '打造企业微信智能应用',
|
||||||
|
dingtalk: '钉钉应用',
|
||||||
|
dingtalkTip: '打造钉钉智能应用',
|
||||||
|
wechat: '公众号',
|
||||||
|
wechatTip: '打造公众号智能应用',
|
||||||
|
lark: '飞书应用',
|
||||||
|
larkTip: '打造飞书智能应用',
|
||||||
|
slack: 'Slack',
|
||||||
|
slackTip: '打造 Slack 智能应用',
|
||||||
|
setting: '配置',
|
||||||
|
callback: '回调地址',
|
||||||
|
callbackTip: '请输入回调地址',
|
||||||
|
wecomPlatform: '企业微信后台',
|
||||||
|
wechatPlatform: '微信公众平台',
|
||||||
|
dingtalkPlatform: '钉钉开放平台',
|
||||||
|
larkPlatform: '飞书开放平台',
|
||||||
|
wecomSetting: {
|
||||||
|
title: '企业微信应用配置',
|
||||||
|
cropId: '企业 ID',
|
||||||
|
cropIdPlaceholder: '请输入企业 ID',
|
||||||
|
agentIdPlaceholder: '请输入Agent ID',
|
||||||
|
secretPlaceholder: '请输入Secret',
|
||||||
|
tokenPlaceholder: '请输入Token',
|
||||||
|
encodingAesKeyPlaceholder: '请输入EncodingAESKey',
|
||||||
|
authenticationSuccessful: '认证成功',
|
||||||
|
urlInfo: '-应用管理-自建-创建的应用-接收消息-设置 API 接收的 "URL" 中'
|
||||||
|
},
|
||||||
|
dingtalkSetting: {
|
||||||
|
title: '钉钉应用配置',
|
||||||
|
clientIdPlaceholder: '请输入Client ID',
|
||||||
|
clientSecretPlaceholder: '请输入Client Secret',
|
||||||
|
urlInfo: '-机器人页面,设置 "消息接收模式" 为 HTTP模式 ,并把上面URL填写到"消息接收地址"中'
|
||||||
|
},
|
||||||
|
wechatSetting: {
|
||||||
|
title: '公众号应用配置',
|
||||||
|
appId: '开发者ID (APP ID)',
|
||||||
|
appIdPlaceholder: '请输入开发者ID (APP ID)',
|
||||||
|
appSecret: '开发者密钥 (APP SECRET)',
|
||||||
|
appSecretPlaceholder: '请输入开发者密钥 (APP SECRET)',
|
||||||
|
token: '令牌 (TOKEN)',
|
||||||
|
tokenPlaceholder: '请输入令牌 (TOKEN)',
|
||||||
|
aesKey: '消息加解密密钥',
|
||||||
|
aesKeyPlaceholder: '请输入消息加解密密钥',
|
||||||
|
urlInfo: '-设置与开发-基本配置-服务器配置的 "服务器地址URL" 中'
|
||||||
|
},
|
||||||
|
larkSetting: {
|
||||||
|
title: '飞书应用配置',
|
||||||
|
appIdPlaceholder: '请输入App ID',
|
||||||
|
appSecretPlaceholder: '请输入App Secret',
|
||||||
|
verificationTokenPlaceholder: '请输入Verification Token',
|
||||||
|
urlInfo: '-事件与回调-事件配置-配置订阅方式的 "请求地址" 中',
|
||||||
|
folderTokenPlaceholder: '请输入Folder Token'
|
||||||
|
},
|
||||||
|
slackSetting: {
|
||||||
|
title: 'Slack 应用配置',
|
||||||
|
signingSecretPlaceholder: '请输入 Signing Secret',
|
||||||
|
botUserTokenPlaceholder: '请输入 Bot User Token'
|
||||||
|
},
|
||||||
|
copyUrl: '复制链接填入到'
|
||||||
|
},
|
||||||
|
hitTest: {
|
||||||
|
title: '命中测试',
|
||||||
|
text: '针对用户提问调试段落匹配情况,保障回答效果。',
|
||||||
|
emptyMessage1: '命中段落显示在这里',
|
||||||
|
emptyMessage2: '没有命中的分段'
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,8 +6,9 @@ import document from './document'
|
|||||||
import system from './system'
|
import system from './system'
|
||||||
import userManage from './user-manage'
|
import userManage from './user-manage'
|
||||||
import resourceAuthorization from './resource-authorization'
|
import resourceAuthorization from './resource-authorization'
|
||||||
|
import application from './application'
|
||||||
// import notFound from './404'
|
// import notFound from './404'
|
||||||
// import application from './application'
|
|
||||||
// import applicationOverview from './application-overview'
|
// import applicationOverview from './application-overview'
|
||||||
|
|
||||||
// import user from './user'
|
// import user from './user'
|
||||||
@ -27,9 +28,10 @@ export default {
|
|||||||
document,
|
document,
|
||||||
system,
|
system,
|
||||||
userManage,
|
userManage,
|
||||||
resourceAuthorization
|
resourceAuthorization,
|
||||||
|
application,
|
||||||
// notFound,
|
// notFound,
|
||||||
// application,
|
|
||||||
// applicationOverview,
|
// applicationOverview,
|
||||||
|
|
||||||
// user,
|
// user,
|
||||||
|
|||||||
@ -1,3 +1,31 @@
|
|||||||
export default {
|
export default {
|
||||||
title: '资源授权',
|
title: '资源授权',
|
||||||
|
member: '成员',
|
||||||
|
manage: '所有者',
|
||||||
|
permissionSetting: '资源权限配置',
|
||||||
|
addMember: '添加成员',
|
||||||
|
addSubTitle: '成员登录后可以访问到您授权的数据。',
|
||||||
|
searchBar: {
|
||||||
|
placeholder: '请输入用户名搜索'
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
button: '移除',
|
||||||
|
confirmTitle: '是否移除成员:',
|
||||||
|
confirmMessage: '移除后将会取消成员拥有的知识库和应用权限。'
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
management: '管理',
|
||||||
|
check: '查看'
|
||||||
|
},
|
||||||
|
teamForm: {
|
||||||
|
form: {
|
||||||
|
userName: {
|
||||||
|
label: '用户名/邮箱',
|
||||||
|
placeholder: '请输入成员的用户名或邮箱',
|
||||||
|
requiredMessage: '请输入用户名/邮箱'
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,4 +39,7 @@ $primary-color: #3370ff;
|
|||||||
/** ai-chat */
|
/** ai-chat */
|
||||||
--dialog-bg-gradient-color:
|
--dialog-bg-gradient-color:
|
||||||
linear-gradient(188deg, rgba(235, 241, 255, 0.2) 39.6%, rgba(231, 249, 255, 0.2) 94.3%), #eff0f1;
|
linear-gradient(188deg, rgba(235, 241, 255, 0.2) 39.6%, rgba(231, 249, 255, 0.2) 94.3%), #eff0f1;
|
||||||
|
|
||||||
|
/** 资源授权 */
|
||||||
|
--setting-left-width: 280px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,122 @@
|
|||||||
|
<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 AuthorizationApi from '@/api/user/resource-authorization'
|
||||||
|
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)
|
||||||
|
AuthorizationApi.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>
|
||||||
@ -0,0 +1,205 @@
|
|||||||
|
<template>
|
||||||
|
<el-input
|
||||||
|
v-model="filterText"
|
||||||
|
:placeholder="$t('common.search')"
|
||||||
|
prefix-icon="Search"
|
||||||
|
class="p-24 pt-0 pb-0 mb-16 mt-4"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<div class="p-24 pt-0">
|
||||||
|
<el-table :data="filterData" :max-height="tableHeight">
|
||||||
|
<el-table-column
|
||||||
|
prop="name"
|
||||||
|
:label="
|
||||||
|
isApplication
|
||||||
|
? $t('views.application.applicationForm.form.appName.label')
|
||||||
|
: $t('views.dataset.datasetForm.form.datasetName.label')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex align-center">
|
||||||
|
<AppAvatar
|
||||||
|
v-if="isApplication && isAppIcon(row?.icon)"
|
||||||
|
style="background: none"
|
||||||
|
class="mr-12"
|
||||||
|
shape="square"
|
||||||
|
:size="24"
|
||||||
|
>
|
||||||
|
<img :src="row?.icon" alt="" />
|
||||||
|
</AppAvatar>
|
||||||
|
|
||||||
|
<AppAvatar
|
||||||
|
v-else-if="row?.name && isApplication"
|
||||||
|
:name="row?.name"
|
||||||
|
pinyinColor
|
||||||
|
shape="square"
|
||||||
|
:size="24"
|
||||||
|
class="mr-12"
|
||||||
|
/>
|
||||||
|
<AppAvatar
|
||||||
|
v-if="row.icon === '1' && isDataset"
|
||||||
|
class="mr-8 avatar-purple"
|
||||||
|
shape="square"
|
||||||
|
:size="24"
|
||||||
|
>
|
||||||
|
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
|
||||||
|
</AppAvatar>
|
||||||
|
<AppAvatar
|
||||||
|
v-else-if="row.icon === '2' && isDataset"
|
||||||
|
class="mr-8 avatar-purple"
|
||||||
|
shape="square"
|
||||||
|
:size="24"
|
||||||
|
style="background: none"
|
||||||
|
>
|
||||||
|
<img src="@/assets/logo_lark.svg" style="width: 100%" alt="" />
|
||||||
|
</AppAvatar>
|
||||||
|
<AppAvatar v-else-if="isDataset" class="mr-8 avatar-blue" shape="square" :size="24">
|
||||||
|
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
||||||
|
</AppAvatar>
|
||||||
|
<auto-tooltip :content="row?.name">
|
||||||
|
{{ row?.name }}
|
||||||
|
</auto-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('views.team.setting.management')"
|
||||||
|
align="center"
|
||||||
|
width="100"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<el-checkbox
|
||||||
|
:disabled="props.manage"
|
||||||
|
v-model="allChecked[TeamEnum.MANAGE]"
|
||||||
|
:indeterminate="allIndeterminate[TeamEnum.MANAGE]"
|
||||||
|
:label="$t('views.team.setting.management')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-checkbox
|
||||||
|
:disabled="props.manage"
|
||||||
|
v-model="row.operate[TeamEnum.MANAGE]"
|
||||||
|
@change="(e: boolean) => checkedOperateChange(TeamEnum.MANAGE, row, e)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('views.team.setting.check')"
|
||||||
|
align="center"
|
||||||
|
width="100"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<el-checkbox
|
||||||
|
:disabled="props.manage"
|
||||||
|
v-model="allChecked[TeamEnum.USE]"
|
||||||
|
:indeterminate="allIndeterminate[TeamEnum.USE]"
|
||||||
|
:label="$t('views.team.setting.check')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-checkbox
|
||||||
|
:disabled="props.manage"
|
||||||
|
v-model="row.operate[TeamEnum.USE]"
|
||||||
|
@change="(e: boolean) => checkedOperateChange(TeamEnum.USE, row, e)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, watch, computed } from 'vue'
|
||||||
|
import { TeamEnum } from '@/enums/team'
|
||||||
|
import { isAppIcon } from '@/utils/application'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
id: String,
|
||||||
|
type: String,
|
||||||
|
tableHeight: Number,
|
||||||
|
manage: Boolean
|
||||||
|
})
|
||||||
|
|
||||||
|
const isDataset = computed(() => props.type === TeamEnum.DATASET)
|
||||||
|
const isApplication = computed(() => props.type === TeamEnum.APPLICATION)
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:data'])
|
||||||
|
const allChecked: any = ref({
|
||||||
|
[TeamEnum.MANAGE]: computed({
|
||||||
|
get: () => {
|
||||||
|
return filterData.value.some((item: any) => item.operate[TeamEnum.MANAGE])
|
||||||
|
},
|
||||||
|
set: (val: boolean) => {
|
||||||
|
if (val) {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.MANAGE] = true
|
||||||
|
item.operate[TeamEnum.USE] = true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.MANAGE] = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
[TeamEnum.USE]: computed({
|
||||||
|
get: () => {
|
||||||
|
return filterData.value.some((item: any) => item.operate[TeamEnum.USE])
|
||||||
|
},
|
||||||
|
set: (val: boolean) => {
|
||||||
|
if (val) {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.USE] = true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filterData.value.map((item: any) => {
|
||||||
|
item.operate[TeamEnum.USE] = false
|
||||||
|
item.operate[TeamEnum.MANAGE] = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const filterText = ref('')
|
||||||
|
|
||||||
|
const filterData = computed(() =>
|
||||||
|
props.data.filter((v: any) => v.name.toLowerCase().includes(filterText.value.toLowerCase()))
|
||||||
|
)
|
||||||
|
|
||||||
|
const allIndeterminate: any = ref({
|
||||||
|
[TeamEnum.MANAGE]: computed(() => {
|
||||||
|
const all_not_checked = filterData.value.every((item: any) => !item.operate[TeamEnum.MANAGE])
|
||||||
|
if (all_not_checked) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !filterData.value.every((item: any) => item.operate[TeamEnum.MANAGE])
|
||||||
|
}),
|
||||||
|
[TeamEnum.USE]: computed(() => {
|
||||||
|
const all_not_checked = filterData.value.every((item: any) => !item.operate[TeamEnum.USE])
|
||||||
|
if (all_not_checked) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !filterData.value.every((item: any) => item.operate[TeamEnum.USE])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
function checkedOperateChange(Name: string | number, row: any, e: boolean) {
|
||||||
|
props.data.map((item: any) => {
|
||||||
|
if (item.id === row.id) {
|
||||||
|
item.operate[Name] = e
|
||||||
|
if (Name === TeamEnum.MANAGE && e) {
|
||||||
|
item.operate[TeamEnum.USE] = true
|
||||||
|
} else if (Name === TeamEnum.USE && !e) {
|
||||||
|
item.operate[TeamEnum.MANAGE] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -1,9 +1,223 @@
|
|||||||
<template><span>22222</span></template>
|
<template>
|
||||||
|
<div class="p-16-24">
|
||||||
|
<h4 class="mb-16">{{ $t('views.userManage.title') }}</h4>
|
||||||
|
<el-card>
|
||||||
|
<div class="resource-authorization flex main-calc-height">
|
||||||
|
<div class="team-member p-8 border-r">
|
||||||
|
<div class="flex-between p-16">
|
||||||
|
<h4>{{ $t('views.resourceAuthorization.member') }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="team-member-input">
|
||||||
|
<el-input
|
||||||
|
v-model="filterText"
|
||||||
|
:placeholder="$t('common.search')"
|
||||||
|
prefix-icon="Search"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="list-height-left">
|
||||||
|
<el-scrollbar>
|
||||||
|
<common-list
|
||||||
|
:data="filterMember"
|
||||||
|
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>
|
||||||
|
<el-tag v-if="isManage(row.type)" class="default-tag">{{
|
||||||
|
$t('views.resourceAuthorization.manage')
|
||||||
|
}}</el-tag>
|
||||||
|
</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>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click.prevent="deleteMember(row)">{{
|
||||||
|
$t('views.resourceAuthorization.delete.button')
|
||||||
|
}}</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</common-list>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="permission-setting flex" v-loading="rLoading">
|
||||||
|
<div class="team-manage__table">
|
||||||
|
<h4 class="p-24 pb-0 mb-4">{{ $t('views.resourceAuthorization.permissionSetting') }}</h4>
|
||||||
|
<el-tabs v-model="activeName" class="team-manage__tabs">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="(item, index) in settingTags"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:name="item.value"
|
||||||
|
>
|
||||||
|
<!-- <PermissionSetting
|
||||||
|
:key="index"
|
||||||
|
:data="item.data"
|
||||||
|
:type="item.value"
|
||||||
|
:tableHeight="tableHeight"
|
||||||
|
:manage="isManage(currentType)"
|
||||||
|
></PermissionSetting> -->
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="submit-button">
|
||||||
|
<el-button type="primary" @click="submitPermissions">{{ $t('common.save') }}</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<!-- <CreateMemberDialog ref="CreateMemberRef" @refresh="refresh" /> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, reactive, watch } from 'vue'
|
import { onMounted, ref, reactive, watch } from 'vue'
|
||||||
|
import AuthorizationApi from '@/api/user/resource-authorization'
|
||||||
|
import type { TeamMember } from '@/api/type/team'
|
||||||
|
// import CreateMemberDialog from './component/CreateMemberDialog.vue'
|
||||||
|
// import PermissionSetting from './component/PermissionSetting.vue'
|
||||||
|
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||||
|
import { AuthorizationEnum } from '@/enums/system'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
// const CreateMemberRef = ref<InstanceType<typeof CreateMemberDialog>>()
|
||||||
|
const loading = ref(false)
|
||||||
|
const rLoading = ref(false)
|
||||||
|
const memberList = ref<TeamMember[]>([]) // 全部成员
|
||||||
|
const filterMember = ref<TeamMember[]>([]) // 搜索过滤后列表
|
||||||
|
const currentUser = ref<String>('')
|
||||||
|
const currentType = ref<String>('')
|
||||||
|
|
||||||
onMounted(() => {})
|
const filterText = ref('')
|
||||||
|
|
||||||
|
const activeName = ref(AuthorizationEnum.DATASET)
|
||||||
|
const tableHeight = ref(0)
|
||||||
|
|
||||||
|
const settingTags = reactive([
|
||||||
|
{
|
||||||
|
label: t('views.knowledge.title'),
|
||||||
|
value: AuthorizationEnum.DATASET,
|
||||||
|
data: [] as any,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('views.application.title'),
|
||||||
|
value: AuthorizationEnum.APPLICATION,
|
||||||
|
data: [] as any,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
watch(filterText, (val) => {
|
||||||
|
if (val) {
|
||||||
|
filterMember.value = memberList.value.filter((v) =>
|
||||||
|
v.username.toLowerCase().includes(val.toLowerCase()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
filterMember.value = memberList.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function isManage(type: String) {
|
||||||
|
return type === 'manage'
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitPermissions() {
|
||||||
|
rLoading.value = true
|
||||||
|
const obj: any = {
|
||||||
|
team_member_permission_list: [],
|
||||||
|
}
|
||||||
|
settingTags.map((item) => {
|
||||||
|
item.data.map((v: any) => {
|
||||||
|
obj['team_member_permission_list'].push({
|
||||||
|
target_id: v.id,
|
||||||
|
type: v.type,
|
||||||
|
operate: v.operate,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
AuthorizationApi.putResourceAuthorization(currentUser.value, obj)
|
||||||
|
.then(() => {
|
||||||
|
MsgSuccess(t('common.submitSuccess'))
|
||||||
|
ResourcePermissions(currentUser.value)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
rLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResourcePermissions() {
|
||||||
|
rLoading.value = true
|
||||||
|
AuthorizationApi.getResourceAuthorization('default')
|
||||||
|
.then((res) => {
|
||||||
|
rLoading.value = false
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
rLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function refresh(data?: string[]) {}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
tableHeight.value = window.innerHeight - 330
|
||||||
|
window.onresize = () => {
|
||||||
|
return (() => {
|
||||||
|
tableHeight.value = window.innerHeight - 330
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
ResourcePermissions()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.resource-authorization {
|
||||||
|
.add-user-icon {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
.team-member-input {
|
||||||
|
padding: 0 calc(var(--app-base-px) * 2);
|
||||||
|
}
|
||||||
|
.team-member {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: var(--setting-left-width);
|
||||||
|
min-width: var(--setting-left-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-setting {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: calc(100% - var(--setting-left-width));
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
.submit-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 54px;
|
||||||
|
right: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list-height-left {
|
||||||
|
height: calc(var(--create-dataset-height) - 60px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__tabs {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-scroll) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__table {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user