feat(系统设置): 参数设置-基础设置页面&部分组件调整
This commit is contained in:
parent
dd89cf0905
commit
411e2d1e5d
|
@ -6,11 +6,13 @@
|
|||
</template>
|
||||
|
||||
<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 zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
|
||||
import GlobalSetting from '@/components/pure/global-setting/index.vue';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
import { saveBaseInfo, getBaseInfo } from '@/api/modules/setting/config';
|
||||
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
|
||||
|
||||
const { currentLocale } = useLocale();
|
||||
const locale = computed(() => {
|
||||
|
@ -23,4 +25,25 @@
|
|||
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>
|
||||
|
|
|
@ -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-disabled();
|
||||
}
|
||||
.arco-btn-outline--secondary {
|
||||
.btn-outline-sec-default();
|
||||
.btn-outline-sec-hover();
|
||||
.btn-outline-sec-active();
|
||||
.btn-outline-sec-disabled();
|
||||
}
|
||||
|
||||
/** 输入框,选择器,文本域 **/
|
||||
.arco-input-wrapper,
|
||||
|
@ -452,7 +458,6 @@
|
|||
|
||||
/** 滚动条 **/
|
||||
.arco-scrollbar-track-direction-horizontal {
|
||||
margin-bottom: 4px;
|
||||
height: 6px;
|
||||
.arco-scrollbar-thumb-bar {
|
||||
@apply m-0;
|
||||
|
|
|
@ -38,6 +38,11 @@
|
|||
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() {
|
||||
&:not(:disabled):active {
|
||||
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
|
||||
class="pr-[5px]"
|
||||
:style="{
|
||||
overflowY: 'auto',
|
||||
minWidth: 1000,
|
||||
overflow: 'auto',
|
||||
width: `calc(100vw - ${menuWidth}px - 48px)`,
|
||||
height: props.autoHeight ? 'auto' : `calc(100vh - ${cardOverHeight}px)`,
|
||||
}"
|
||||
>
|
||||
<slot></slot>
|
||||
<div class="min-w-[1000px]">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</a-scrollbar>
|
||||
</div>
|
||||
<div
|
||||
|
@ -50,9 +52,10 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { computed } from 'vue';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
|
@ -89,6 +92,12 @@
|
|||
const router = useRouter();
|
||||
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 cardOverHeight = computed(() => {
|
||||
|
@ -100,7 +109,7 @@
|
|||
// 隐藏底部
|
||||
return 192;
|
||||
}
|
||||
return 246 + _spcialHeight;
|
||||
return 256 + _spcialHeight;
|
||||
});
|
||||
|
||||
function back() {
|
||||
|
@ -149,5 +158,8 @@
|
|||
:deep(.arco-scrollbar-track-direction-vertical) {
|
||||
right: -10px;
|
||||
}
|
||||
:deep(.arco-scrollbar-track-direction-horizontal) {
|
||||
bottom: -10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
<a-skeleton-line :rows="props.skeletonLine" :line-height="24" />
|
||||
</a-space>
|
||||
</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">
|
||||
<template v-if="item.isTag">
|
||||
<a-tag
|
||||
|
@ -22,7 +25,7 @@
|
|||
</template>
|
||||
<a-button v-else-if="item.isButton" type="text" @click="handleItemClick(item)">{{ item.value }}</a-button>
|
||||
<div v-else>
|
||||
{{ item.value?.toString() === '' ? '-' : item.value }}
|
||||
<slot name="value" :item="item">{{ item.value?.toString() === '' ? '-' : item.value }}</slot>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
|
@ -34,16 +37,23 @@
|
|||
export interface Description {
|
||||
label: string;
|
||||
value: (string | number) | (string | number)[];
|
||||
key?: string;
|
||||
isTag?: boolean;
|
||||
isButton?: boolean;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
showSkeleton?: boolean;
|
||||
skeletonLine?: number;
|
||||
descriptions: Description[];
|
||||
}>();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
showSkeleton?: boolean;
|
||||
skeletonLine?: number;
|
||||
column?: number;
|
||||
descriptions: Description[];
|
||||
}>(),
|
||||
{
|
||||
column: 1,
|
||||
}
|
||||
);
|
||||
|
||||
function handleItemClick(item: Description) {
|
||||
if (typeof item.onClick === 'function') {
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
:width="props.width"
|
||||
:footer="props.footer"
|
||||
:mask="props.mask"
|
||||
:class="props.mask ? '' : 'ms-drawer-no-mask'"
|
||||
@ok="handleOk"
|
||||
:class="['ms-drawer', props.mask ? '' : 'ms-drawer-no-mask']"
|
||||
@cancel="handleCancel"
|
||||
@close="handleCancel"
|
||||
@close="handleClose"
|
||||
>
|
||||
<template #title>
|
||||
<slot name="title">
|
||||
|
@ -18,19 +17,38 @@
|
|||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
<slot>
|
||||
<MsDescription
|
||||
v-if="props.descriptions?.length > 0 || showDescription"
|
||||
:descriptions="props.descriptions"
|
||||
:show-skeleton="props.showSkeleton"
|
||||
:skeleton-line="10"
|
||||
></MsDescription>
|
||||
</slot>
|
||||
<a-scrollbar
|
||||
:style="{
|
||||
overflowY: 'auto',
|
||||
height: 'calc(100vh - 146px)',
|
||||
}"
|
||||
>
|
||||
<slot>
|
||||
<MsDescription
|
||||
v-if="props.descriptions && props.descriptions.length > 0"
|
||||
:descriptions="props.descriptions"
|
||||
:show-skeleton="props.showSkeleton"
|
||||
:skeleton-line="10"
|
||||
></MsDescription>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, defineAsyncComponent } from 'vue';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import type { Description } from '@/components/pure/ms-description/index.vue';
|
||||
|
||||
// 懒加载描述组件
|
||||
|
@ -44,9 +62,11 @@
|
|||
descriptions?: Description[];
|
||||
footer?: boolean;
|
||||
mask?: boolean;
|
||||
showDescription?: boolean;
|
||||
showSkeleton?: boolean;
|
||||
[key: string]: any;
|
||||
okLoading?: boolean;
|
||||
okText?: string;
|
||||
cancelText?: string;
|
||||
width: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<DrawerProps>(), {
|
||||
|
@ -54,7 +74,9 @@
|
|||
mask: true,
|
||||
showSkeleton: false,
|
||||
});
|
||||
const emit = defineEmits(['update:visible']);
|
||||
const emit = defineEmits(['update:visible', 'confirm', 'cancel']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const visible = ref(props.visible);
|
||||
|
||||
|
@ -66,11 +88,18 @@
|
|||
);
|
||||
|
||||
const handleOk = () => {
|
||||
visible.value = false;
|
||||
emit('confirm');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
emit('update:visible', false);
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false;
|
||||
emit('update:visible', false);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -92,6 +121,11 @@
|
|||
border-bottom: 1px solid var(--color-text-n8);
|
||||
}
|
||||
}
|
||||
.ms-drawer {
|
||||
.arco-scrollbar-track-direction-vertical {
|
||||
right: -12px;
|
||||
}
|
||||
}
|
||||
.ms-drawer-no-mask {
|
||||
left: auto;
|
||||
.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.resourcePoolDetail': 'Add resource pool',
|
||||
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
|
||||
'menu.settings.system.parameter': 'System parameter',
|
||||
'navbar.action.locale': 'Switch to English',
|
||||
...sys,
|
||||
...localeSettings,
|
||||
|
|
|
@ -36,6 +36,7 @@ export default {
|
|||
'menu.settings.system.resourcePool': '资源池',
|
||||
'menu.settings.system.resourcePoolDetail': '添加资源池',
|
||||
'menu.settings.system.resourcePoolEdit': '编辑资源池',
|
||||
'menu.settings.system.parameter': '系统参数',
|
||||
'navbar.action.locale': '切换为中文',
|
||||
...sys,
|
||||
...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,
|
||||
},
|
||||
},
|
||||
{
|
||||
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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串脱敏
|
||||
* @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>
|
||||
<MsDrawer
|
||||
v-model:visible="showScriptDrawer"
|
||||
width="680px"
|
||||
:width="680"
|
||||
:mask="false"
|
||||
:footer="false"
|
||||
:title="t('system.plugin.showScriptTitle', { name: props.config.title })"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<MsDrawer
|
||||
v-model:visible="showJobDrawer"
|
||||
width="680px"
|
||||
:width="680"
|
||||
:title="t('system.resourcePool.customJobTemplate')"
|
||||
:footer="false"
|
||||
@close="handleClose"
|
||||
|
|
|
@ -1,67 +1,65 @@
|
|||
<template>
|
||||
<div>
|
||||
<MsCard :loading="loading" has-breadcrumb simple>
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<a-button type="primary" @click="addPool">
|
||||
{{ t('system.resourcePool.createPool') }}
|
||||
</a-button>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('system.resourcePool.searchPool')"
|
||||
class="w-[230px]"
|
||||
allow-clear
|
||||
@search="searchPool"
|
||||
@press-enter="searchPool"
|
||||
></a-input-search>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
<a-button type="text" @click="showPoolDetail(record)">{{ record.name }}</a-button>
|
||||
</template>
|
||||
<template #enable="{ record }">
|
||||
<div v-if="record.enable" class="flex items-center">
|
||||
<icon-check-circle-fill class="mr-[2px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('system.resourcePool.tableEnable') }}
|
||||
</div>
|
||||
<div v-else class="flex items-center text-[var(--color-text-4)]">
|
||||
<icon-stop class="mr-[2px]" />
|
||||
{{ t('system.resourcePool.tableDisable') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<MsButton @click="editPool(record)">{{ t('system.resourcePool.editPool') }}</MsButton>
|
||||
<MsButton v-if="record.enable" @click="disabledPool(record)">{{
|
||||
t('system.resourcePool.tableDisable')
|
||||
}}</MsButton>
|
||||
<MsButton v-else @click="enablePool(record)">{{ t('system.resourcePool.tableEnable') }}</MsButton>
|
||||
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</MsCard>
|
||||
<MsDrawer
|
||||
v-model:visible="showDetailDrawer"
|
||||
width="480px"
|
||||
:title="activePool?.name"
|
||||
:title-tag="activePool?.enable ? t('system.resourcePool.tableEnable') : t('system.resourcePool.tableDisable')"
|
||||
:title-tag-color="activePool?.enable ? 'green' : 'gray'"
|
||||
:descriptions="activePoolDesc"
|
||||
:footer="false"
|
||||
:mask="false"
|
||||
:show-skeleton="drawerLoading"
|
||||
show-description
|
||||
>
|
||||
<template #tbutton>
|
||||
<a-button type="outline" size="mini" :disabled="drawerLoading" @click="editPool(activePool)">
|
||||
{{ t('system.resourcePool.editPool') }}
|
||||
</a-button>
|
||||
<MsCard :loading="loading" has-breadcrumb simple>
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<a-button type="primary" @click="addPool">
|
||||
{{ t('system.resourcePool.createPool') }}
|
||||
</a-button>
|
||||
<a-input-search
|
||||
v-model:model-value="keyword"
|
||||
:placeholder="t('system.resourcePool.searchPool')"
|
||||
class="w-[230px]"
|
||||
allow-clear
|
||||
@search="searchPool"
|
||||
@press-enter="searchPool"
|
||||
></a-input-search>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
<a-button type="text" @click="showPoolDetail(record)">{{ record.name }}</a-button>
|
||||
</template>
|
||||
</MsDrawer>
|
||||
<JobTemplateDrawer
|
||||
v-model:visible="showJobDrawer"
|
||||
:default-val="activePool?.testResourceReturnDTO.jobDefinition || ''"
|
||||
read-only
|
||||
/>
|
||||
</div>
|
||||
<template #enable="{ record }">
|
||||
<div v-if="record.enable" class="flex items-center">
|
||||
<icon-check-circle-fill class="mr-[2px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('system.resourcePool.tableEnable') }}
|
||||
</div>
|
||||
<div v-else class="flex items-center text-[var(--color-text-4)]">
|
||||
<icon-stop class="mr-[2px]" />
|
||||
{{ t('system.resourcePool.tableDisable') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<MsButton @click="editPool(record)">{{ t('system.resourcePool.editPool') }}</MsButton>
|
||||
<MsButton v-if="record.enable" @click="disabledPool(record)">{{
|
||||
t('system.resourcePool.tableDisable')
|
||||
}}</MsButton>
|
||||
<MsButton v-else @click="enablePool(record)">{{ t('system.resourcePool.tableEnable') }}</MsButton>
|
||||
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
</MsCard>
|
||||
<MsDrawer
|
||||
v-model:visible="showDetailDrawer"
|
||||
:width="480"
|
||||
:title="activePool?.name"
|
||||
:title-tag="activePool?.enable ? t('system.resourcePool.tableEnable') : t('system.resourcePool.tableDisable')"
|
||||
:title-tag-color="activePool?.enable ? 'green' : 'gray'"
|
||||
:descriptions="activePoolDesc"
|
||||
:footer="false"
|
||||
:mask="false"
|
||||
:show-skeleton="drawerLoading"
|
||||
show-description
|
||||
>
|
||||
<template #tbutton>
|
||||
<a-button type="outline" size="mini" :disabled="drawerLoading" @click="editPool(activePool)">
|
||||
{{ t('system.resourcePool.editPool') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</MsDrawer>
|
||||
<JobTemplateDrawer
|
||||
v-model:visible="showJobDrawer"
|
||||
:default-val="activePool?.testResourceReturnDTO.jobDefinition || ''"
|
||||
read-only
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -85,7 +83,6 @@
|
|||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import type { ResourcePoolDetail } from '@/models/setting/resourcePool';
|
||||
import { sleep } from '@/utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
|
|
Loading…
Reference in New Issue