feat(系统设置): 参数设置-基础设置页面&部分组件调整
This commit is contained in:
parent
dd89cf0905
commit
411e2d1e5d
|
@ -6,11 +6,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, onBeforeMount } from 'vue';
|
||||||
import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
|
import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
|
||||||
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
||||||
import GlobalSetting from '@/components/pure/global-setting/index.vue';
|
import GlobalSetting from '@/components/pure/global-setting/index.vue';
|
||||||
import useLocale from '@/locale/useLocale';
|
import useLocale from '@/locale/useLocale';
|
||||||
|
import { saveBaseInfo, getBaseInfo } from '@/api/modules/setting/config';
|
||||||
|
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
|
||||||
|
|
||||||
const { currentLocale } = useLocale();
|
const { currentLocale } = useLocale();
|
||||||
const locale = computed(() => {
|
const locale = computed(() => {
|
||||||
|
@ -23,4 +25,25 @@
|
||||||
return zhCN;
|
return zhCN;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 项目初始化时需要获取基础设置信息,看当前站点 url是否为系统内置默认地址,如果是需要替换为当前项目部署的 url 地址
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
try {
|
||||||
|
const isInitUrl = getLocalStorage('isInitUrl'); // 是否已经初始化过 url
|
||||||
|
if (isInitUrl === 'true') return;
|
||||||
|
const res = await getBaseInfo();
|
||||||
|
if (res.url === 'http://127.0.0.1:8081') {
|
||||||
|
await saveBaseInfo([
|
||||||
|
{
|
||||||
|
paramKey: 'base.url',
|
||||||
|
paramValue: window.location.origin,
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setLocalStorage('isInitUrl', 'true'); // 设置已经初始化过 url,避免重复初始化
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import MSR from '@/api/http/index';
|
||||||
|
import {
|
||||||
|
TestEmailUrl,
|
||||||
|
SaveBaseInfoUrl,
|
||||||
|
SaveEmailInfoUrl,
|
||||||
|
GetBaseInfoUrl,
|
||||||
|
GetEmailInfoUrl,
|
||||||
|
} from '@/api/requrls/setting/config';
|
||||||
|
|
||||||
|
import type { SaveInfoParams, TestEmailParams, EmailConfig, BaseConfig } from '@/models/setting/config';
|
||||||
|
|
||||||
|
// 测试邮箱连接
|
||||||
|
export function testEmail(data: TestEmailParams) {
|
||||||
|
return MSR.post({ url: TestEmailUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存基础信息
|
||||||
|
export function saveBaseInfo(data: SaveInfoParams) {
|
||||||
|
return MSR.post({ url: SaveBaseInfoUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取基础信息
|
||||||
|
export function getBaseInfo() {
|
||||||
|
return MSR.get<BaseConfig>({ url: GetBaseInfoUrl });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存邮箱信息
|
||||||
|
export function saveEmailInfo(data: SaveInfoParams) {
|
||||||
|
return MSR.post({ url: SaveEmailInfoUrl, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取邮箱信息
|
||||||
|
export function getEmailInfo() {
|
||||||
|
return MSR.get<EmailConfig>({ url: GetEmailInfoUrl });
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
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';
|
|
@ -142,6 +142,12 @@
|
||||||
.btn-outline-primary-active();
|
.btn-outline-primary-active();
|
||||||
.btn-outline-primary-disabled();
|
.btn-outline-primary-disabled();
|
||||||
}
|
}
|
||||||
|
.arco-btn-outline--secondary {
|
||||||
|
.btn-outline-sec-default();
|
||||||
|
.btn-outline-sec-hover();
|
||||||
|
.btn-outline-sec-active();
|
||||||
|
.btn-outline-sec-disabled();
|
||||||
|
}
|
||||||
|
|
||||||
/** 输入框,选择器,文本域 **/
|
/** 输入框,选择器,文本域 **/
|
||||||
.arco-input-wrapper,
|
.arco-input-wrapper,
|
||||||
|
@ -452,7 +458,6 @@
|
||||||
|
|
||||||
/** 滚动条 **/
|
/** 滚动条 **/
|
||||||
.arco-scrollbar-track-direction-horizontal {
|
.arco-scrollbar-track-direction-horizontal {
|
||||||
margin-bottom: 4px;
|
|
||||||
height: 6px;
|
height: 6px;
|
||||||
.arco-scrollbar-thumb-bar {
|
.arco-scrollbar-thumb-bar {
|
||||||
@apply m-0;
|
@apply m-0;
|
||||||
|
|
|
@ -38,6 +38,11 @@
|
||||||
background-color: var(--color-text-n9) !important;
|
background-color: var(--color-text-n9) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.btn-outline-sec-default() {
|
||||||
|
border-color: var(--color-text-brand) !important;
|
||||||
|
color: var(--color-text-1) !important;
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
.btn-outline-sec-active() {
|
.btn-outline-sec-active() {
|
||||||
&:not(:disabled):active {
|
&:not(:disabled):active {
|
||||||
border-color: var(--color-text-brand) !important;
|
border-color: var(--color-text-brand) !important;
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<div class="mt-[4px] w-full text-[12px] text-[var(--color-text-4)]">
|
||||||
|
{{ props.text }}
|
||||||
|
<MsIcon
|
||||||
|
v-if="props.showFillIcon"
|
||||||
|
type="icon-icon_corner_right_up"
|
||||||
|
class="cursor-pointer text-[rgb(var(--primary-6))]"
|
||||||
|
@click="fillHeapByDefault"
|
||||||
|
></MsIcon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
text: string;
|
||||||
|
showFillIcon?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
showFillIcon: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const emit = defineEmits(['fill']);
|
||||||
|
|
||||||
|
function fillHeapByDefault() {
|
||||||
|
emit('fill');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -18,12 +18,14 @@
|
||||||
<a-scrollbar
|
<a-scrollbar
|
||||||
class="pr-[5px]"
|
class="pr-[5px]"
|
||||||
:style="{
|
:style="{
|
||||||
overflowY: 'auto',
|
overflow: 'auto',
|
||||||
minWidth: 1000,
|
width: `calc(100vw - ${menuWidth}px - 48px)`,
|
||||||
height: props.autoHeight ? 'auto' : `calc(100vh - ${cardOverHeight}px)`,
|
height: props.autoHeight ? 'auto' : `calc(100vh - ${cardOverHeight}px)`,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
<div class="min-w-[1000px]">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
</div>
|
||||||
</a-scrollbar>
|
</a-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -50,9 +52,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { computed } from 'vue';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<
|
defineProps<
|
||||||
|
@ -89,6 +92,12 @@
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const collapsedWidth = 86;
|
||||||
|
const menuWidth = computed(() => {
|
||||||
|
return appStore.menuCollapse ? collapsedWidth : appStore.menuWidth;
|
||||||
|
});
|
||||||
|
|
||||||
const _spcialHeight = props.hasBreadcrumb ? 31 + props.specialHeight : props.specialHeight; // 有面包屑的话,默认面包屑高度31
|
const _spcialHeight = props.hasBreadcrumb ? 31 + props.specialHeight : props.specialHeight; // 有面包屑的话,默认面包屑高度31
|
||||||
|
|
||||||
const cardOverHeight = computed(() => {
|
const cardOverHeight = computed(() => {
|
||||||
|
@ -100,7 +109,7 @@
|
||||||
// 隐藏底部
|
// 隐藏底部
|
||||||
return 192;
|
return 192;
|
||||||
}
|
}
|
||||||
return 246 + _spcialHeight;
|
return 256 + _spcialHeight;
|
||||||
});
|
});
|
||||||
|
|
||||||
function back() {
|
function back() {
|
||||||
|
@ -149,5 +158,8 @@
|
||||||
:deep(.arco-scrollbar-track-direction-vertical) {
|
:deep(.arco-scrollbar-track-direction-vertical) {
|
||||||
right: -10px;
|
right: -10px;
|
||||||
}
|
}
|
||||||
|
:deep(.arco-scrollbar-track-direction-horizontal) {
|
||||||
|
bottom: -10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
<a-skeleton-line :rows="props.skeletonLine" :line-height="24" />
|
<a-skeleton-line :rows="props.skeletonLine" :line-height="24" />
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-skeleton>
|
</a-skeleton>
|
||||||
<a-descriptions v-else :data="(props.descriptions as unknown as DescData[])" size="large" :column="1">
|
<a-descriptions v-else :data="(props.descriptions as unknown as DescData[])" size="large" :column="props.column">
|
||||||
|
<template #title>
|
||||||
|
<slot name="title"></slot>
|
||||||
|
</template>
|
||||||
<a-descriptions-item v-for="item of props.descriptions" :key="item.label" :label="item.label">
|
<a-descriptions-item v-for="item of props.descriptions" :key="item.label" :label="item.label">
|
||||||
<template v-if="item.isTag">
|
<template v-if="item.isTag">
|
||||||
<a-tag
|
<a-tag
|
||||||
|
@ -22,7 +25,7 @@
|
||||||
</template>
|
</template>
|
||||||
<a-button v-else-if="item.isButton" type="text" @click="handleItemClick(item)">{{ item.value }}</a-button>
|
<a-button v-else-if="item.isButton" type="text" @click="handleItemClick(item)">{{ item.value }}</a-button>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{{ item.value?.toString() === '' ? '-' : item.value }}
|
<slot name="value" :item="item">{{ item.value?.toString() === '' ? '-' : item.value }}</slot>
|
||||||
</div>
|
</div>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
|
@ -34,16 +37,23 @@
|
||||||
export interface Description {
|
export interface Description {
|
||||||
label: string;
|
label: string;
|
||||||
value: (string | number) | (string | number)[];
|
value: (string | number) | (string | number)[];
|
||||||
|
key?: string;
|
||||||
isTag?: boolean;
|
isTag?: boolean;
|
||||||
isButton?: boolean;
|
isButton?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
showSkeleton?: boolean;
|
showSkeleton?: boolean;
|
||||||
skeletonLine?: number;
|
skeletonLine?: number;
|
||||||
|
column?: number;
|
||||||
descriptions: Description[];
|
descriptions: Description[];
|
||||||
}>();
|
}>(),
|
||||||
|
{
|
||||||
|
column: 1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function handleItemClick(item: Description) {
|
function handleItemClick(item: Description) {
|
||||||
if (typeof item.onClick === 'function') {
|
if (typeof item.onClick === 'function') {
|
||||||
|
|
|
@ -4,10 +4,9 @@
|
||||||
:width="props.width"
|
:width="props.width"
|
||||||
:footer="props.footer"
|
:footer="props.footer"
|
||||||
:mask="props.mask"
|
:mask="props.mask"
|
||||||
:class="props.mask ? '' : 'ms-drawer-no-mask'"
|
:class="['ms-drawer', props.mask ? '' : 'ms-drawer-no-mask']"
|
||||||
@ok="handleOk"
|
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
@close="handleCancel"
|
@close="handleClose"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<slot name="title">
|
<slot name="title">
|
||||||
|
@ -18,19 +17,38 @@
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
|
<a-scrollbar
|
||||||
|
:style="{
|
||||||
|
overflowY: 'auto',
|
||||||
|
height: 'calc(100vh - 146px)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
<slot>
|
<slot>
|
||||||
<MsDescription
|
<MsDescription
|
||||||
v-if="props.descriptions?.length > 0 || showDescription"
|
v-if="props.descriptions && props.descriptions.length > 0"
|
||||||
:descriptions="props.descriptions"
|
:descriptions="props.descriptions"
|
||||||
:show-skeleton="props.showSkeleton"
|
:show-skeleton="props.showSkeleton"
|
||||||
:skeleton-line="10"
|
:skeleton-line="10"
|
||||||
></MsDescription>
|
></MsDescription>
|
||||||
</slot>
|
</slot>
|
||||||
|
</a-scrollbar>
|
||||||
|
<template #footer>
|
||||||
|
<slot name="footer">
|
||||||
|
<a-button :disabled="props.okLoading" @click="handleCancel">
|
||||||
|
{{ t(props.cancelText || 'ms.drawer.cancel') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" :loading="props.okLoading" @click="handleOk">
|
||||||
|
{{ t(props.okText || 'ms.drawer.ok') }}
|
||||||
|
</a-button>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, defineAsyncComponent } from 'vue';
|
import { ref, watch, defineAsyncComponent } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import type { Description } from '@/components/pure/ms-description/index.vue';
|
import type { Description } from '@/components/pure/ms-description/index.vue';
|
||||||
|
|
||||||
// 懒加载描述组件
|
// 懒加载描述组件
|
||||||
|
@ -44,9 +62,11 @@
|
||||||
descriptions?: Description[];
|
descriptions?: Description[];
|
||||||
footer?: boolean;
|
footer?: boolean;
|
||||||
mask?: boolean;
|
mask?: boolean;
|
||||||
showDescription?: boolean;
|
|
||||||
showSkeleton?: boolean;
|
showSkeleton?: boolean;
|
||||||
[key: string]: any;
|
okLoading?: boolean;
|
||||||
|
okText?: string;
|
||||||
|
cancelText?: string;
|
||||||
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<DrawerProps>(), {
|
const props = withDefaults(defineProps<DrawerProps>(), {
|
||||||
|
@ -54,7 +74,9 @@
|
||||||
mask: true,
|
mask: true,
|
||||||
showSkeleton: false,
|
showSkeleton: false,
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['update:visible']);
|
const emit = defineEmits(['update:visible', 'confirm', 'cancel']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const visible = ref(props.visible);
|
const visible = ref(props.visible);
|
||||||
|
|
||||||
|
@ -66,11 +88,18 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
visible.value = false;
|
emit('confirm');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
|
emit('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false;
|
||||||
|
emit('update:visible', false);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -92,6 +121,11 @@
|
||||||
border-bottom: 1px solid var(--color-text-n8);
|
border-bottom: 1px solid var(--color-text-n8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ms-drawer {
|
||||||
|
.arco-scrollbar-track-direction-vertical {
|
||||||
|
right: -12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.ms-drawer-no-mask {
|
.ms-drawer-no-mask {
|
||||||
left: auto;
|
left: auto;
|
||||||
.arco-drawer {
|
.arco-drawer {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export default {
|
||||||
|
'ms.drawer.cancel': 'Cancel',
|
||||||
|
'ms.drawer.ok': 'Confirm',
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
export default {
|
||||||
|
'ms.drawer.cancel': '取消',
|
||||||
|
'ms.drawer.ok': '确认',
|
||||||
|
};
|
|
@ -36,6 +36,7 @@ export default {
|
||||||
'menu.settings.system.resourcePool': 'Resource Pool',
|
'menu.settings.system.resourcePool': 'Resource Pool',
|
||||||
'menu.settings.system.resourcePoolDetail': 'Add resource pool',
|
'menu.settings.system.resourcePoolDetail': 'Add resource pool',
|
||||||
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
|
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
|
||||||
|
'menu.settings.system.parameter': 'System parameter',
|
||||||
'navbar.action.locale': 'Switch to English',
|
'navbar.action.locale': 'Switch to English',
|
||||||
...sys,
|
...sys,
|
||||||
...localeSettings,
|
...localeSettings,
|
||||||
|
|
|
@ -36,6 +36,7 @@ export default {
|
||||||
'menu.settings.system.resourcePool': '资源池',
|
'menu.settings.system.resourcePool': '资源池',
|
||||||
'menu.settings.system.resourcePoolDetail': '添加资源池',
|
'menu.settings.system.resourcePoolDetail': '添加资源池',
|
||||||
'menu.settings.system.resourcePoolEdit': '编辑资源池',
|
'menu.settings.system.resourcePoolEdit': '编辑资源池',
|
||||||
|
'menu.settings.system.parameter': '系统参数',
|
||||||
'navbar.action.locale': '切换为中文',
|
'navbar.action.locale': '切换为中文',
|
||||||
...sys,
|
...sys,
|
||||||
...localeSettings,
|
...localeSettings,
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 基础信息配置
|
||||||
|
export interface BaseConfig {
|
||||||
|
url: string;
|
||||||
|
prometheusHost: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 邮箱信息配置
|
||||||
|
export interface EmailConfig {
|
||||||
|
host: string; // 主机
|
||||||
|
port: string; // 端口
|
||||||
|
account: string; // 账户
|
||||||
|
from: string; // 发件人
|
||||||
|
password: string; // 密码
|
||||||
|
ssl: string;
|
||||||
|
tsl: string;
|
||||||
|
recipient: string; // 收件人
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParamItem {
|
||||||
|
paramKey: string; // 参数的 key
|
||||||
|
paramValue: string; // 参数的值
|
||||||
|
type: string; // 参数类型,一般是 string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存基础信息、邮箱信息接口入参
|
||||||
|
export type SaveInfoParams = ParamItem[];
|
||||||
|
|
||||||
|
// 测试邮箱连接接口入参
|
||||||
|
export interface TestEmailParams {
|
||||||
|
'smtp.host': string;
|
||||||
|
'smtp.port': string;
|
||||||
|
'smtp.account': string;
|
||||||
|
'smtp.password': string;
|
||||||
|
'smtp.from': string;
|
||||||
|
'smtp.ssl': string;
|
||||||
|
'smtp.tsl': string;
|
||||||
|
'smtp.recipient': string;
|
||||||
|
}
|
|
@ -89,6 +89,16 @@ const Setting: AppRouteRecordRaw = {
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'parameter',
|
||||||
|
name: 'settingSystemParameter',
|
||||||
|
component: () => import('@/views/setting/system/config/index.vue'),
|
||||||
|
meta: {
|
||||||
|
locale: 'menu.settings.system.parameter',
|
||||||
|
roles: ['*'],
|
||||||
|
isTopMenu: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -120,3 +120,16 @@ export function formatFileSize(fileSize: number): string {
|
||||||
|
|
||||||
return `${formattedSize} ${unit}`;
|
return `${formattedSize} ${unit}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串脱敏
|
||||||
|
* @param str 需要脱敏的字符串
|
||||||
|
* @returns 脱敏后的字符串
|
||||||
|
*/
|
||||||
|
export function desensitize(str: string): string {
|
||||||
|
if (!str || typeof str !== 'string') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.replace(/./g, '*');
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,491 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<MsCard class="mb-[16px]" :loading="baseloading" simple auto-height>
|
||||||
|
<div class="mb-[16px] flex justify-between">
|
||||||
|
<div class="text-[var(--color-text-000)]">{{ t('system.config.baseInfo') }}</div>
|
||||||
|
<a-button type="outline" size="mini" @click="baseInfoDrawerVisible = true">
|
||||||
|
{{ t('system.config.update') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<MsDescription :descriptions="baseInfoDescs" class="no-bottom" :column="2" />
|
||||||
|
</MsCard>
|
||||||
|
<MsCard class="mb-[16px]" :loading="emailLoading" simple auto-height>
|
||||||
|
<div class="mb-[16px] flex justify-between">
|
||||||
|
<div class="text-[var(--color-text-000)]">{{ t('system.config.emailConfig') }}</div>
|
||||||
|
<a-button type="outline" size="mini" @click="emailConfigDrawerVisible = true">
|
||||||
|
{{ t('system.config.update') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<MsDescription :descriptions="emailInfoDescs" :column="2">
|
||||||
|
<template #value="{ item }">
|
||||||
|
<div v-if="item.key && ['ssl', 'tsl'].includes(item.key)">
|
||||||
|
<div v-if="item.value === 'true'" class="flex items-center">
|
||||||
|
<icon-check-circle-fill class="mr-[8px] text-[rgb(var(--success-6))]" />{{
|
||||||
|
t('system.config.email.open')
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex items-center">
|
||||||
|
<MsIcon type="icon-icon_disable" class="mr-[4px] text-[var(--color-text-4)]" />
|
||||||
|
{{ t('system.config.email.close') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="item.key === 'password' && item.value?.toString() !== ''">
|
||||||
|
<span v-if="pswInVisible">
|
||||||
|
{{ item.value }}
|
||||||
|
<icon-eye class="cursor-pointer text-[var(--color-text-4)]" @click="togglePswVisible" />
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ desensitize(item.value as string) }}
|
||||||
|
<icon-eye-invisible class="cursor-pointer text-[var(--color-text-4)]" @click="togglePswVisible" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>{{ item.value?.toString() === '' ? '-' : item.value }}</div>
|
||||||
|
</template>
|
||||||
|
</MsDescription>
|
||||||
|
<a-button
|
||||||
|
type="outline"
|
||||||
|
size="mini"
|
||||||
|
class="arco-btn-outline--secondary"
|
||||||
|
:loading="testLoading"
|
||||||
|
@click="testLink('page')"
|
||||||
|
>
|
||||||
|
{{ t('system.config.email.test') }}
|
||||||
|
</a-button>
|
||||||
|
</MsCard>
|
||||||
|
<MsDrawer
|
||||||
|
v-model:visible="baseInfoDrawerVisible"
|
||||||
|
:title="t('system.config.baseInfo.updateTitle')"
|
||||||
|
:ok-text="t('system.config.baseInfo.update')"
|
||||||
|
:ok-loading="baseDrawerLoading"
|
||||||
|
:width="680"
|
||||||
|
@confirm="updateBaseInfo"
|
||||||
|
@cancel="baseInfoCancel"
|
||||||
|
>
|
||||||
|
<a-form ref="baseFormRef" :model="baseInfoForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('system.config.pageUrl')"
|
||||||
|
field="url"
|
||||||
|
asterisk-position="end"
|
||||||
|
:rules="[{ required: true, message: t('system.config.baseInfo.pageUrlRequired') }]"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="baseInfoForm.url"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.baseInfo.pageUrlPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
<MsFormItemSub :text="t('system.config.baseInfo.pageUrlSub', { url: defaulUrl })" @fill="fillDefaultUrl" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('system.config.prometheus')" field="prometheusHost" asterisk-position="end">
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="baseInfoForm.prometheusHost"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.baseInfo.prometheusPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
<MsFormItemSub
|
||||||
|
:text="t('system.config.baseInfo.prometheusSub', { prometheus: defaulPrometheus })"
|
||||||
|
@fill="fillDefaultPrometheus"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</MsDrawer>
|
||||||
|
<MsDrawer
|
||||||
|
v-model:visible="emailConfigDrawerVisible"
|
||||||
|
:title="t('system.config.email.updateTitle')"
|
||||||
|
:ok-text="t('system.config.email.update')"
|
||||||
|
:ok-loading="emailDrawerLoading"
|
||||||
|
:width="680"
|
||||||
|
@confirm="updateEmailConfig"
|
||||||
|
@cancel="emailConfigCancel"
|
||||||
|
>
|
||||||
|
<a-form ref="emailFormRef" :model="emailConfigForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('system.config.email.host')"
|
||||||
|
field="host"
|
||||||
|
asterisk-position="end"
|
||||||
|
:rules="[{ required: true, message: t('system.config.email.hostRequired') }]"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="emailConfigForm.host"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.email.hostPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('system.config.email.port')"
|
||||||
|
field="port"
|
||||||
|
asterisk-position="end"
|
||||||
|
:rules="[{ required: true, message: t('system.config.email.portRequired') }]"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="emailConfigForm.port"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.email.portPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('system.config.email.account')"
|
||||||
|
field="account"
|
||||||
|
asterisk-position="end"
|
||||||
|
:rules="[{ required: true, message: t('system.config.email.accountRequired') }, emailRule]"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="emailConfigForm.account"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.email.accountPlaceholder')"
|
||||||
|
autocomplete="off"
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('system.config.email.password')" field="password" asterisk-position="end">
|
||||||
|
<a-input-password
|
||||||
|
v-model:model-value="emailConfigForm.password"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.email.passwordPlaceholder')"
|
||||||
|
autocomplete="off"
|
||||||
|
allow-clear
|
||||||
|
></a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('system.config.email.from')" field="from" asterisk-position="end" :rules="[emailRule]">
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="emailConfigForm.from"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.email.fromPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('system.config.email.recipient')"
|
||||||
|
field="recipient"
|
||||||
|
asterisk-position="end"
|
||||||
|
:rules="[emailRule]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="emailConfigForm.recipient"
|
||||||
|
:max-length="250"
|
||||||
|
:placeholder="t('system.config.email.recipientPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('system.config.email.ssl')" field="ssl" asterisk-position="end">
|
||||||
|
<a-switch v-model:model-value="emailConfigForm.ssl" />
|
||||||
|
<MsFormItemSub :text="t('system.config.email.sslTip')" :show-fill-icon="false" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('system.config.email.tsl')" field="tsl" asterisk-position="end">
|
||||||
|
<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')">
|
||||||
|
{{ t('system.config.email.test') }}
|
||||||
|
</a-button>
|
||||||
|
</a-form>
|
||||||
|
</MsDrawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onBeforeMount, ref } from 'vue';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { desensitize } from '@/utils';
|
||||||
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
|
import MsFormItemSub from '@/components/bussiness/ms-form-item-sub/index.vue';
|
||||||
|
import { validateEmail } from '@/utils/validate';
|
||||||
|
import { testEmail, saveBaseInfo, saveEmailInfo, getBaseInfo, getEmailInfo } from '@/api/modules/setting/config';
|
||||||
|
|
||||||
|
import type { EmailConfig, TestEmailParams } from '@/models/setting/config';
|
||||||
|
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const baseloading = ref(false);
|
||||||
|
const baseDrawerLoading = ref(false);
|
||||||
|
const baseInfoDrawerVisible = ref(false);
|
||||||
|
const baseFormRef = ref<FormInstance>();
|
||||||
|
const baseInfo = ref({
|
||||||
|
url: 'http://127.0.0.1:8081',
|
||||||
|
prometheusHost: 'http://ms-prometheus:9090',
|
||||||
|
});
|
||||||
|
const baseInfoForm = ref({ ...baseInfo.value });
|
||||||
|
const baseInfoDescs = ref<Description[]>([]);
|
||||||
|
// 默认示例
|
||||||
|
const defaulUrl = 'https://metersphere.com';
|
||||||
|
const defaulPrometheus = 'http://ms-prometheus:9090';
|
||||||
|
|
||||||
|
function fillDefaultUrl() {
|
||||||
|
baseInfoForm.value.url = defaulUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillDefaultPrometheus() {
|
||||||
|
baseInfoForm.value.prometheusHost = defaulPrometheus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化基础信息
|
||||||
|
*/
|
||||||
|
async function initBaseInfo() {
|
||||||
|
try {
|
||||||
|
baseloading.value = true;
|
||||||
|
const res = await getBaseInfo();
|
||||||
|
baseInfo.value = { ...res };
|
||||||
|
baseInfoForm.value = { ...res };
|
||||||
|
baseInfoDescs.value = [
|
||||||
|
{
|
||||||
|
label: t('system.config.pageUrl'),
|
||||||
|
value: res.url,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.prometheus'),
|
||||||
|
value: res.prometheusHost,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
baseloading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接基础信息参数
|
||||||
|
*/
|
||||||
|
function makeBaseInfoParams() {
|
||||||
|
const { url, prometheusHost } = baseInfoForm.value;
|
||||||
|
return [
|
||||||
|
{ paramKey: 'base.url', paramValue: url, type: 'text' },
|
||||||
|
{ paramKey: 'base.prometheus.host', paramValue: prometheusHost, type: 'text' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存基础信息
|
||||||
|
*/
|
||||||
|
function updateBaseInfo() {
|
||||||
|
baseFormRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
baseDrawerLoading.value = true;
|
||||||
|
await saveBaseInfo(makeBaseInfoParams());
|
||||||
|
Message.success(t('system.config.baseInfo.updateSuccess'));
|
||||||
|
baseInfoDrawerVisible.value = false;
|
||||||
|
initBaseInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
baseDrawerLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function baseInfoCancel() {
|
||||||
|
baseFormRef.value?.resetFields();
|
||||||
|
baseInfoForm.value = { ...baseInfo.value };
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailLoading = ref(false);
|
||||||
|
const emailDrawerLoading = ref(false);
|
||||||
|
const testLoading = ref(false);
|
||||||
|
const drawerTestLoading = ref(false);
|
||||||
|
const emailConfigDrawerVisible = ref(false);
|
||||||
|
const emailConfig = ref({
|
||||||
|
host: '', // 主机
|
||||||
|
port: '', // 端口
|
||||||
|
account: '', // 账户
|
||||||
|
from: '', // 发件人
|
||||||
|
password: '', // 密码
|
||||||
|
ssl: false, // 是否开启ssl
|
||||||
|
tsl: false, // 是否开启tsl
|
||||||
|
recipient: '', // 收件人
|
||||||
|
});
|
||||||
|
const emailConfigForm = ref({ ...emailConfig.value });
|
||||||
|
const emailFormRef = ref<FormInstance>();
|
||||||
|
const emailInfoDescs = ref<Description[]>([]);
|
||||||
|
|
||||||
|
const pswInVisible = ref(false); // 是否展示未脱敏密码
|
||||||
|
|
||||||
|
function togglePswVisible() {
|
||||||
|
pswInVisible.value = !pswInVisible.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailRule = {
|
||||||
|
validator: (value: string | undefined, callback: (error?: string) => void) => {
|
||||||
|
if (value && !validateEmail(value)) {
|
||||||
|
callback(t('system.config.email.emailErrTip'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化邮箱信息
|
||||||
|
*/
|
||||||
|
async function initEmailInfo() {
|
||||||
|
try {
|
||||||
|
emailLoading.value = true;
|
||||||
|
const res = await getEmailInfo();
|
||||||
|
const _ssl = Boolean(res.ssl);
|
||||||
|
const _tsl = Boolean(res.tsl);
|
||||||
|
emailConfig.value = { ...res, ssl: _ssl, tsl: _tsl };
|
||||||
|
emailConfigForm.value = { ...res, ssl: _ssl, tsl: _tsl };
|
||||||
|
const { host, port, account, password, from, recipient, ssl, tsl } = res;
|
||||||
|
emailInfoDescs.value = [
|
||||||
|
{
|
||||||
|
label: t('system.config.email.host'),
|
||||||
|
value: host,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.email.port'),
|
||||||
|
value: port,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.email.account'),
|
||||||
|
value: account,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.email.password'),
|
||||||
|
value: password,
|
||||||
|
key: 'password',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.email.from'),
|
||||||
|
value: from,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.email.recipient'),
|
||||||
|
value: recipient,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.email.ssl'),
|
||||||
|
value: ssl,
|
||||||
|
key: 'ssl',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('system.config.email.tsl'),
|
||||||
|
value: tsl,
|
||||||
|
key: 'tsl',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
emailLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接邮箱信息参数
|
||||||
|
*/
|
||||||
|
function makeEmailParams() {
|
||||||
|
const { host, port, account, password, from, recipient, ssl, tsl } = emailConfigForm.value;
|
||||||
|
return [
|
||||||
|
{ paramKey: 'smtp.host', paramValue: host, type: 'text' },
|
||||||
|
{ paramKey: 'smtp.port', paramValue: port, type: 'text' },
|
||||||
|
{ paramKey: 'smtp.account', paramValue: account, type: 'text' },
|
||||||
|
{ paramKey: 'smtp.password', paramValue: password, type: 'text' },
|
||||||
|
{ paramKey: 'smtp.from', paramValue: from, type: 'text' },
|
||||||
|
{ paramKey: 'smtp.recipient', paramValue: recipient, type: 'text' },
|
||||||
|
{ paramKey: 'smtp.ssl', paramValue: ssl?.toString(), type: 'text' },
|
||||||
|
{ paramKey: 'smtp.tsl', paramValue: tsl?.toString(), type: 'text' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存邮箱信息
|
||||||
|
*/
|
||||||
|
function updateEmailConfig() {
|
||||||
|
emailFormRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
emailDrawerLoading.value = true;
|
||||||
|
await saveEmailInfo(makeEmailParams());
|
||||||
|
Message.success(t('system.config.email.updateSuccess'));
|
||||||
|
emailConfigDrawerVisible.value = false;
|
||||||
|
initEmailInfo();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
emailDrawerLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function emailConfigCancel() {
|
||||||
|
emailFormRef.value?.resetFields();
|
||||||
|
emailConfigForm.value = { ...emailConfig.value };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接测试邮箱参数
|
||||||
|
* @param form 抽屉中的表单/页面中的表单
|
||||||
|
*/
|
||||||
|
function makeEmailTestParams(form: EmailConfig) {
|
||||||
|
const { host, port, account, password, from, recipient, ssl, tsl } = form;
|
||||||
|
return {
|
||||||
|
'smtp.host': host,
|
||||||
|
'smtp.port': port,
|
||||||
|
'smtp.account': account,
|
||||||
|
'smtp.password': password,
|
||||||
|
'smtp.from': from,
|
||||||
|
'smtp.ssl': ssl,
|
||||||
|
'smtp.tsl': tsl,
|
||||||
|
'smtp.recipient': recipient,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试邮箱
|
||||||
|
* @param emailInfo 来源于抽屉/页面
|
||||||
|
*/
|
||||||
|
async function testLink(emailInfo: 'page' | 'drawer') {
|
||||||
|
try {
|
||||||
|
let params = {} as TestEmailParams;
|
||||||
|
if (emailInfo === 'drawer') {
|
||||||
|
drawerTestLoading.value = true;
|
||||||
|
params = makeEmailTestParams({
|
||||||
|
...emailConfigForm.value,
|
||||||
|
ssl: emailConfigForm.value.ssl?.toString(),
|
||||||
|
tsl: emailConfigForm.value.tsl?.toString(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
testLoading.value = true;
|
||||||
|
params = makeEmailTestParams({
|
||||||
|
...emailConfig.value,
|
||||||
|
ssl: emailConfig.value.ssl?.toString(),
|
||||||
|
tsl: emailConfig.value.tsl?.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await testEmail(params);
|
||||||
|
Message.success(t('system.config.email.testSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
testLoading.value = false;
|
||||||
|
drawerTestLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
initBaseInfo();
|
||||||
|
initEmailInfo();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.no-bottom) {
|
||||||
|
.arco-descriptions-item-label,
|
||||||
|
.arco-descriptions-item-value {
|
||||||
|
@apply pb-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<template>
|
||||||
|
<MsCard
|
||||||
|
class="mb-[16px]"
|
||||||
|
:title="t('system.config.parameterConfig')"
|
||||||
|
hide-back
|
||||||
|
hide-footer
|
||||||
|
auto-height
|
||||||
|
no-content-padding
|
||||||
|
>
|
||||||
|
<a-tabs v-model:active-key="activeTab" class="no-content">
|
||||||
|
<a-tab-pane key="baseConfig" :title="t('system.config.baseConfig')" />
|
||||||
|
<a-tab-pane key="pageConfig" :title="t('system.config.pageConfig')" />
|
||||||
|
<a-tab-pane key="authConfig" :title="t('system.config.authConfig')" />
|
||||||
|
</a-tabs>
|
||||||
|
</MsCard>
|
||||||
|
<baseConfig v-show="activeTab === 'baseConfig'" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import baseConfig from './components/baseConfig.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const activeTab = ref('baseConfig');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.no-content) {
|
||||||
|
.arco-tabs-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,6 @@
|
||||||
|
export default {
|
||||||
|
'system.config.parameterConfig': 'System Parameters Configuration',
|
||||||
|
'system.config.baseConfig': 'Basic Settings',
|
||||||
|
'system.config.pageConfig': 'Interface settings',
|
||||||
|
'system.config.authConfig': 'Authentication Settings',
|
||||||
|
};
|
|
@ -0,0 +1,48 @@
|
||||||
|
export default {
|
||||||
|
'system.config.parameterConfig': '系统参数配置',
|
||||||
|
'system.config.baseConfig': '基础设置',
|
||||||
|
'system.config.pageConfig': '界面设置',
|
||||||
|
'system.config.authConfig': '认证设置',
|
||||||
|
'system.config.baseInfo': '基本信息',
|
||||||
|
'system.config.update': '更新',
|
||||||
|
'system.config.pageUrl': '当前站点 URL',
|
||||||
|
'system.config.prometheus': 'Prometheus',
|
||||||
|
'system.config.emailConfig': '邮件设置',
|
||||||
|
'system.config.email.host': 'SMTP 主机',
|
||||||
|
'system.config.email.port': 'SMTP 端口',
|
||||||
|
'system.config.email.account': 'SMTP 账户',
|
||||||
|
'system.config.email.password': 'SMTP 密码',
|
||||||
|
'system.config.email.from': '指定发件人',
|
||||||
|
'system.config.email.recipient': '测试收件人',
|
||||||
|
'system.config.email.ssl': 'SSL',
|
||||||
|
'system.config.email.tsl': 'TSL',
|
||||||
|
'system.config.email.open': '开启',
|
||||||
|
'system.config.email.close': '关闭',
|
||||||
|
'system.config.email.test': '测试连接',
|
||||||
|
'system.config.baseInfo.updateTitle': '更新基本信息',
|
||||||
|
'system.config.baseInfo.update': '更新',
|
||||||
|
'system.config.baseInfo.updateSuccess': '更新成功',
|
||||||
|
'system.config.baseInfo.pageUrlSub': '例如:{url}',
|
||||||
|
'system.config.baseInfo.pageUrlRequired': '站点 URL 不能为空',
|
||||||
|
'system.config.baseInfo.pageUrlPlaceholder': '请输入当前站点 URL',
|
||||||
|
'system.config.baseInfo.prometheusSub': '例如:{prometheus}',
|
||||||
|
'system.config.baseInfo.prometheusRequired': 'prometheus 不能为空',
|
||||||
|
'system.config.baseInfo.prometheusPlaceholder': ' 请输入 prometheus',
|
||||||
|
'system.config.email.updateTitle': '更新邮件设置',
|
||||||
|
'system.config.email.update': '更新',
|
||||||
|
'system.config.email.hostRequired': 'SMTP 主机不能为空',
|
||||||
|
'system.config.email.hostPlaceholder': '请输入SMTP 主机地址',
|
||||||
|
'system.config.email.portRequired': 'SMTP 端口不能为空',
|
||||||
|
'system.config.email.portPlaceholder': '请输入SMTP 端口',
|
||||||
|
'system.config.email.accountRequired': 'SMTP 账户不能为空',
|
||||||
|
'system.config.email.accountPlaceholder': '请输入SMTP 账户',
|
||||||
|
'system.config.email.passwordRequired': 'SMTP 密码不能为空',
|
||||||
|
'system.config.email.passwordPlaceholder': '请输入SMTP 密码',
|
||||||
|
'system.config.email.fromPlaceholder': '请输入指定发件人邮箱',
|
||||||
|
'system.config.email.recipientPlaceholder': '请输入测试收件人邮箱',
|
||||||
|
'system.config.email.sslTip': '若 SMTP 端口是 465,需要启用 SSL',
|
||||||
|
'system.config.email.tslTip': '若 SMTP 端口是 587,需要启用 TSL',
|
||||||
|
'system.config.email.emailErrTip': '邮箱格式错误,请重新输入',
|
||||||
|
'system.config.email.updateSuccess': '更新成功',
|
||||||
|
'system.config.email.testSuccess': '邮箱连接成功',
|
||||||
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MsDrawer
|
<MsDrawer
|
||||||
v-model:visible="showScriptDrawer"
|
v-model:visible="showScriptDrawer"
|
||||||
width="680px"
|
:width="680"
|
||||||
:mask="false"
|
:mask="false"
|
||||||
:footer="false"
|
:footer="false"
|
||||||
:title="t('system.plugin.showScriptTitle', { name: props.config.title })"
|
:title="t('system.plugin.showScriptTitle', { name: props.config.title })"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MsDrawer
|
<MsDrawer
|
||||||
v-model:visible="showJobDrawer"
|
v-model:visible="showJobDrawer"
|
||||||
width="680px"
|
:width="680"
|
||||||
:title="t('system.resourcePool.customJobTemplate')"
|
:title="t('system.resourcePool.customJobTemplate')"
|
||||||
:footer="false"
|
:footer="false"
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
|
||||||
<MsCard :loading="loading" has-breadcrumb simple>
|
<MsCard :loading="loading" has-breadcrumb simple>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<a-button type="primary" @click="addPool">
|
<a-button type="primary" @click="addPool">
|
||||||
|
@ -40,7 +39,7 @@
|
||||||
</MsCard>
|
</MsCard>
|
||||||
<MsDrawer
|
<MsDrawer
|
||||||
v-model:visible="showDetailDrawer"
|
v-model:visible="showDetailDrawer"
|
||||||
width="480px"
|
:width="480"
|
||||||
:title="activePool?.name"
|
:title="activePool?.name"
|
||||||
:title-tag="activePool?.enable ? t('system.resourcePool.tableEnable') : t('system.resourcePool.tableDisable')"
|
:title-tag="activePool?.enable ? t('system.resourcePool.tableEnable') : t('system.resourcePool.tableDisable')"
|
||||||
:title-tag-color="activePool?.enable ? 'green' : 'gray'"
|
:title-tag-color="activePool?.enable ? 'green' : 'gray'"
|
||||||
|
@ -61,7 +60,6 @@
|
||||||
:default-val="activePool?.testResourceReturnDTO.jobDefinition || ''"
|
:default-val="activePool?.testResourceReturnDTO.jobDefinition || ''"
|
||||||
read-only
|
read-only
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -85,7 +83,6 @@
|
||||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
import type { ResourcePoolDetail } from '@/models/setting/resourcePool';
|
import type { ResourcePoolDetail } from '@/models/setting/resourcePool';
|
||||||
import { sleep } from '@/utils';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
Loading…
Reference in New Issue