feat(系统设置): 认证页面&部分组件调整

This commit is contained in:
baiqi 2023-08-08 16:28:44 +08:00 committed by 刘瑞斌
parent 4c11277304
commit 4d6d23c4ac
30 changed files with 675 additions and 162 deletions

View File

@ -15,6 +15,7 @@ export default mergeConfig(
'/app': {
target: 'http://172.16.200.18:8081/',
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/app/, ''),
},
},
},

View File

@ -14,7 +14,8 @@
import { saveBaseInfo, getBaseInfo } from '@/api/modules/setting/config';
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
import useAppStore from '@/store/modules/app';
import { watchStyle, watchTheme } from '@/utils/theme';
import { watchStyle, watchTheme, setFavicon } from '@/utils/theme';
import { GetPlatformIconUrl } from '@/api/requrls/setting/config';
const appStore = useAppStore();
@ -34,6 +35,7 @@
watchStyle(appStore.pageConfig.style, appStore.pageConfig);
watchTheme(appStore.pageConfig.theme, appStore.pageConfig);
window.document.title = appStore.pageConfig.title;
setFavicon(GetPlatformIconUrl);
onBeforeMount(async () => {
try {

View File

@ -10,7 +10,9 @@ import {
GetAuthListUrl,
GetAuthDetailUrl,
UpdateAuthUrl,
UpdateAuthStatusUrl,
AddAuthUrl,
DeleteAuthUrl,
} from '@/api/requrls/setting/config';
import { TableQueryParams } from '@/models/common';
@ -23,6 +25,7 @@ import type {
PageConfigReturns,
AuthItem,
AuthParams,
UpdateAuthStatusParams,
} from '@/models/setting/config';
// 测试邮箱连接
@ -67,7 +70,7 @@ export function getAuthList(data: TableQueryParams) {
// 获取认证源详情
export function getAuthDetail(id: string) {
return MSR.get<AuthItem>({ url: GetAuthDetailUrl, params: { id } });
return MSR.get<AuthItem>({ url: GetAuthDetailUrl, params: id });
}
// 添加认证源
@ -79,3 +82,13 @@ export function addAuth(data: AuthParams) {
export function updateAuth(data: AuthParams) {
return MSR.post({ url: UpdateAuthUrl, data });
}
// 更新认证源状态
export function updateAuthStatus(data: UpdateAuthStatusParams) {
return MSR.post({ url: UpdateAuthStatusUrl, data });
}
// 删除认证源
export function deleteAuth(id: string) {
return MSR.get({ url: DeleteAuthUrl, params: id });
}

View File

@ -7,6 +7,8 @@ import {
ImportUserUrl,
EnableUserUrl,
GetSystemRoleUrl,
ResetPasswordUrl,
BatchAddUserGroupUrl,
} from '@/api/requrls/setting/user';
import type {
UserListItem,
@ -17,6 +19,7 @@ import type {
ImportUserParams,
SystemRole,
ImportResult,
BatchAddUserGroupParams,
} from '@/models/setting/user';
import type { TableQueryParams } from '@/models/common';
@ -54,3 +57,13 @@ export function importUserInfo(data: ImportUserParams) {
export function getSystemRoles() {
return MSR.get<SystemRole>({ url: GetSystemRoleUrl });
}
// 重置用户密码
export function resetUserPassword(userIds: string[]) {
return MSR.post({ url: ResetPasswordUrl, data: userIds });
}
// 批量添加用户到多个用户组
export function batchAddUserGroup(data: BatchAddUserGroupParams) {
return MSR.post({ url: BatchAddUserGroupUrl, data });
}

View File

@ -1,11 +1,34 @@
// 测试邮件链接
export const TestEmailUrl = '/system/parameter/test/email';
// 保存基本信息
export const SaveBaseInfoUrl = '/system/parameter/save/base-info';
// 保存邮件配置
export const SaveEmailInfoUrl = '/system/parameter/edit/email-info';
// 获取邮件配置
export const GetEmailInfoUrl = '/system/parameter/get/email-info';
// 获取基础信息
export const GetBaseInfoUrl = '/system/parameter/get/base-info';
// 保存界面配置
export const SavePageConfigUrl = '/display/save';
// 获取界面配置
export const GetPageConfigUrl = '/display/info';
// 更新认证源
export const UpdateAuthUrl = '/system/authsource/update';
// 更新认证源状态
export const UpdateAuthStatusUrl = '/system/authsource/update/status';
// 获取认证源列表
export const GetAuthListUrl = '/system/authsource/list';
// 添加认证源
export const AddAuthUrl = '/system/authsource/add';
// 获取认证源详情
export const GetAuthDetailUrl = '/system/authsource/get';
// 删除认证源
export const DeleteAuthUrl = '/system/authsource/delete';
// 获取系统主页左上角图片
export const GetTitleImgUrl = '/display/get/title';
// 获取登录 logo
export const GetLoginLogoUrl = '/display/get/loginLogo';
// 获取登录大图
export const GetLoginImageUrl = '/display/get/loginImage';
// 获取平台标签图标
export const GetPlatformIconUrl = '/display/get/icon';

View File

@ -1,7 +1,18 @@
// 获取用户列表
export const GetUserListUrl = '/system/user/page';
// 添加用户
export const CreateUserUrl = '/system/user/add';
// 更新用户
export const UpdateUserUrl = '/system/user/update';
// 删除用户
export const DeleteUserUrl = '/system/user/delete';
// 导入用户
export const ImportUserUrl = '/system/user/import';
// 启用/禁用用户
export const EnableUserUrl = '/system/user/update/enable';
// 获取全局用户组列表
export const GetSystemRoleUrl = '/system/user/get/global/system/role';
// 重置用户密码
export const ResetPasswordUrl = '/system/user/reset/password';
// 批量添加用户到多个用户组
export const BatchAddUserGroupUrl = '/user/role/relation/global/add/batch';

View File

@ -81,3 +81,10 @@ body {
.one-line-text {
@apply overflow-hidden overflow-ellipsis whitespace-nowrap;
}
/* 修改进度条的背景颜色 */
#nprogress {
.bar {
background: rgb(var(--primary-6));
}
}

View File

@ -5,6 +5,7 @@
@border-radius-large: 12px;
@color-white: #fff;
@color-fill-2: rgb(var(--primary-9));
/** 常用颜色类 **/

View File

@ -1,6 +1,6 @@
<template>
<a-form ref="formRef" :model="form" layout="vertical">
<div class="mb-[16px] min-w-[732px] overflow-y-auto rounded-[4px] bg-[var(--color-fill-1)] p-[12px]">
<div class="mb-[16px] overflow-y-auto rounded-[4px] bg-[var(--color-fill-1)] p-[12px]">
<a-scrollbar class="overflow-y-auto" :style="{ 'max-height': props.maxHeight }">
<div class="flex flex-wrap items-start justify-between gap-[8px]">
<template v-for="(item, i) of form.list" :key="`form-item-${i}`">

View File

@ -16,7 +16,7 @@
@apply flex cursor-pointer items-center justify-center;
}
.menu-btn:not([disabled='true']):hover {
background-color: var(--color-fill-2);
background-color: rgb(var(--primary-9));
}
.menu-btn[disabled='true'] {
opacity: 0.7;
@ -105,7 +105,7 @@
}
}
button:hover {
background-color: var(--color-fill-2);
background-color: rgb(var(--primary-9));
}
& > div {
@apply flex h-full flex-wrap items-center justify-center;

View File

@ -1,5 +1,5 @@
<template>
<a-spin class="block h-full" :loading="props.loading" :size="28">
<a-spin class="!block h-full" :loading="props.loading" :size="28">
<div
:class="[
'ms-card',
@ -30,7 +30,8 @@
</div>
<div
v-if="!props.hideFooter && !props.simple"
class="fixed bottom-0 right-[16px] z-10 w-full bg-white p-[24px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
class="fixed bottom-0 right-[16px] z-10 bg-white p-[24px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
:style="{ width: `calc(100% - ${menuWidth + 16}px)` }"
>
<div class="ml-0 mr-auto">
<slot name="footerLeft"></slot>
@ -109,7 +110,7 @@
//
return 192;
}
return 256 + _spcialHeight;
return 246 + _spcialHeight;
});
function back() {

View File

@ -25,7 +25,9 @@
</template>
<a-button v-else-if="item.isButton" type="text" @click="handleItemClick(item)">{{ item.value }}</a-button>
<div v-else>
<slot name="value" :item="item">{{ item.value?.toString() === '' ? '-' : item.value }}</slot>
<slot name="value" :item="item">
{{ item.value === undefined || item.value === null || item.value?.toString() === '' ? '-' : item.value }}
</slot>
</div>
</a-descriptions-item>
</a-descriptions>

View File

@ -2,13 +2,10 @@
<div class="navbar">
<div class="left-side">
<a-space>
<template v-if="props.logo">
<div class="flex max-w-[145px] items-center overflow-hidden">
<img :src="props.logo" class="mr-[4px] h-[32px] w-[32px]" />
{{ props.name }}
</div>
</template>
<svg-icon v-else width="145px" height="32px" name="MS-full-logo" />
</a-space>
</div>
<div v-if="!props.isPreview" class="center-side">

View File

@ -11,6 +11,6 @@ export enum TableKeyEnum {
USERGROUPUSER = 'userGroupUser',
SYSTEM_USER = 'systemUser',
SYSTEM_RESOURCEPOOL = 'systemResourcePool',
SYSTEM_AUTH = 'systemResourcePool',
SYSTEM_AUTH = 'systemAuth',
ORGANNATIONMEMBER = 'member',
}

View File

@ -65,6 +65,7 @@
import usePermission from '@/hooks/usePermission';
import PageLayout from './page-layout.vue';
import MsBreadCrumb from '@/components/bussiness/ms-breadcrumb/index.vue';
import { GetTitleImgUrl } from '@/api/requrls/setting/config';
interface Props {
isPreview?: boolean;
@ -96,9 +97,7 @@
const route = useRoute();
const permission = usePermission();
const innerLogo = computed(() =>
props.isPreview ? innerProps.value.logo : appStore.pageConfig.logoPlatform[0]?.url
);
const innerLogo = computed(() => (props.isPreview ? innerProps.value.logo : GetTitleImgUrl));
const innerName = computed(() => (props.isPreview ? innerProps.value.name : appStore.pageConfig.platformName));
const navbarHeight = `56px`;

View File

@ -25,7 +25,7 @@ export default {
'menu.uiTest': 'UI test',
'menu.performanceTest': 'Performance test',
'menu.projectManagement': 'Project management',
'menu.settings': 'System Settings',
'menu.settings': 'Settings',
'menu.settings.system': 'System',
'menu.settings.organization': 'Organization',
'menu.settings.organization.member': 'Member',

View File

@ -89,6 +89,9 @@ export interface PageConfig extends ThemeConfig, LoginConfig, PlatformConfig {}
export type PageConfigKeys = keyof PageConfig;
// 认证源类型
export type AuthType = 'CAS' | 'LDAP' | 'OAuth2' | 'OIDC';
// 认证源配置列表项对象
export interface AuthItem {
id: string;
@ -97,7 +100,7 @@ export interface AuthItem {
updateTime: number;
description: string;
name: string;
type: string;
type: AuthType;
configuration: string;
}
@ -110,7 +113,7 @@ export interface AuthForm {
enable: boolean;
description: string;
name: string;
type: string;
type: AuthType;
configuration: Recordable;
}
@ -121,3 +124,9 @@ export type AuthParams = Omit<AuthForm, 'configuration'> & {
// 认证源配置详情对象
export type AuthDetail = AuthForm & Omit<AuthItem, 'configuration'>;
// 更新认证源状态入参
export interface UpdateAuthStatusParams {
id: string;
enable: boolean;
}

View File

@ -87,8 +87,8 @@ export interface DeleteUserParams {
export interface SystemRole {
id: string;
name: string;
selected: boolean;
closeable: boolean;
selected: boolean; // 是否可选
closeable: boolean; // 是否可取消
}
export interface ImportResult {
@ -96,3 +96,8 @@ export interface ImportResult {
successCount: number;
errorMessages: Record<string, any>;
}
export interface BatchAddUserGroupParams {
userIds: string[]; // 用户 id 集合
roleIds: string[]; // 用户组 id 集合
}

View File

@ -6,7 +6,6 @@ import { getSystemVersion } from '@/api/modules/system';
import { useI18n } from '@/hooks/useI18n';
import { cloneDeep } from 'lodash-es';
import { getPageConfig } from '@/api/modules/setting/config';
import { setFavicon } from '@/utils';
import { watchStyle, watchTheme } from '@/utils/theme';
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface';
@ -257,10 +256,6 @@ const useAppStore = defineStore('app', {
// 非自定义则需要重置自定义风格,避免本地缓存与接口配置不一致
this.pageConfig.customStyle = defaultThemeConfig.customStyle;
}
if (this.pageConfig.icon[0]?.url) {
// 设置网站 favicon
setFavicon(this.pageConfig.icon[0].url);
}
// 如果风格和主题有变化,则初始化一下主题和风格;没有变化则不需要在此初始化,在 App.vue 中初始化过了
if (hasStyleChange) {
watchStyle(this.pageConfig.style, this.pageConfig);

View File

@ -133,21 +133,3 @@ export function desensitize(str: string): string {
return str.replace(/./g, '*');
}
// 动态设置 favicon
export function setFavicon(url: string) {
const head = document.querySelector('head');
const link = document.createElement('link');
link.rel = 'shortcut icon';
link.href = url;
link.type = 'image/x-icon';
// 移除之前的 favicon
const oldFavicon = document.querySelector('link[rel="shortcut icon"]');
if (oldFavicon) {
head?.removeChild(oldFavicon);
}
// 添加新的 favicon
head?.appendChild(link);
}

View File

@ -132,3 +132,21 @@ export function watchTheme(val: Theme, pageConfig: PageConfig) {
}
}
}
// 动态设置 favicon
export function setFavicon(url: string) {
const head = document.querySelector('head');
const link = document.createElement('link');
link.rel = 'shortcut icon';
link.href = url;
link.type = 'image/x-icon';
// 移除之前的 favicon
const oldFavicon = document.querySelector('link[rel="shortcut icon"]');
if (oldFavicon) {
head?.removeChild(oldFavicon);
}
// 添加新的 favicon
head?.appendChild(link);
}

View File

@ -6,18 +6,15 @@
<script lang="ts" setup>
import { computed } from 'vue';
import useAppStore from '@/store/modules/app';
import { GetLoginImageUrl } from '@/api/requrls/setting/config';
const props = defineProps<{
isPreview?: boolean;
banner?: string;
}>();
const appStore = useAppStore();
const defaultBanner = `${import.meta.env.BASE_URL}images/login-banner.jpg`;
const innerBanner = computed(() => {
return props.banner || appStore.pageConfig.loginImage[0]?.url || defaultBanner;
return props.banner || GetLoginImageUrl;
});
</script>

View File

@ -2,8 +2,7 @@
<div class="login-form" :style="props.isPreview ? 'height: inherit' : 'height: 100vh'">
<div class="title">
<div class="flex justify-center">
<img v-if="innerLogo" :src="innerLogo" class="h-[60px] w-[290px]" />
<svg-icon v-else width="290px" height="60px" name="login-logo" />
<img :src="innerLogo" class="h-[60px] w-[290px]" />
</div>
<div class="title-0 mt-[16px] flex justify-center">
<span class="title-welcome">{{ innerSlogan || $t('login.form.title') }}</span>
@ -61,8 +60,10 @@
import { useStorage } from '@vueuse/core';
import { useUserStore, useAppStore } from '@/store';
import useLoading from '@/hooks/useLoading';
import type { LoginData } from '@/models/user';
import { setLoginExpires } from '@/utils/auth';
import { GetLoginLogoUrl } from '@/api/requrls/setting/config';
import type { LoginData } from '@/models/user';
const router = useRouter();
const { t } = useI18n();
@ -76,7 +77,7 @@
}>();
const innerLogo = computed(() => {
return props.isPreview ? props.logo : appStore.pageConfig.loginLogo[0]?.url;
return props.isPreview ? props.logo : GetLoginLogoUrl;
});
const innerSlogan = computed(() => {

View File

@ -32,7 +32,7 @@
</MsCard>
<MsDrawer
v-model:visible="showDetailDrawer"
:width="480"
:width="680"
:title="activeAuthDetail.name"
:title-tag="activeAuthDetail.enable ? t('system.config.auth.enable') : t('system.config.auth.disable')"
:title-tag-color="activeAuthDetail.enable ? 'green' : 'gray'"
@ -43,7 +43,7 @@
show-description
>
<template #tbutton>
<a-button type="outline" size="mini" :disabled="detailDrawerLoading" @click="editAuth(activeAuthDetail)">
<a-button type="outline" size="mini" :disabled="detailDrawerLoading" @click="editAuth(activeAuthDetail, true)">
{{ t('system.config.auth.edit') }}
</a-button>
</template>
@ -54,7 +54,7 @@
:ok-text="t(isEdit ? 'system.config.auth.update' : 'system.config.auth.drawerAdd')"
:ok-loading="drawerLoading"
:width="680"
show-continue
:show-continue="!isEdit"
@confirm="handleDrawerConfirm"
@continue="handleDrawerConfirm(true)"
@cancel="handleDrawerCancel"
@ -62,7 +62,7 @@
<a-form ref="authFormRef" :model="activeAuthForm" layout="vertical">
<a-form-item
:label="t('system.config.auth.name')"
field="url"
field="name"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.nameRequired') }]"
required
@ -74,7 +74,7 @@
allow-clear
></a-input>
</a-form-item>
<a-form-item :label="t('system.config.auth.desc')" field="url" asterisk-position="end">
<a-form-item :label="t('system.config.auth.desc')" field="description" asterisk-position="end">
<a-textarea
v-model:model-value="activeAuthForm.description"
:max-length="250"
@ -82,7 +82,7 @@
allow-clear
></a-textarea>
</a-form-item>
<a-form-item :label="t('system.config.auth.addResource')" field="url" asterisk-position="end">
<a-form-item :label="t('system.config.auth.addResource')" field="type" asterisk-position="end">
<a-radio-group v-model:model-value="activeAuthForm.type" type="button">
<a-radio v-for="item of authTypeList" :key="item" :value="item">{{ item }}</a-radio>
</a-radio-group>
@ -90,13 +90,13 @@
<template v-if="activeAuthForm.type === 'CAS'">
<a-form-item
:label="t('system.config.auth.serviceUrl')"
field="url"
field="configuration.casUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.serviceUrlRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.serviceUrl"
v-model:model-value="activeAuthForm.configuration.casUrl"
:max-length="250"
:placeholder="t('system.config.auth.serviceUrlPlaceholder')"
allow-clear
@ -104,7 +104,7 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.loginUrl')"
field="url"
field="configuration.loginUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.loginUrlRequired') }]"
required
@ -119,13 +119,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.callbackUrl')"
field="url"
field="configuration.redirectUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.callbackUrlRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.callbackUrl"
v-model:model-value="activeAuthForm.configuration.redirectUrl"
:max-length="250"
:placeholder="t('system.config.auth.callbackUrlPlaceholder')"
allow-clear
@ -133,13 +133,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.verifyUrl')"
field="url"
field="configuration.validateUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.verifyUrlRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.verifyUrl"
v-model:model-value="activeAuthForm.configuration.validateUrl"
:max-length="250"
:placeholder="t('system.config.auth.verifyUrlPlaceholder')"
allow-clear
@ -150,7 +150,7 @@
<template v-else-if="activeAuthForm.type === 'OIDC'">
<a-form-item
:label="t('system.config.auth.authUrl')"
field="url"
field="configuration.authUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.authUrlRequired') }]"
required
@ -164,7 +164,7 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.tokenUrl')"
field="url"
field="configuration.tokenUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.tokenUrlRequired') }]"
required
@ -178,7 +178,7 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.userInfoUrl')"
field="url"
field="configuration.userInfoUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.userInfoUrlRequired') }]"
required
@ -192,13 +192,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.callbackUrl')"
field="url"
field="configuration.redirectUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.callbackUrlRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.callbackUrl"
v-model:model-value="activeAuthForm.configuration.redirectUrl"
:max-length="250"
:placeholder="t('system.config.auth.OIDCCallbackUrlPlaceholder')"
allow-clear
@ -206,7 +206,7 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.clientId')"
field="url"
field="configuration.clientId"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.clientIdRequired') }]"
required
@ -220,13 +220,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.clientSecret')"
field="url"
field="configuration.secret"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.clientSecretRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.clientSecret"
v-model:model-value="activeAuthForm.configuration.secret"
:max-length="250"
:placeholder="t('system.config.auth.clientSecretPlaceholder')"
allow-clear
@ -234,19 +234,19 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.logoutSessionUrl')"
field="url"
field="configuration.logoutUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.logoutSessionUrlRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.logoutSessionUrl"
v-model:model-value="activeAuthForm.configuration.logoutUrl"
:max-length="250"
:placeholder="t('system.config.auth.logoutSessionUrlPlaceholder')"
allow-clear
></a-input>
</a-form-item>
<a-form-item :label="t('system.config.auth.loginUrl')" field="url" asterisk-position="end">
<a-form-item :label="t('system.config.auth.loginUrl')" field="configuration.loginUrl" asterisk-position="end">
<a-input
v-model:model-value="activeAuthForm.configuration.loginUrl"
:max-length="250"
@ -259,7 +259,7 @@
<template v-else-if="activeAuthForm.type === 'OAuth2'">
<a-form-item
:label="t('system.config.auth.authUrl')"
field="url"
field="configuration.authUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.authUrlRequired') }]"
required
@ -273,7 +273,7 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.tokenUrl')"
field="url"
field="configuration.tokenUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.tokenUrlRequired') }]"
required
@ -287,7 +287,7 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.userInfoUrl')"
field="url"
field="configuration.userInfoUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.userInfoUrlRequired') }]"
required
@ -301,13 +301,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.callbackUrl')"
field="url"
field="configuration.redirectUrl"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.callbackUrlRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.callbackUrl"
v-model:model-value="activeAuthForm.configuration.redirectUrl"
:max-length="250"
:placeholder="t('system.config.auth.OIDCCallbackUrlPlaceholder')"
allow-clear
@ -315,7 +315,7 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.clientId')"
field="url"
field="configuration.clientId"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.clientIdRequired') }]"
required
@ -329,13 +329,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.clientSecret')"
field="url"
field="configuration.secret"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.clientSecretRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.clientSecret"
v-model:model-value="activeAuthForm.configuration.secret"
:max-length="250"
:placeholder="t('system.config.auth.clientSecretPlaceholder')"
allow-clear
@ -343,35 +343,39 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.propertyMap')"
field="url"
field="configuration.mapping"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.propertyMapRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.propertyMap"
v-model:model-value="activeAuthForm.configuration.mapping"
:max-length="250"
:placeholder="t('system.config.auth.propertyMapPlaceholder')"
placeholder="{'userid':'login','username':'name','email':'email'}"
allow-clear
></a-input>
</a-form-item>
<a-form-item :label="t('system.config.auth.logoutSessionUrl')" field="url" asterisk-position="end">
<a-form-item
:label="t('system.config.auth.logoutSessionUrl')"
field="configuration.logoutUrl"
asterisk-position="end"
>
<a-input
v-model:model-value="activeAuthForm.configuration.logoutSessionUrl"
v-model:model-value="activeAuthForm.configuration.logoutUrl"
:max-length="250"
:placeholder="t('system.config.auth.logoutSessionUrlPlaceholder')"
allow-clear
></a-input>
</a-form-item>
<a-form-item :label="t('system.config.auth.linkRange')" field="url" asterisk-position="end">
<a-form-item :label="t('system.config.auth.linkRange')" field="configuration.scope" asterisk-position="end">
<a-input
v-model:model-value="activeAuthForm.configuration.linkRange"
v-model:model-value="activeAuthForm.configuration.scope"
:max-length="250"
:placeholder="t('system.config.auth.linkRangePlaceholder')"
allow-clear
></a-input>
</a-form-item>
<a-form-item :label="t('system.config.auth.loginUrl')" field="url" asterisk-position="end">
<a-form-item :label="t('system.config.auth.loginUrl')" field="configuration.loginUrl" asterisk-position="end">
<a-input
v-model:model-value="activeAuthForm.configuration.loginUrl"
:max-length="250"
@ -384,13 +388,13 @@
<template v-else-if="activeAuthForm.type === 'LDAP'">
<a-form-item
:label="t('system.config.auth.LDAPUrl')"
field="url"
field="configuration.url"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.LDAPUrlRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.LDAPUrl"
v-model:model-value="activeAuthForm.configuration.url"
:max-length="250"
:placeholder="t('system.config.auth.LDAPUrlPlaceholder')"
allow-clear
@ -399,41 +403,43 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.DN')"
field="url"
field="configuration.dn"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.DNRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.DN"
v-model:model-value="activeAuthForm.configuration.dn"
:max-length="250"
:placeholder="t('system.config.auth.DNPlaceholder')"
:input-attrs="{ autocomplete: 'off' }"
allow-clear
></a-input>
</a-form-item>
<a-form-item
:label="t('system.config.auth.password')"
field="url"
field="configuration.password"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.passwordRequired') }]"
required
>
<a-input
<a-input-password
v-model:model-value="activeAuthForm.configuration.password"
:max-length="250"
:placeholder="t('system.config.auth.passwordPlaceholder')"
:placeholder="t('system.config.auth.LDAPPasswordPlaceholder')"
:input-attrs="{ autocomplete: 'off' }"
allow-clear
></a-input>
></a-input-password>
</a-form-item>
<a-form-item
:label="t('system.config.auth.OU')"
field="url"
field="configuration.ou"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.OURequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.OU"
v-model:model-value="activeAuthForm.configuration.ou"
:max-length="250"
:placeholder="t('system.config.auth.OUPlaceholder')"
allow-clear
@ -442,13 +448,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.userFilter')"
field="url"
field="configuration.filter"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.userFilterRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.userFilter"
v-model:model-value="activeAuthForm.configuration.filter"
:max-length="250"
:placeholder="t('system.config.auth.userFilterPlaceholder')"
allow-clear
@ -457,13 +463,13 @@
</a-form-item>
<a-form-item
:label="t('system.config.auth.LDAPPropertyMap')"
field="url"
field="configuration.mapping"
asterisk-position="end"
:rules="[{ required: true, message: t('system.config.auth.LDAPPropertyMapRequired') }]"
required
>
<a-input
v-model:model-value="activeAuthForm.configuration.LDAPPropertyMap"
v-model:model-value="activeAuthForm.configuration.mapping"
:max-length="250"
:placeholder="t('system.config.auth.LDAPPropertyMapPlaceholder')"
allow-clear
@ -495,14 +501,22 @@
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import useModal from '@/hooks/useModal';
import { getAuthList, getAuthDetail, addAuth, updateAuth } from '@/api/modules/setting/config';
import {
getAuthList,
getAuthDetail,
addAuth,
updateAuth,
updateAuthStatus,
deleteAuth,
} from '@/api/modules/setting/config';
import MsFormItemSub from '@/components/bussiness/ms-form-item-sub/index.vue';
import { scrollIntoView } from '@/utils/dom';
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import type { AuthDetail, AuthForm, AuthItem } from '@/models/setting/config';
import type { AuthDetail, AuthForm, AuthItem, AuthType } from '@/models/setting/config';
import type { Description } from '@/components/pure/ms-description/index.vue';
const { t } = useI18n();
const { openModal } = useModal();
@ -572,7 +586,7 @@
*/
async function enableAuth(record: any) {
openModal({
type: 'warning',
type: 'info',
title: t('system.config.auth.enableTipTitle', { name: record.name }),
content: t('system.config.auth.enableTipContent'),
okText: t('system.config.auth.enableConfirm'),
@ -585,7 +599,10 @@
onBeforeOk: async () => {
try {
enableLoading.value = true;
// await togglePoolStatus(record.id);
await updateAuthStatus({
id: record.id,
enable: true,
});
Message.success(t('system.config.auth.enableSuccess'));
loadList();
return true;
@ -606,7 +623,7 @@
*/
function disabledAuth(record: any) {
openModal({
type: 'warning',
type: 'info',
title: t('system.config.auth.disableTipTitle', { name: record.name }),
content: t('system.config.auth.disableTipContent'),
okText: t('system.config.auth.disableConfirm'),
@ -619,7 +636,10 @@
onBeforeOk: async () => {
try {
disableLoading.value = true;
// await togglePoolStatus(record.id);
await updateAuthStatus({
id: record.id,
enable: false,
});
Message.success(t('system.config.auth.disableSuccess'));
loadList();
return true;
@ -638,7 +658,7 @@
/**
* 删除认证源
*/
function deleteAuth(record: any) {
function delAuth(record: any) {
openModal({
type: 'warning',
title: t('system.config.auth.deleteTipTitle', { name: record.name }),
@ -656,7 +676,7 @@
onBeforeOk: async () => {
try {
delLoading.value = true;
// await delPoolInfo(record.id);
await deleteAuth(record.id);
Message.success(t('system.config.auth.deleteSuccess'));
loadList();
return true;
@ -678,7 +698,7 @@
function handleSelect(item: ActionsItem, record: any) {
switch (item.eventTag) {
case 'delete':
deleteAuth(record);
delAuth(record);
break;
default:
break;
@ -687,13 +707,13 @@
const showDetailDrawer = ref(false);
const detailDrawerLoading = ref(false);
const activeAuthDesc = ref([]);
const activeAuthDesc = ref<Description[]>([]);
const activeAuthDetail = ref<AuthDetail>({
id: '',
enable: true,
description: '',
name: '',
type: '',
type: 'CAS',
updateTime: 0,
createTime: 0,
configuration: {},
@ -709,6 +729,142 @@
detailDrawerLoading.value = true;
const res = await getAuthDetail(record.id);
activeAuthDetail.value = { ...res, configuration: JSON.parse(res.configuration || '{}') };
const { configuration } = activeAuthDetail.value;
let description: Description[] = [
{
label: t('system.config.auth.desc'),
value: activeAuthDetail.value.description,
},
];
switch (res.type) {
case 'CAS':
description = description.concat([
{
label: t('system.config.auth.serviceUrl'),
value: configuration.casUrl,
},
{
label: t('system.config.auth.loginUrl'),
value: configuration.loginUrl,
},
{
label: t('system.config.auth.callbackUrl'),
value: configuration.redirectUrl,
},
{
label: t('system.config.auth.verifyUrl'),
value: configuration.validateUrl,
},
]);
break;
case 'OIDC':
description = description.concat([
{
label: t('system.config.auth.authUrl'),
value: configuration.authUrl,
},
{
label: t('system.config.auth.tokenUrl'),
value: configuration.tokenUrl,
},
{
label: t('system.config.auth.userInfoUrl'),
value: configuration.userInfoUrl,
},
{
label: t('system.config.auth.callbackUrl'),
value: configuration.redirectUrl,
},
{
label: t('system.config.auth.clientId'),
value: configuration.clientId,
},
{
label: t('system.config.auth.clientSecret'),
value: configuration.secret,
},
{
label: t('system.config.auth.logoutSessionUrl'),
value: configuration.logoutUrl,
},
{
label: t('system.config.auth.loginUrl'),
value: configuration.loginUrl,
},
]);
break;
case 'OAuth2':
description = description.concat([
{
label: t('system.config.auth.authUrl'),
value: configuration.authUrl,
},
{
label: t('system.config.auth.tokenUrl'),
value: configuration.tokenUrl,
},
{
label: t('system.config.auth.userInfoUrl'),
value: configuration.userInfoUrl,
},
{
label: t('system.config.auth.callbackUrl'),
value: configuration.redirectUrl,
},
{
label: t('system.config.auth.clientId'),
value: configuration.clientId,
},
{
label: t('system.config.auth.clientSecret'),
value: configuration.secret,
},
{
label: t('system.config.auth.logoutSessionUrl'),
value: configuration.logoutUrl,
},
{
label: t('system.config.auth.linkRange'),
value: configuration.scope,
},
{
label: t('system.config.auth.loginUrl'),
value: configuration.loginUrl,
},
]);
break;
case 'LDAP':
description = description.concat([
{
label: t('system.config.auth.LDAPUrl'),
value: configuration.url,
},
{
label: t('system.config.auth.DN'),
value: configuration.dn,
},
{
label: t('system.config.auth.password'),
value: configuration.password,
},
{
label: t('system.config.auth.OU'),
value: configuration.ou,
},
{
label: t('system.config.auth.userFilter'),
value: configuration.filter,
},
{
label: t('system.config.auth.LDAPPropertyMap'),
value: configuration.mapping,
},
]);
break;
default:
break;
}
activeAuthDesc.value = description;
} catch (error) {
console.log(error);
} finally {
@ -726,7 +882,7 @@
enable: true,
description: '',
name: '',
type: 'CAS',
type: 'CAS' as AuthType,
configuration: {},
};
const activeAuthForm = ref<AuthForm>({
@ -737,15 +893,31 @@
/**
* 编辑认证源
* @param record 表格项
* @param isFromDetail 是否从详情抽屉打开编辑
*/
function editAuth(record: AuthItem | AuthDetail) {
async function editAuth(record: AuthItem | AuthDetail, isFromDetail = false) {
if (isFromDetail) {
drawerTitle.value = t('system.config.auth.update');
showDrawer.value = true;
activeAuthForm.value = { ...record } as AuthDetail;
showDetailDrawer.value = false;
return;
}
try {
drawerTitle.value = t('system.config.auth.update');
showDrawer.value = true;
drawerLoading.value = true;
const res = await getAuthDetail(record.id);
activeAuthForm.value = {
...record,
...res,
configuration:
typeof record.configuration === 'string' ? JSON.parse(record.configuration || '{}') : record.configuration,
typeof res.configuration === 'string' ? JSON.parse(res.configuration || '{}') : res.configuration,
};
} catch (error) {
console.log(error);
} finally {
drawerLoading.value = false;
}
}
/**
@ -767,13 +939,64 @@
async function saveAuth(isContinue: boolean) {
try {
drawerLoading.value = true;
const { configuration } = activeAuthForm.value;
let _configuration = {};
switch (activeAuthForm.value.type) {
case 'CAS':
_configuration = {
casUrl: configuration.casUrl,
loginUrl: configuration.loginUrl,
redirectUrl: configuration.redirectUrl,
validateUrl: configuration.validateUrl,
};
break;
case 'OIDC':
_configuration = {
authUrl: configuration.authUrl,
tokenUrl: configuration.tokenUrl,
userInfoUrl: configuration.userInfoUrl,
redirectUrl: configuration.redirectUrl,
clientId: configuration.clientId,
secret: configuration.secret,
logoutUrl: configuration.logoutUrl,
loginUrl: configuration.loginUrl,
};
break;
case 'OAuth2':
_configuration = {
authUrl: configuration.authUrl,
tokenUrl: configuration.tokenUrl,
userInfoUrl: configuration.userInfoUrl,
redirectUrl: configuration.redirectUrl,
clientId: configuration.clientId,
secret: configuration.secret,
mapping: configuration.mapping,
logoutUrl: configuration.logoutUrl,
scope: configuration.scope,
loginUrl: configuration.loginUrl,
};
break;
case 'LDAP':
_configuration = {
url: configuration.url,
dn: configuration.dn,
password: configuration.password,
ou: configuration.ou,
filter: configuration.filter,
mapping: configuration.mapping,
};
break;
default:
break;
}
const params = {
...activeAuthForm.value,
configuration: JSON.stringify(activeAuthForm.value.configuration),
configuration: JSON.stringify(_configuration),
};
if (isEdit.value) {
await updateAuth(params);
Message.success(t('system.config.auth.updateSuccess'));
showDrawer.value = false;
} else {
await addAuth(params);
Message.success(t('system.config.auth.addSuccess'));
@ -783,6 +1006,7 @@
showDrawer.value = false;
}
}
loadList();
} catch (error) {
console.log(error);
} finally {
@ -790,6 +1014,10 @@
}
}
/**
* 处理抽屉确认
* @param isContinue 是否继续添加
*/
function handleDrawerConfirm(isContinue: boolean) {
authFormRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
if (!errors) {

View File

@ -182,10 +182,10 @@
<a-switch v-model:model-value="emailConfigForm.tsl" />
<MsFormItemSub :text="t('system.config.email.tslTip')" :show-fill-icon="false" />
</a-form-item>
<a-button type="outline" class="w-[88px]" :loading="drawerTestLoading" @click="testLink('drawer')">
</a-form>
<a-button type="outline" class="flex-1" :loading="drawerTestLoading" @click="testLink('drawer')">
{{ t('system.config.email.test') }}
</a-button>
</a-form>
</MsDrawer>
</div>
</template>

View File

@ -10,7 +10,7 @@
</div>
<a-radio-group v-model:model-value="pageConfig.theme" type="button" class="mb-[4px]">
<a-radio v-for="item of themeList" :key="item.value" :value="item.value">
{{ item.label }}
{{ t(item.label) }}
</a-radio>
</a-radio-group>
<div v-if="pageConfig.theme === 'custom'" class="ml-[4px]">
@ -24,7 +24,7 @@
</div>
<a-radio-group v-model:model-value="pageConfig.style" type="button" class="mb-[4px]">
<a-radio v-for="item of styleList" :key="item.value" :value="item.value">
{{ item.label }}
{{ t(item.label) }}
</a-radio>
</a-radio-group>
<div v-if="pageConfig.style === 'custom'" class="mb-[4px] ml-[4px]">
@ -42,7 +42,7 @@
<MsButton class="!leading-none" @click="resetLoginPageConfig">{{ t('system.config.page.reset') }}</MsButton>
</div>
<!-- 登录页预览盒子 -->
<div class="config-preview">
<div :class="['config-preview', currentLocale === 'en-US' ? 'config-preview--en' : '']">
<div ref="loginPageFullRef" class="login-preview">
<div :class="['config-preview-head', isLoginPageFullscreen ? 'full-preview-head' : '']">
<div class="flex items-center justify-between">
@ -194,7 +194,7 @@
<MsButton class="!leading-none" @click="resetPlatformConfig">{{ t('system.config.page.reset') }}</MsButton>
</div>
<!-- 平台主页预览盒子 -->
<div class="config-preview !h-[290px]">
<div :class="['config-preview', '!h-[290px]', currentLocale === 'en-US' ? '!h-[340px]' : '']">
<div ref="platformPageFullRef" class="login-preview">
<div
class="absolute right-[18px] top-[16px] z-[999] w-[96px] cursor-pointer text-right !text-[var(--color-text-4)]"
@ -302,6 +302,7 @@
import { useFullscreen } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
import { useI18n } from '@/hooks/useI18n';
import useLocale from '@/locale/useLocale';
import MsCard from '@/components/pure/ms-card/index.vue';
import MsColorSelect from '@/components/pure/ms-color-select/index.vue';
import MsButton from '@/components/pure/ms-button/index.vue';
@ -313,12 +314,14 @@
import defaultLayout from '@/layout/default-layout.vue';
import { scrollIntoView } from '@/utils/dom';
import { setCustomTheme, setPlatformColor, watchStyle, watchTheme } from '@/utils/theme';
import { sleep } from '@/utils';
import { savePageConfig } from '@/api/modules/setting/config';
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
const defaultBanner = `${import.meta.env.BASE_URL}images/login-banner.jpg`;
const { t } = useI18n();
const { currentLocale } = useLocale();
const appStore = useAppStore();
const collapsedWidth = 86;
const menuWidth = computed(() => {
@ -336,26 +339,26 @@
const styleList = [
{
label: t('system.config.page.default'),
label: 'system.config.page.default',
value: 'default',
},
{
label: t('system.config.page.follow'),
label: 'system.config.page.follow',
value: 'follow',
},
{
label: t('system.config.page.custom'),
label: 'system.config.page.custom',
value: 'custom',
},
];
const themeList = [
{
label: t('system.config.page.default'),
label: 'system.config.page.default',
value: 'default',
},
{
label: t('system.config.page.custom'),
label: 'system.config.page.custom',
value: 'custom',
},
];
@ -424,7 +427,7 @@
paramValue: pageConfig.value.icon[0]?.url,
type: 'file',
fileName: pageConfig.value.icon[0]?.name,
isDefault: pageConfig.value.icon.length === 0, //
original: pageConfig.value.icon.length === 0, //
hasFile: pageConfig.value.icon[0]?.file, //
},
{
@ -432,7 +435,7 @@
paramValue: pageConfig.value.loginLogo[0]?.url,
type: 'file',
fileName: pageConfig.value.loginLogo[0]?.name,
isDefault: pageConfig.value.loginLogo.length === 0,
original: pageConfig.value.loginLogo.length === 0,
hasFile: pageConfig.value.loginLogo[0]?.file,
},
{
@ -440,7 +443,7 @@
paramValue: pageConfig.value.loginImage[0]?.url,
type: 'file',
fileName: pageConfig.value.loginImage[0]?.name,
isDefault: pageConfig.value.loginImage.length === 0,
original: pageConfig.value.loginImage.length === 0,
hasFile: pageConfig.value.loginImage[0]?.file,
},
{
@ -448,7 +451,7 @@
paramValue: pageConfig.value.logoPlatform[0]?.url,
type: 'file',
fileName: pageConfig.value.logoPlatform[0]?.name,
isDefault: pageConfig.value.logoPlatform.length === 0,
original: pageConfig.value.logoPlatform.length === 0,
hasFile: pageConfig.value.logoPlatform[0]?.file,
},
{ paramKey: 'ui.slogan', paramValue: pageConfig.value.slogan, type: 'text' },
@ -467,7 +470,7 @@
{ paramKey: 'ui.platformName', paramValue: pageConfig.value.platformName, type: 'text' },
].filter((e) => {
if (e.type === 'file') {
return e.hasFile || e.isDefault;
return e.hasFile || e.original;
}
return true;
});
@ -507,8 +510,8 @@
pageloading.value = true;
await savePageConfig(makeParams());
Message.success(t('system.config.page.saveSuccess'));
appStore.initPageConfig(); //
isSave.value = true;
await sleep(300);
window.location.reload();
} catch (error) {
console.log(error);
} finally {
@ -543,14 +546,16 @@
if (isSave.value === false) {
//
if (
pageConfig.value.style !== appStore.pageConfig.style &&
pageConfig.value.customStyle !== appStore.pageConfig.style
pageConfig.value.style === 'custom'
? pageConfig.value.customStyle !== appStore.pageConfig.style
: pageConfig.value.style !== appStore.pageConfig.style
) {
watchStyle(appStore.pageConfig.style, appStore.pageConfig);
}
if (
pageConfig.value.theme !== appStore.pageConfig.theme &&
pageConfig.value.customTheme !== appStore.pageConfig.theme
pageConfig.value.theme === 'custom'
? pageConfig.value.customTheme !== appStore.pageConfig.theme
: pageConfig.value.theme !== appStore.pageConfig.theme
) {
watchTheme(appStore.pageConfig.theme, appStore.pageConfig);
}
@ -576,8 +581,14 @@
@apply relative flex items-start overflow-hidden;
height: 495px;
&--en {
height: 595px;
}
@media screen and (min-width: 1600px) {
height: 550px;
&--en {
height: 570px;
}
}
@media screen and (min-width: 1800px) {
height: auto;
@ -599,7 +610,7 @@
width: 740px;
@media screen and (min-width: 1600px) {
width: 882px;
width: 888px;
}
@media screen and (min-width: 1800px) {
width: 100%;
@ -637,6 +648,7 @@
}
.config-form {
margin-left: 12px;
width: 40%;
.config-form-card {
@apply bg-white;

View File

@ -3,4 +3,197 @@ export default {
'system.config.baseConfig': 'Basic Settings',
'system.config.pageConfig': 'Interface settings',
'system.config.authConfig': 'Authentication Settings',
'system.config.baseInfo': 'Base Information',
'system.config.update': 'Update',
'system.config.pageUrl': 'Current site URL',
'system.config.prometheus': 'Prometheus',
'system.config.emailConfig': 'E-Mail settings',
'system.config.email.host': 'SMTP host',
'system.config.email.port': 'SMTP port',
'system.config.email.account': 'SMTP account',
'system.config.email.password': 'SMTP password',
'system.config.email.from': 'Specified sender',
'system.config.email.recipient': 'Test recipient',
'system.config.email.ssl': 'SSL',
'system.config.email.tsl': 'TSL',
'system.config.email.open': 'Open',
'system.config.email.close': 'Close',
'system.config.email.test': 'Test connection',
'system.config.baseInfo.updateTitle': 'Update basic info',
'system.config.baseInfo.update': 'Update',
'system.config.baseInfo.updateSuccess': 'Updated successfully',
'system.config.baseInfo.pageUrlSub': 'eg: {url}',
'system.config.baseInfo.pageUrlRequired': 'Site URL cannot be empty',
'system.config.baseInfo.pageUrlPlaceholder': 'Please enter current site URL',
'system.config.baseInfo.prometheusSub': 'eg: {prometheus}',
'system.config.baseInfo.prometheusRequired': 'Prometheus cannot be empty',
'system.config.baseInfo.prometheusPlaceholder': 'Please enter prometheus',
'system.config.email.updateTitle': 'Update mail settings',
'system.config.email.update': 'Update',
'system.config.email.hostRequired': 'SMTP host cannot be empty',
'system.config.email.hostPlaceholder': 'Please enter the SMTP host address',
'system.config.email.portRequired': 'SMTP port cannot be empty',
'system.config.email.portPlaceholder': 'Please enter the SMTP port',
'system.config.email.accountRequired': 'SMTP account cannot be empty',
'system.config.email.accountPlaceholder': 'Please enter SMTP account',
'system.config.email.passwordRequired': 'SMTP password cannot be empty',
'system.config.email.passwordPlaceholder': 'Please enter SMTP password',
'system.config.email.fromPlaceholder': 'Please enter the designated sender email',
'system.config.email.recipientPlaceholder': 'Please enter the email address of the test recipient',
'system.config.email.sslTip': 'If the SMTP port is 465, SSL needs to be enabled',
'system.config.email.tslTip': 'If the SMTP port is 587, TSL needs to be enabled',
'system.config.email.emailErrTip': 'Email format error, please re-enter',
'system.config.email.updateSuccess': 'Updated successfully',
'system.config.email.testSuccess': 'Email connection is successful',
'system.config.page.style': 'Platform style',
'system.config.page.styleTip': 'Platform style refers to the page background color style',
'system.config.page.theme': 'Platform theme color',
'system.config.page.themeTip':
'The platform theme color refers to the purple color of MeterSphere except the page background color',
'system.config.page.default': 'Default',
'system.config.page.follow': 'Follow theme',
'system.config.page.custom': 'Customize',
'system.config.page.loginPageConfig': 'Platform login page settings',
'system.config.page.pagePreview': 'Page preview',
'system.config.page.reset': 'Reset',
'system.config.page.icon': 'Website icon',
'system.config.page.replace': 'Replace',
'system.config.page.iconTip':
'The icon displayed on the top website; it is recommended to use a transparent background image in SVG or PNG format, with a width and height of 18px; the image size is only supported within 200 KB',
'system.config.page.loginLogo': 'Login logo',
'system.config.page.loginLogoTip':
'The logo on the right side of the login page; it is recommended to use a transparent background image in SVG or PNG format, with a height of no less than 48px; the image size is only supported within 200 KB',
'system.config.page.loginBg': 'Login background image',
'system.config.page.loginBgTip':
'It is recommended to use the SVG format for the background image; the recommended size for vector graphics is 800*900, and the recommended size for bitmaps is 800*900; the size of the image only supports within 1 MB',
'system.config.page.slogan': 'Slogan',
'system.config.page.sloganPlaceholder': 'Please enter Slogan',
'system.config.page.sloganRquired': 'Slogan cannot be empty',
'system.config.page.sloganTip': 'The slogan under the product logo',
'system.config.page.title': 'Website name',
'system.config.page.titlePlaceholder': 'Please enter a website name',
'system.config.page.titleTip': 'The platform name displayed on the web page tab',
'system.config.page.loginPreviewTip':
'tips: The default is the MeterSphere system interface, which supports custom platform interface settings',
'system.config.page.platformConfig': 'Platform settings',
'system.config.page.platformLogo': 'Platform logo',
'system.config.page.platformLogoTip':
'The logo displayed at the top of the platform page; it is recommended to use a transparent background image in SVG or PNG format, with a height of not less than 32px; the image size is only supported within 200 KB',
'system.config.page.platformName': 'Platform name',
'system.config.page.platformNamePlaceholder': 'Please enter a platform name',
'system.config.page.platformNameRquired': 'Platform name cannot be empty',
'system.config.page.platformNameTip':
'The general product name of the whole site, the recommended number of words is 8',
'system.config.page.helpDoc': 'Help document',
'system.config.page.helpDocPlaceholder': 'Please enter the address of the help document',
'system.config.page.helpDocTip': 'Help document jump link can be set, the default is official help document',
'system.config.page.platformConfigTip':
'tips: The default is the MeterSphere system interface, which supports custom platform interface settings',
'system.config.page.resetAll': 'Reset',
'system.config.page.save': 'Save & Apply',
'system.config.page.unsave': 'Unsaved',
'system.config.page.saveSuccess': 'Saved successfully',
'system.config.auth.add': 'Add authentication',
'system.config.auth.enable': 'Enable',
'system.config.auth.enableSuccess': 'Enabled successfully',
'system.config.auth.enableTipTitle': 'Enable authentication {name}',
'system.config.auth.enableTipContent': 'After enabling it, you can log in through this authentication method',
'system.config.auth.enableConfirm': 'Confirm enable',
'system.config.auth.disable': 'Disable',
'system.config.auth.disableSuccess': 'Disabled successfully',
'system.config.auth.disableTipTitle': 'Disable authentication {name}',
'system.config.auth.disableTipContent': 'After disabling, this authentication method is not supported for login',
'system.config.auth.disableConfirm': 'Confirm disable',
'system.config.auth.updateSuccess': 'Updated successfully',
'system.config.auth.addSuccess': 'Added successfully',
'system.config.auth.cancel': 'Cancel',
'system.config.auth.edit': 'Edit',
'system.config.auth.name': 'Name',
'system.config.auth.status': 'Status',
'system.config.auth.desc': 'Description',
'system.config.auth.createTime': 'Creation time',
'system.config.auth.updateTime': 'Update time',
'system.config.auth.action': 'Action',
'system.config.auth.delete': 'Delete',
'system.config.auth.deleteTipTitle': 'Are you sure you want to delete the certification for {name}?',
'system.config.auth.deleteTipContent': 'It cannot be restored after deletion, please operate with caution!',
'system.config.auth.deleteConfirm': 'Confirm delete',
'system.config.auth.deleteSuccess': 'Deleted successfully',
'system.config.auth.updateTitle': 'Update certification',
'system.config.auth.drawerAdd': 'Add',
'system.config.auth.update': 'Update',
'system.config.auth.nameRequired': 'Authentication source name cannot be empty',
'system.config.auth.namePlaceholder': 'Please enter the authentication source name',
'system.config.auth.descPlaceholder': 'Please describe the certification source',
'system.config.auth.addResource': 'Add resource',
'system.config.auth.serviceUrl': 'Server address',
'system.config.auth.serviceUrlRequired': 'Server address cannot be empty',
'system.config.auth.serviceUrlPlaceholder': 'eg: http://<casurl>',
'system.config.auth.loginUrl': 'Login address',
'system.config.auth.loginUrlRequired': 'Login address cannot be empty',
'system.config.auth.loginUrlPlaceholder': 'eg: http://<casurl>/login',
'system.config.auth.loginUrlTip': 'When authentication fails, redirect to this login page',
'system.config.auth.verifyUrl': 'Verify address',
'system.config.auth.verifyUrlRequired': 'Verification address cannot be empty',
'system.config.auth.verifyUrlPlaceholder': 'eg: http://<casurl>/serviceValidate',
'system.config.auth.verifyUrlTip': 'The information used to verify the login is correct',
'system.config.auth.callbackUrl': 'Callback address',
'system.config.auth.callbackUrlRequired': 'Callback address cannot be empty',
// eslint-disable-next-line no-template-curly-in-string
'system.config.auth.callbackUrlPlaceholder': 'eg: http://<meteresphere-endpoint>/sso/callback/cas/suthld',
'system.config.auth.OIDCCallbackUrlPlaceholder':
// eslint-disable-next-line no-template-curly-in-string
'eg: http://<metersphere-endpoint>/sso/callback or http://<metersphere-endpoint>/sso/callback/authld',
'system.config.auth.authUrl': 'Authorized end address',
'system.config.auth.authUrlRequired': 'Authorization end address cannot be empty',
'system.config.auth.authUrlPlaceholder':
'eg: http://<keyclock>auth/realms/<metersphere>/protocol/openid-connect/auth',
'system.config.auth.tokenUrl': 'Token endpoint address',
'system.config.auth.tokenUrlRequired': 'Token endpoint address cannot be empty',
'system.config.auth.tokenUrlPlaceholder':
'eg: http://<keyclock>auth/realms/<metersphere>/protocol/openid-connect/token',
'system.config.auth.userInfoUrl': 'User information endpoint address',
'system.config.auth.userInfoUrlRequired': 'User information endpoint address cannot be empty',
'system.config.auth.userInfoUrlPlaceholder':
'eg: http://<keyclock>auth/realms/<metersphere>/protocol/openid-connect/userinfo',
'system.config.auth.clientId': 'Client ID',
'system.config.auth.clientIdRequired': 'Client ID cannot be empty',
'system.config.auth.clientIdPlaceholder': 'eg: metersphere',
'system.config.auth.clientSecret': 'Client secret',
'system.config.auth.clientSecretRequired': 'Client secret cannot be empty',
'system.config.auth.clientSecretPlaceholder': 'OIDC client secret',
'system.config.auth.logoutSessionUrl': 'Logout session address',
'system.config.auth.logoutSessionUrlRequired': 'Logout session address cannot be empty',
'system.config.auth.logoutSessionUrlPlaceholder':
'eg: http://<keyclock>/auth/realms/<metersphere>/protocol/openid-connect/logout',
'system.config.auth.password': 'Password',
'system.config.auth.passwordRequired': 'Password cannot be empty',
'system.config.auth.passwordPlaceholder': 'OIDC client secret',
'system.config.auth.LDAPPasswordPlaceholder': 'Please enter',
'system.config.auth.linkRange': 'Connection range',
'system.config.auth.linkRangePlaceholder': 'openid profile email',
'system.config.auth.propertyMap': 'Property map',
'system.config.auth.propertyMapRequired': 'Property map cannot be empty',
'system.config.auth.LDAPUrl': 'LDAP address',
'system.config.auth.LDAPUrlRequired': 'LDAP address cannot be empty',
'system.config.auth.LDAPUrlPlaceholder': 'Please enter',
'system.config.auth.LDAPUrlTip': 'Set server address',
'system.config.auth.DN': 'Bind DN',
'system.config.auth.DNRequired': 'DN cannot be empty',
'system.config.auth.DNPlaceholder': 'Please enter',
'system.config.auth.OU': 'User OU',
'system.config.auth.OURequired': 'OU cannot be empty',
'system.config.auth.OUPlaceholder': 'Please enter',
'system.config.auth.OUTip': 'Multiple OUs are separated by "I"',
'system.config.auth.userFilter': 'User filter',
'system.config.auth.userFilterRequired': 'User filter cannot be empty',
'system.config.auth.userFilterPlaceholder': 'Please enter',
'system.config.auth.userFilterTip': 'cn or uid or sAMAccountName=%(user)s',
'system.config.auth.LDAPPropertyMap': 'LDAP attribute mapping',
'system.config.auth.LDAPPropertyMapRequired': 'LDAP attribute map cannot be empty',
'system.config.auth.LDAPPropertyMapPlaceholder': 'Please enter',
'system.config.auth.LDAPPropertyMapTip':
'The left key is the MeterSphere user attribute, and the right value is the authentication platform user attribute',
'system.config.auth.testLink': 'Test connection',
'system.config.auth.testLogin': 'Test login',
};

View File

@ -171,7 +171,6 @@ export default {
'system.config.auth.linkRangePlaceholder': 'openid profile email',
'system.config.auth.propertyMap': '属性映射',
'system.config.auth.propertyMapRequired': '属性映射不能为空',
'system.config.auth.propertyMapPlaceholder': '{"userid":"login","username":"name","email":"email"}',
'system.config.auth.LDAPUrl': 'LDAP 地址',
'system.config.auth.LDAPUrlRequired': 'LDAP 地址不能为空',
'system.config.auth.LDAPUrlPlaceholder': '请输入',

View File

@ -125,7 +125,7 @@
title: 'system.resourcePool.tableColunmActions',
slotName: 'action',
fixed: 'right',
width: 120,
width: 140,
showInTable: true,
},
];

View File

@ -192,9 +192,11 @@
class="text-[rgb(var(--primary-5))]"
:href="importErrorFileUrl"
:download="`${t('system.user.importErrorFile')}.pdf`"
>{{ t('system.user.importResultContentDownload') }}</a-link
>{{ t('system.user.importResultContentSubEnd') }}</div
>
{{ t('system.user.importResultContentDownload') }}
</a-link>
{{ t('system.user.importResultContentSubEnd') }}
</div>
</template>
<template #footer>
<a-button type="text" class="!text-[var(--color-text-1)]" @click="cancelImport">
@ -233,6 +235,7 @@
deleteUserInfo,
importUserInfo,
getSystemRoles,
resetUserPassword,
} from '@/api/modules/setting/user';
import { validateEmail, validatePhone } from '@/utils/validate';
import batchModal from './components/batchModal.vue';
@ -289,7 +292,7 @@
title: 'system.user.tableColunmActions',
slotName: 'action',
fixed: 'right',
width: 90,
width: 120,
showInTable: true,
},
];
@ -337,6 +340,7 @@
cancelText: t('system.user.resetPswCancel'),
onBeforeOk: async () => {
try {
await resetUserPassword(userIdList);
Message.success(t('system.user.resetPswSuccess'));
return true;
} catch (error) {