Skip to content

Commit ba180f0

Browse files
feat: portal
1 parent 903501d commit ba180f0

6 files changed

Lines changed: 74 additions & 25 deletions

File tree

‎ui/src/views/VIEW_README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,9 @@ Dialog。新增或重命名文件时,应同步更新所有导入和页面功
419419
## 备注要求
420420

421421
门户设置的 `components/ButtonEditPortal.vue`、`ButtonPortalAuthSetting.vue` 和
422-
`ButtonPortalCorsSetting.vue` 分别封装对应按钮、浮层及表单状态。认证组件使用 `MkDrawer`,
422+
`ButtonPortalCorsSetting.vue` 分别封装对应按钮、浮层及表单状态,通过 `disabled` 接收页面统一的
423+
编辑限制。页面使用 `perm.system.portal.edit()` 判断门户编辑权限,无权限时禁用配置、复制按钮、
424+
访问开关及浮层表单和按钮,保存入口同步校验权限。认证组件使用 `MkDrawer`,
423425
打开时与系统 `LoginSetting` 共用 `AuthSettingApi.getLoginSetting()`,通过返回的 `login_methods`
424426
生成登录方式选项,复用登录方式标签,初始保留账号登录。提供默认登录方式及验证码与账号锁定设置;默认方式
425427
仅可选已勾选项,取消勾选当前默认项时回退到首个可选项。认证组件暴露 `open()` 供首次开启认证

‎ui/src/views/system/chat-management/portal-setting/PortalSettingView.vue‎

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import LogoIcon from '@/components/mk-logo/LogoIcon.vue'
66
import { copyText } from '@/utils/clipboard'
77
import { resetUrl } from '@/utils/icon'
88
import { MsgSuccess } from '@/utils/message'
9+
import { perm } from '@/permission'
910
import PortalPreview from './components/PortalPreview.vue'
1011
import ButtonEditPortal from './components/ButtonEditPortal.vue'
1112
import ButtonPortalAuthSetting from './components/ButtonPortalAuthSetting.vue'
@@ -49,6 +50,8 @@ function loadPortalSetting() {
4950
5051
/* 保存门户配置:接口返回完整配置,成功后整体回填作为页面唯一数据来源 */
5152
function savePortalSetting(payload: PortalSettingPayload | FormData): Promise<void> {
53+
if (!canEditPortal.value || saving.value) return Promise.resolve()
54+
5255
saving.value = true
5356
return PortalApi.putPortalSetting(payload)
5457
.then((setting) => {
@@ -61,7 +64,7 @@ function savePortalSetting(payload: PortalSettingPayload | FormData): Promise<vo
6164
}
6265
6366
function handleAccessChange(field: AccessField, value: string | number | boolean) {
64-
if (saving.value) return
67+
if (!canEditPortal.value || saving.value) return
6568
if (field === 'enable_auth' && value && !portalSetting.auth_config.login_value?.length) {
6669
handleOpenAuthSetting(true)
6770
return
@@ -81,6 +84,8 @@ function savePortalCorsSetting(origins: string[]) {
8184
}
8285
8386
function handleOpenAuthSetting(enableAfterSave = false) {
87+
if (!canEditPortal.value || saving.value) return
88+
8489
authSettingButtonRef.value?.open(enableAfterSave)
8590
}
8691
@@ -97,6 +102,9 @@ const previewName = computed(() => portalSetting.name)
97102
const previewLogo = computed(() => resetUrl(portalSetting.logo))
98103
99104
onMounted(() => loadPortalSetting())
105+
106+
// 权限 控制
107+
const canEditPortal = computed(() => perm.system.portal.edit())
100108
</script>
101109

102110
<template>
@@ -111,7 +119,7 @@ onMounted(() => loadPortalSetting())
111119
<h4 class="truncate" :title="previewName">{{ previewName }}</h4>
112120
</div>
113121
<!-- 编辑门户名称与 Logo -->
114-
<ButtonEditPortal :setting="portalSetting" :saving="saving" :save="savePortalSetting" />
122+
<ButtonEditPortal :setting="portalSetting" :saving="saving" :disabled="!canEditPortal" :save="savePortalSetting" />
115123
</div>
116124
</el-card>
117125
<el-card shadow="never">
@@ -122,6 +130,7 @@ onMounted(() => loadPortalSetting())
122130
<div class="flex-between mb-2 gap-2">
123131
<span>门户公开访问链接</span>
124132
<el-switch
133+
:disabled="!canEditPortal || saving"
125134
:model-value="portalSetting.enable_public_access"
126135
size="small"
127136
@change="handleAccessChange('enable_public_access', $event)"
@@ -130,7 +139,7 @@ onMounted(() => loadPortalSetting())
130139
<el-input :model-value="portalAccessUrl" readonly>
131140
<template #suffix>
132141
<!-- 复制门户公开访问链接 -->
133-
<el-button text @click="copyText(portalAccessUrl)" class="-mr-1">
142+
<el-button text :disabled="!canEditPortal" @click="copyText(portalAccessUrl)" class="-mr-1">
134143
<MkIcon name="icon_copy_outlined" class="text-N600" />
135144
</el-button>
136145
</template>
@@ -140,12 +149,17 @@ onMounted(() => loadPortalSetting())
140149
<div>
141150
<div class="flex-between mb-2 gap-2">
142151
<span>智能体后端 API 访问</span>
143-
<el-switch :model-value="portalSetting.enable_api" size="small" @change="handleAccessChange('enable_api', $event)" />
152+
<el-switch
153+
:disabled="!canEditPortal || saving"
154+
:model-value="portalSetting.enable_api"
155+
size="small"
156+
@change="handleAccessChange('enable_api', $event)"
157+
/>
144158
</div>
145159
<el-input :model-value="portalApiUrl" readonly>
146160
<template #suffix>
147161
<!-- 复制智能体后端 API 访问 -->
148-
<el-button text @click="copyText(portalApiUrl)" class="-mr-1">
162+
<el-button text :disabled="!canEditPortal" @click="copyText(portalApiUrl)" class="-mr-1">
149163
<MkIcon name="icon_copy_outlined" class="text-N600" />
150164
</el-button>
151165
</template>
@@ -156,6 +170,7 @@ onMounted(() => loadPortalSetting())
156170
<div class="flex-between mb-2 gap-2">
157171
<span>知识库后端 API 检索</span>
158172
<el-switch
173+
:disabled="!canEditPortal || saving"
159174
:model-value="portalSetting.enable_knowledge_base_api"
160175
size="small"
161176
@change="handleAccessChange('enable_knowledge_base_api', $event)"
@@ -164,7 +179,7 @@ onMounted(() => loadPortalSetting())
164179
<el-input :model-value="portalApiUrl" readonly>
165180
<template #suffix>
166181
<!-- 复制知识库后端 API 检索 -->
167-
<el-button text @click="copyText(portalApiUrl)" class="-mr-1">
182+
<el-button text :disabled="!canEditPortal" @click="copyText(portalApiUrl)" class="-mr-1">
168183
<MkIcon name="icon_copy_outlined" class="text-N600" />
169184
</el-button>
170185
</template>
@@ -174,16 +189,32 @@ onMounted(() => loadPortalSetting())
174189
<span>身份认证</span>
175190
<div class="flex-align-center gap-2">
176191
<!-- 配置身份认证 -->
177-
<ButtonPortalAuthSetting ref="authSettingButtonRef" :setting="portalSetting" :saving="saving" :save="savePortalAuthSetting" />
178-
<el-switch :model-value="portalSetting.enable_auth" size="small" @change="handleAccessChange('enable_auth', $event)" />
192+
<ButtonPortalAuthSetting
193+
ref="authSettingButtonRef"
194+
:setting="portalSetting"
195+
:saving="saving"
196+
:disabled="!canEditPortal"
197+
:save="savePortalAuthSetting"
198+
/>
199+
<el-switch
200+
:disabled="!canEditPortal || saving"
201+
:model-value="portalSetting.enable_auth"
202+
size="small"
203+
@change="handleAccessChange('enable_auth', $event)"
204+
/>
179205
</div>
180206
</div>
181207
<div class="flex-between gap-2">
182208
<span>跨域设置</span>
183209
<div class="flex-align-center gap-2">
184210
<!-- 配置跨域地址 -->
185-
<ButtonPortalCorsSetting v-model="portalCorsOrigins" :saving="saving" :save="savePortalCorsSetting" />
186-
<el-switch :model-value="portalSetting.enable_cors" size="small" @change="handleAccessChange('enable_cors', $event)" />
211+
<ButtonPortalCorsSetting v-model="portalCorsOrigins" :saving="saving" :disabled="!canEditPortal" :save="savePortalCorsSetting" />
212+
<el-switch
213+
:disabled="!canEditPortal || saving"
214+
:model-value="portalSetting.enable_cors"
215+
size="small"
216+
@change="handleAccessChange('enable_cors', $event)"
217+
/>
187218
</div>
188219
</div>
189220
</div>

‎ui/src/views/system/chat-management/portal-setting/components/ButtonEditPortal.vue‎

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { FormInstance, FormRules } from 'element-plus'
44
import type { PortalSetting } from '@/api/types'
55
import MkEditAvatar from '@/components/mk-edit-avatar/index.vue'
66
7-
const props = defineProps<{ setting: PortalSetting; saving: boolean; save: (payload: FormData) => Promise<void> }>()
7+
const props = defineProps<{ disabled: boolean; setting: PortalSetting; saving: boolean; save: (payload: FormData) => Promise<void> }>()
88
99
/* 门户名称与 Logo */
1010
const editVisible = ref(false)
@@ -17,6 +17,8 @@ const portalRules: FormRules = {
1717
}
1818
1919
function handleOpenPortalEdit() {
20+
if (props.disabled || props.saving) return
21+
2022
handleClosePortalEdit()
2123
portalForm.name = props.setting.name
2224
portalForm.logo = props.setting.logo || ''
@@ -31,9 +33,9 @@ function handleLogoChange(icon: string, file: File | null) {
3133
}
3234
3335
function handleSavePortalInfo() {
34-
if (props.saving) return
36+
if (props.disabled || props.saving) return
3537
portalFormRef.value?.validate((valid) => {
36-
if (!valid || props.saving) return
38+
if (!valid || props.disabled || props.saving) return
3739
const payload = new FormData()
3840
payload.append('name', portalForm.name.trim())
3941
if (logoChanged.value) payload.append('logo', logoFile.value || '')
@@ -54,14 +56,22 @@ function handleClosePortalEdit() {
5456

5557
<template>
5658
<!-- 编辑门户名称与 Logo -->
57-
<el-button text @click="handleOpenPortalEdit">
59+
<el-button text :disabled="disabled || saving" @click="handleOpenPortalEdit">
5860
<MkIcon name="icon_edit_outlined" />
5961
</el-button>
6062
<MkDialog v-model="editVisible" title="编辑" @closed="handleClosePortalEdit">
61-
<el-form ref="portalFormRef" :model="portalForm" :rules="portalRules" label-position="top" require-asterisk-position="right" @submit.prevent>
63+
<el-form
64+
v-loading="saving"
65+
ref="portalFormRef"
66+
:model="portalForm"
67+
:rules="portalRules"
68+
label-position="top"
69+
require-asterisk-position="right"
70+
@submit.prevent
71+
>
6272
<el-form-item label="名称" prop="name">
6373
<div class="flex-align-center w-full gap-3">
64-
<MkEditAvatar v-model="portalForm.logo" @change="handleLogoChange" :size="32">
74+
<MkEditAvatar :editable="!saving" v-model="portalForm.logo" @change="handleLogoChange" :size="32">
6575
<template #default="{ icon }">
6676
<PortalIcon :icon="icon" />
6777
</template>

‎ui/src/views/system/chat-management/portal-setting/components/ButtonPortalAuthSetting.vue‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import AuthSettingApi from '@/api/admin/system/settings/auth-setting'
66
import type { LoginMethod, PortalSetting, PortalSettingPayload } from '@/api/types'
77
import { LOGIN_METHOD_LABELS } from '@/constants/auth'
88
9-
const props = defineProps<{ setting: PortalSetting; saving: boolean; save: (payload: PortalSettingPayload) => Promise<void> }>()
9+
const props = defineProps<{ disabled: boolean; setting: PortalSetting; saving: boolean; save: (payload: PortalSettingPayload) => Promise<void> }>()
1010
1111
/* 登录方式与默认登录方式 */
1212
const loginMethodOptions = ref<string[]>([LOGIN_METHOD.LOCAL])
@@ -37,6 +37,8 @@ const authRules: FormRules<typeof authForm> = {
3737
3838
/* 抽屉回填与重置,草稿不直接修改门户配置 */
3939
function open(enableAfterSave = false) {
40+
if (props.disabled || props.saving) return
41+
4042
enableAuthAfterSave.value = enableAfterSave
4143
const config = props.setting.auth_config
4244
Object.assign(authForm, {
@@ -66,8 +68,10 @@ function handleClosed() {
6668
6769
/* 保存认证配置,仅提交后端读取的字段并保留其余配置 */
6870
function handleSaveAuthSetting() {
71+
if (props.disabled || props.saving || loginMethodsLoading.value) return
72+
6973
authFormRef.value?.validate((valid) => {
70-
if (!valid || props.saving || loginMethodsLoading.value) return
74+
if (!valid || props.disabled || props.saving || loginMethodsLoading.value) return
7175
return props
7276
.save({
7377
...(enableAuthAfterSave.value ? { enable_auth: true } : {}),
@@ -90,12 +94,12 @@ defineExpose({ open })
9094

9195
<template>
9296
<!-- 配置身份认证 -->
93-
<el-button text type="primary" title="身份认证设置" :disabled="saving" @click="handleOpenAuthSetting">
97+
<el-button text type="primary" title="身份认证设置" :disabled="disabled || saving" @click="handleOpenAuthSetting">
9498
<MkIcon name="icon_setting" />
9599
</el-button>
96100
<MkDrawer v-model="authVisible" title="身份认证设置" @closed="handleClosed">
97101
<el-form
98-
v-loading="loginMethodsLoading"
102+
v-loading="loginMethodsLoading || saving"
99103
ref="authFormRef"
100104
:model="authForm"
101105
:rules="authRules"

‎ui/src/views/system/chat-management/portal-setting/components/ButtonPortalCorsSetting.vue‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { ref } from 'vue'
33
4-
const props = defineProps<{ saving?: boolean; save: (origins: string[]) => Promise<void> }>()
4+
const props = defineProps<{ disabled: boolean; saving?: boolean; save: (origins: string[]) => Promise<void> }>()
55
const allowedOrigins = defineModel<string[]>({ default: () => [] })
66
77
/* 跨域地址草稿,取消不修改已保存配置。 */
@@ -11,6 +11,8 @@ const corsAddressPlaceholder =
1111
'请输入允许的跨域地址,开启后不输入跨域地址则不限制。\n跨域地址一行一个,如:\nhttp://127.0.0.1:5678\nhttps://dataease.io'
1212
1313
function handleOpenCorsSetting() {
14+
if (props.disabled || props.saving) return
15+
1416
corsAddressDraft.value = allowedOrigins.value.join('\n')
1517
corsVisible.value = true
1618
}
@@ -25,7 +27,7 @@ function parseCorsOrigins(value: string) {
2527
}
2628
2729
function handleSaveCorsSetting() {
28-
if (props.saving) return
30+
if (props.disabled || props.saving) return
2931
return props.save(parseCorsOrigins(corsAddressDraft.value)).then(() => {
3032
corsVisible.value = false
3133
})
@@ -38,7 +40,7 @@ function handleClosed() {
3840

3941
<template>
4042
<!-- 配置跨域地址 -->
41-
<el-button text type="primary" title="跨域设置" @click="handleOpenCorsSetting">
43+
<el-button text type="primary" title="跨域设置" :disabled="disabled || saving" @click="handleOpenCorsSetting">
4244
<MkIcon name="icon_setting" />
4345
</el-button>
4446
<MkDialog v-model="corsVisible" title="跨域设置" @closed="handleClosed">

‎ui/src/views/system/chat-management/portal-setting/components/PortalPreview.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ defineProps<{ name: string; logo: string | null }>()
2525
<el-avatar :size="32" class="shrink-0 bg-primary-gradient!">
2626
<img src="@/assets/mk_icon_user_gradient.svg" alt="" style="width: 54%" />
2727
</el-avatar>
28-
<span>用户名</span>
28+
<span>姓名</span>
2929
</div>
3030
</div>
3131
<div class="min-w-0 flex-1 p-6">

0 commit comments

Comments
 (0)