mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-05-05 03:21:27 +08:00
【同步】前端项目源码
【修复】工作流兼容问题
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { NCard, NSpace, NDescriptions, NDescriptionsItem, NIcon, NButton } from 'naive-ui'
|
||||
import { $t } from '@locales/index'
|
||||
import { LogoGithub } from '@vicons/ionicons5'
|
||||
/**
|
||||
* 关于我们标签页组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'AboutSettings',
|
||||
setup() {
|
||||
return () => (
|
||||
<div class="about-settings">
|
||||
<NCard title={$t('t_4_1745833932780')} class="mb-4">
|
||||
<NSpace vertical size={24}>
|
||||
<NDescriptions bordered>
|
||||
<NDescriptionsItem label={$t('t_5_1745833933241')}>
|
||||
<div class="flex items-center">
|
||||
<span class="text-[2rem] font-medium">v1.0.0</span>
|
||||
</div>
|
||||
</NDescriptionsItem>
|
||||
<NDescriptionsItem label={$t('t_29_1746667589773')}>
|
||||
<div class="flex items-center space-x-2 h-[3.2rem]">
|
||||
<NIcon size="20" class="text-gray-600">
|
||||
<LogoGithub />
|
||||
</NIcon>
|
||||
<NButton text tag="a" href="https://github.com/allinssl/allinssl" target="_blank" type="primary">
|
||||
https://github.com/allinssl/allinssl
|
||||
</NButton>
|
||||
</div>
|
||||
</NDescriptionsItem>
|
||||
</NDescriptions>
|
||||
</NSpace>
|
||||
</NCard>
|
||||
|
||||
<NCard title={$t('t_13_1745833933630')} class="mb-4">
|
||||
<div class="about-content">
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<p class="text-[3rem] font-medium">ALLinSSL</p>
|
||||
<br />
|
||||
<p class="text-[1.6rem] text-primary mb-[2rem]">{$t('t_35_1746773362992')}</p>
|
||||
<span class="text-[1.4rem] mb-[1rem] text-gray-500">
|
||||
{$t(
|
||||
'本工具可帮助用户轻松管理多个网站的SSL证书,提供自动化的证书申请、更新和部署流程,并实时监控证书状态,确保网站安全持续运行。',
|
||||
)}
|
||||
<ul class="list-disc pl-[2rem] mt-[2rem]">
|
||||
<li class="mb-[1rem]">
|
||||
<span class="text-[1.4rem]">{$t('t_36_1746773348989')}</span>
|
||||
{$t('t_1_1746773763643')}
|
||||
</li>
|
||||
<li class="mb-[1rem]">
|
||||
<span class="text-[1.4rem]">{$t('t_38_1746773349796')}</span>
|
||||
{$t('t_39_1746773358932')}
|
||||
</li>
|
||||
<li class="mb-[1rem]">
|
||||
<span class="text-[1.4rem]">{$t('t_40_1746773352188')}</span>
|
||||
{$t('t_41_1746773364475')}
|
||||
</li>
|
||||
<li class="mb-[1rem]">
|
||||
<span class="text-[1.4rem]">{$t('t_42_1746773348768')}</span>
|
||||
{$t('t_43_1746773359511')}
|
||||
</li>
|
||||
<li class="mb-[1rem]">
|
||||
<span class="text-[1.4rem]">{$t('t_44_1746773352805')}</span>
|
||||
{$t('t_45_1746773355717')}
|
||||
</li>
|
||||
<li class="mb-[1rem]">
|
||||
<span class="text-[1.4rem]">{$t('t_46_1746773350579')}</span>
|
||||
{$t('t_47_1746773360760')}
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</NCard>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,106 @@
|
||||
import { NGrid, NFormItemGi, NInput, NSwitch } from 'naive-ui'
|
||||
import { useForm, useModalHooks } from '@baota/naive-ui/hooks'
|
||||
import { useError } from '@baota/hooks/error'
|
||||
import { $t } from '@locales/index'
|
||||
import { useEmailChannelFormController } from '../useController'
|
||||
import { useStore } from '../useStore'
|
||||
|
||||
import type { ReportMail, ReportType } from '@/types/setting'
|
||||
|
||||
/**
|
||||
* 邮箱通知渠道表单组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'EmailChannelForm',
|
||||
props: {
|
||||
data: {
|
||||
type: Object as PropType<ReportType<ReportMail> | null>,
|
||||
default: () => null,
|
||||
},
|
||||
},
|
||||
setup(props: { data: ReportType<ReportMail> | null }) {
|
||||
const { handleError } = useError()
|
||||
const { confirm } = useModalHooks()
|
||||
const { fetchNotifyChannels } = useStore()
|
||||
const { config, rules, emailChannelForm, submitForm } = useEmailChannelFormController()
|
||||
|
||||
if (props.data) {
|
||||
const { name, config } = props.data
|
||||
emailChannelForm.value = {
|
||||
name,
|
||||
...config,
|
||||
}
|
||||
}
|
||||
// 使用表单hooks
|
||||
const {
|
||||
component: EmailForm,
|
||||
example,
|
||||
data,
|
||||
} = useForm({
|
||||
config,
|
||||
defaultValue: emailChannelForm,
|
||||
rules,
|
||||
})
|
||||
|
||||
// 关联确认按钮
|
||||
confirm(async (close) => {
|
||||
try {
|
||||
const { name, ...other } = data.value
|
||||
await example.value?.validate()
|
||||
const res = await submitForm(
|
||||
{
|
||||
type: 'mail',
|
||||
name: name || '',
|
||||
config: other,
|
||||
},
|
||||
example,
|
||||
props.data?.id,
|
||||
)
|
||||
|
||||
fetchNotifyChannels()
|
||||
if (res) close()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div class="email-channel-form">
|
||||
<EmailForm
|
||||
labelPlacement="top"
|
||||
v-slots={{
|
||||
'smtp-template': (formData: Ref<ReportMail>) => {
|
||||
return (
|
||||
<NGrid cols="24" xGap="24">
|
||||
<NFormItemGi span="12" label={$t('t_14_1745833932440')} path="smtpHost">
|
||||
<NInput v-model:value={formData.value.smtpHost} placeholder={$t('t_15_1745833940280')} />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="7" label={$t('t_16_1745833933819')} path="smtpPort">
|
||||
<NInput v-model:value={formData.value.smtpPort} placeholder={$t('t_17_1745833935070')} />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="5" label={$t('t_18_1745833933989')} path="smtpTLS">
|
||||
<NSwitch v-model:value={formData.value.smtpTLS} checkedValue="true" uncheckedValue="false" />
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
)
|
||||
},
|
||||
'username-template': (formData: Ref<ReportMail>) => {
|
||||
return (
|
||||
<NGrid cols="24" xGap="24">
|
||||
<NFormItemGi span="24" label={$t('t_48_1745289355714')} path="password">
|
||||
<NInput
|
||||
v-model:value={formData.value.password}
|
||||
placeholder={$t('t_4_1744164840458')}
|
||||
type="password"
|
||||
showPasswordOn="click"
|
||||
/>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
)
|
||||
},
|
||||
}}
|
||||
></EmailForm>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,33 @@
|
||||
import { NCard, NButton, NGrid, NGridItem } from 'naive-ui'
|
||||
import { $t } from '@locales/index'
|
||||
import { useStore } from '../useStore'
|
||||
import { useController, useGeneralSettingsController } from '../useController'
|
||||
|
||||
/**
|
||||
* 常用设置标签页组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'GeneralSettings',
|
||||
setup() {
|
||||
const { generalSettings } = useStore()
|
||||
const { handleSaveGeneralSettings } = useController()
|
||||
const { GeneralForm } = useGeneralSettingsController()
|
||||
return () => (
|
||||
<div class="flex flex-col gap-[2rem]">
|
||||
<div class="mt-[2rem]">
|
||||
<NButton type="primary" onClick={() => handleSaveGeneralSettings(generalSettings.value)}>
|
||||
{$t('t_9_1745464078110')}
|
||||
</NButton>
|
||||
</div>
|
||||
|
||||
<NCard title={$t('t_10_1745464073098')} class="mb-4">
|
||||
<NGrid cols="1 m:2" xGap={24} yGap={24}>
|
||||
<NGridItem>
|
||||
<GeneralForm labelPlacement="top" />
|
||||
</NGridItem>
|
||||
</NGrid>
|
||||
</NCard>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,156 @@
|
||||
import { NCard, NButton, NList, NListItem, NTag, NSpace, NGrid, NGridItem, NSwitch } from 'naive-ui'
|
||||
import { useController } from '../useController'
|
||||
import { useStore } from '../useStore'
|
||||
import SvgIcon from '@components/svgIcon'
|
||||
import { $t } from '@locales/index'
|
||||
|
||||
/**
|
||||
* 告警通知标签页组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'NotificationSettings',
|
||||
setup() {
|
||||
const { notifyChannels, channelTypes } = useStore()
|
||||
const { openAddEmailChannelModal, editChannelConfig, testChannelConfig, confirmDeleteChannel, handleEnableChange } =
|
||||
useController()
|
||||
|
||||
// 获取已配置的渠道数量
|
||||
const getConfiguredCount = (type: string) => {
|
||||
return notifyChannels.value.filter((item) => item.type === type).length
|
||||
}
|
||||
|
||||
// 检查渠道是否已配置
|
||||
const isChannelConfigured = (type: string) => {
|
||||
return getConfiguredCount(type) > 0
|
||||
}
|
||||
|
||||
// 根据渠道类型和配置状态获取操作按钮
|
||||
const getChannelActionButton = (type: string) => {
|
||||
// 目前只有邮件通知是可配置的
|
||||
if (type === 'mail') {
|
||||
return (
|
||||
<NButton strong secondary type="primary" onClick={() => openAddEmailChannelModal(getConfiguredCount(type))}>
|
||||
{$t('t_1_1746676859550')}
|
||||
</NButton>
|
||||
)
|
||||
}
|
||||
// 其他渠道暂未支持
|
||||
return (
|
||||
<NButton strong secondary disabled>
|
||||
{$t('t_2_1746676856700')}
|
||||
</NButton>
|
||||
)
|
||||
}
|
||||
|
||||
// 渠道配置项数据
|
||||
const channelConfigs = [
|
||||
{
|
||||
type: 'mail',
|
||||
name: $t('t_3_1746676857930'),
|
||||
description: $t('t_4_1746676861473'),
|
||||
color: '#2080f0',
|
||||
},
|
||||
{
|
||||
type: 'dingtalk',
|
||||
name: $t('t_5_1746676856974'),
|
||||
description: $t('t_6_1746676860886'),
|
||||
color: '#1677ff',
|
||||
},
|
||||
{
|
||||
type: 'wecom',
|
||||
name: $t('t_7_1746676857191'),
|
||||
description: $t('t_8_1746676860457'),
|
||||
color: '#07c160',
|
||||
},
|
||||
{
|
||||
type: 'feishu',
|
||||
name: $t('t_9_1746676857164'),
|
||||
description: $t('t_10_1746676862329'),
|
||||
color: '#3370ff',
|
||||
},
|
||||
{
|
||||
type: 'webhook',
|
||||
name: $t('t_11_1746676859158'),
|
||||
description: $t('t_12_1746676860503'),
|
||||
color: '#531dab',
|
||||
},
|
||||
]
|
||||
return () => (
|
||||
<div class="notification-settings">
|
||||
<NCard title={$t('t_13_1746676856842')} class="mb-4">
|
||||
<NGrid cols="2 s:1 m:2" xGap={16} yGap={16}>
|
||||
{channelConfigs.map((item) => (
|
||||
<NGridItem key={item.type}>
|
||||
<div class="flex justify-between items-center p-4 border rounded-md hover:shadow-sm transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<SvgIcon icon={`notify-${item.type}`} size="3rem" />
|
||||
<div class="ml-4">
|
||||
<div class="flex items-center mb-1">
|
||||
<span class="mr-2 font-medium">{item.name}</span>
|
||||
{isChannelConfigured(item.type) && (
|
||||
<NTag size="small" type="success">
|
||||
{$t('t_8_1745735765753')} {getConfiguredCount(item.type)}
|
||||
</NTag>
|
||||
)}
|
||||
</div>
|
||||
<div class="text-gray-500 text-[1.2rem]">{item.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>{getChannelActionButton(item.type)}</div>
|
||||
</div>
|
||||
</NGridItem>
|
||||
))}
|
||||
</NGrid>
|
||||
</NCard>
|
||||
|
||||
{/* 已配置的通知渠道列表 */}
|
||||
{notifyChannels.value.length > 0 && (
|
||||
<NCard title={$t('t_14_1746676859019')} class="mb-4">
|
||||
<NList bordered>
|
||||
{notifyChannels.value.map((item) => (
|
||||
<NListItem key={item.id}>
|
||||
<div class=" items-center justify-between p-2 grid grid-cols-12">
|
||||
<div class="flex items-center col-span-6">
|
||||
<SvgIcon icon={`notify-${item.type}`} size="3rem" />
|
||||
<div class="font-medium mb-1 mx-[1rem]">{item.name}</div>
|
||||
<div class="flex items-center ">
|
||||
<NTag type="info" size="small">
|
||||
{(channelTypes.value as Record<string, string>)[item.type] || item.id}
|
||||
</NTag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 col-span-3 justify-end">
|
||||
<NSwitch
|
||||
v-model:value={item.config.enabled}
|
||||
onUpdateValue={() => handleEnableChange(item)}
|
||||
checkedValue={'1'}
|
||||
uncheckedValue={'0'}
|
||||
v-slots={{
|
||||
checked: () => <span>{$t('t_0_1745457486299')}</span>,
|
||||
unchecked: () => <span>{$t('t_15_1746676856567')}</span>,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-8 col-span-3 justify-end">
|
||||
<NSpace>
|
||||
<NButton size="small" onClick={() => editChannelConfig(item)}>
|
||||
{$t('t_11_1745215915429')}
|
||||
</NButton>
|
||||
<NButton size="small" onClick={() => testChannelConfig(item)}>
|
||||
{$t('t_16_1746676855270')}
|
||||
</NButton>
|
||||
<NButton size="small" type="error" onClick={() => confirmDeleteChannel(item)}>
|
||||
{$t('t_12_1745215914312')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</div>
|
||||
</div>
|
||||
</NListItem>
|
||||
))}
|
||||
</NList>
|
||||
</NCard>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
86
frontend/apps/allin-ssl/src/views/settings/index.tsx
Normal file
86
frontend/apps/allin-ssl/src/views/settings/index.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { NTabs, NTabPane, NCard, NIcon } from 'naive-ui'
|
||||
import { SettingOutlined, BellOutlined, InfoCircleOutlined } from '@vicons/antd'
|
||||
|
||||
import { useStore } from './useStore'
|
||||
import { useController } from './useController'
|
||||
import BaseComponent from '@/components/baseComponent'
|
||||
import GeneralSettings from './components/generalSettings'
|
||||
import NotificationSettings from './components/notificationSettings'
|
||||
import AboutSettings from './components/aboutSettings'
|
||||
|
||||
/**
|
||||
* 设置页面组件
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: 'Settings',
|
||||
setup() {
|
||||
const { activeTab, tabOptions } = useStore()
|
||||
const { fetchAllSettings, isCutTab } = useController()
|
||||
|
||||
// 渲染图标组件
|
||||
const renderIcon = (iconName: string) => {
|
||||
const icons: Record<string, any> = {
|
||||
SettingOutlined: <SettingOutlined />,
|
||||
BellOutlined: <BellOutlined />,
|
||||
InfoCircleOutlined: <InfoCircleOutlined />,
|
||||
}
|
||||
return <NIcon size="20">{icons[iconName]}</NIcon>
|
||||
}
|
||||
|
||||
// 自定义Tab样式已移至全局reset.css
|
||||
|
||||
onMounted(() => {
|
||||
isCutTab()
|
||||
fetchAllSettings()
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="mx-auto max-w-[1600px] w-full p-6">
|
||||
<BaseComponent
|
||||
v-slots={{
|
||||
content: () => (
|
||||
<div class="w-full">
|
||||
<NCard>
|
||||
<NTabs
|
||||
class="bg-white rounded-2xl p-6"
|
||||
type="segment"
|
||||
v-model:value={activeTab.value}
|
||||
size="large"
|
||||
justifyContent="space-evenly"
|
||||
>
|
||||
{tabOptions.value.map((tab) => (
|
||||
<NTabPane key={tab.key} name={tab.key}>
|
||||
{{
|
||||
tab: () => (
|
||||
<div class="flex items-center my-[10px] px-2 py-1 rounded-lg transition-all duration-300 ease-in-out">
|
||||
{renderIcon(tab.icon)}
|
||||
<span class="ml-2">{tab.title}</span>
|
||||
</div>
|
||||
),
|
||||
default: () => (
|
||||
<div class="w-full">
|
||||
{/* 常用设置 */}
|
||||
{activeTab.value === 'general' && <GeneralSettings />}
|
||||
|
||||
{/* 告警通知 */}
|
||||
{activeTab.value === 'notification' && <NotificationSettings />}
|
||||
|
||||
{/* 关于我们 */}
|
||||
{activeTab.value === 'about' && <AboutSettings />}
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</NTabPane>
|
||||
))}
|
||||
</NTabs>
|
||||
</NCard>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
431
frontend/apps/allin-ssl/src/views/settings/useController.tsx
Normal file
431
frontend/apps/allin-ssl/src/views/settings/useController.tsx
Normal file
@@ -0,0 +1,431 @@
|
||||
import { FormInst, FormItemRule, FormRules } from 'naive-ui'
|
||||
import md5 from 'crypto-js/md5'
|
||||
import { useFormHooks, useModal, useDialog, useForm, useMessage, useLoadingMask } from '@baota/naive-ui/hooks'
|
||||
import { useError } from '@baota/hooks/error'
|
||||
import { $t } from '@locales/index'
|
||||
import { useStore } from './useStore'
|
||||
|
||||
import EmailChannelForm from './components/emailChannelForm'
|
||||
import type { ReportMail, AddReportParams, SaveSettingParams, ReportType } from '@/types/setting'
|
||||
|
||||
const {
|
||||
// 标签页
|
||||
activeTab,
|
||||
tabOptions,
|
||||
// 常用设置
|
||||
generalSettings,
|
||||
channelTypes,
|
||||
aboutInfo,
|
||||
fetchGeneralSettings,
|
||||
saveGeneralSettings,
|
||||
// 通知设置
|
||||
fetchNotifyChannels,
|
||||
notifyChannels,
|
||||
// 邮箱通知渠道表单
|
||||
emailChannelForm,
|
||||
addReportChannel,
|
||||
updateReportChannel,
|
||||
testReportChannel,
|
||||
deleteReportChannel,
|
||||
} = useStore()
|
||||
const message = useMessage()
|
||||
const { handleError } = useError()
|
||||
const { useFormInput, useFormInputNumber, useFormSwitch, useFormTextarea, useFormSlot } = useFormHooks()
|
||||
|
||||
/**
|
||||
* 设置页面业务逻辑控制器
|
||||
* @function useController
|
||||
* @description 提供设置页面所需的全部业务逻辑和状态数据,负责协调不同设置组件的交互行为
|
||||
* @returns {Object} 返回设置页面相关的状态数据和处理方法
|
||||
*/
|
||||
export const useController = () => {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
/**
|
||||
* @description 检测是否需要添加工作流
|
||||
*/
|
||||
const isCutTab = () => {
|
||||
const { tab } = route.query
|
||||
if (tab?.includes('notification')) {
|
||||
activeTab.value = 'notification'
|
||||
router.push({ query: {} })
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 一次性加载所有设置数据
|
||||
* @async
|
||||
* @function fetchAllSettings
|
||||
* @description 页面初始化时调用,并行加载系统设置、通知设置和通知渠道数据
|
||||
* @returns {Promise<void>} 无返回值
|
||||
*/
|
||||
const fetchAllSettings = async () => {
|
||||
try {
|
||||
await Promise.all([fetchGeneralSettings(), fetchNotifyChannels()])
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description md5 密码加密
|
||||
* @param {string} password - 原始密码
|
||||
* @returns {string} 加密后的密码
|
||||
*/
|
||||
const encryptPassword = (password: string): string => {
|
||||
return md5(`${password}_bt_all_in_ssl`).toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存常用设置
|
||||
* @async
|
||||
* @function handleSaveGeneralSettings
|
||||
* @description 验证并保存常用设置表单数据
|
||||
* @param {SaveSettingParams} params - 保存设置请求参数
|
||||
* @param {Ref<FormInst | null>} formRef - 表单实例引用,用于验证表单数据
|
||||
* @returns {Promise<void>} 无返回值
|
||||
*/
|
||||
const handleSaveGeneralSettings = async (params: SaveSettingParams) => {
|
||||
try {
|
||||
await saveGeneralSettings({
|
||||
...params,
|
||||
password: params.password !== '' ? encryptPassword(params.password) : '',
|
||||
})
|
||||
// window.location.reload()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开添加邮箱通知渠道弹窗
|
||||
* @function openAddEmailChannelModal
|
||||
* @description 打开添加邮箱通知渠道的模态框,并在关闭后刷新通知渠道列表
|
||||
* @returns {void} 无返回值
|
||||
*/
|
||||
const openAddEmailChannelModal = (limit: number = 1) => {
|
||||
if (limit >= 1) {
|
||||
message.warning($t('t_16_1746773356568'))
|
||||
return
|
||||
}
|
||||
useModal({
|
||||
title: $t('t_18_1745457490931'),
|
||||
area: 650,
|
||||
component: EmailChannelForm,
|
||||
footer: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 处理启用状态切换
|
||||
const handleEnableChange = async (item: ReportType<ReportMail>) => {
|
||||
useDialog({
|
||||
title: $t('t_17_1746773351220', [
|
||||
Number(item.config.enabled) ? $t('t_5_1745215914671') : $t('t_6_1745215914104'),
|
||||
]),
|
||||
content: $t('t_18_1746773355467', [
|
||||
Number(item.config.enabled) ? $t('t_5_1745215914671') : $t('t_6_1745215914104'),
|
||||
]),
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await updateReportChannel({
|
||||
id: Number(item.id),
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
config: JSON.stringify(item.config),
|
||||
})
|
||||
await fetchNotifyChannels()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
},
|
||||
// 取消后刷新通知渠道列表
|
||||
onNegativeClick: () => {
|
||||
fetchNotifyChannels()
|
||||
},
|
||||
onClose: () => {
|
||||
fetchNotifyChannels()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看通知渠道配置
|
||||
* @function viewChannelConfig
|
||||
* @description 显示特定通知渠道的详细配置信息
|
||||
* @param {ReportType<ReportMail>} item - 要查看的通知渠道对象
|
||||
* @returns {void} 无返回值
|
||||
*/
|
||||
const editChannelConfig = (item: ReportType<ReportMail>) => {
|
||||
console.log(item)
|
||||
if (item.type === 'mail') {
|
||||
useModal({
|
||||
title: $t('t_0_1745895057404'),
|
||||
area: 650,
|
||||
component: EmailChannelForm,
|
||||
componentProps: {
|
||||
data: item,
|
||||
},
|
||||
footer: true,
|
||||
onClose: () => fetchNotifyChannels(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试通知渠道配置
|
||||
* @function testChannelConfig
|
||||
* @description 测试通知渠道配置
|
||||
* @param {ReportType<ReportMail>} item - 要测试的通知渠道对象
|
||||
* @returns {void} 无返回值
|
||||
*/
|
||||
const testChannelConfig = (item: ReportType<ReportMail>) => {
|
||||
if (item.type !== 'mail') {
|
||||
message.warning($t('t_19_1746773352558'))
|
||||
return
|
||||
}
|
||||
const { open, close } = useLoadingMask({ text: $t('t_20_1746773356060') })
|
||||
|
||||
useDialog({
|
||||
title: $t('t_21_1746773350759'),
|
||||
content: $t('t_22_1746773360711'),
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
open()
|
||||
await testReportChannel({ id: item.id })
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
} finally {
|
||||
close()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通知渠道
|
||||
* @function confirmDeleteChannel
|
||||
* @description 确认并删除指定的通知渠道
|
||||
* @param {ReportType<ReportMail>} item - 要删除的通知渠道对象
|
||||
* @returns {void} 无返回值
|
||||
*/
|
||||
const confirmDeleteChannel = (item: ReportType<ReportMail>) => {
|
||||
useDialog({
|
||||
title: $t('t_23_1746773350040'),
|
||||
content: $t('t_0_1746773763967', [item.name]),
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await deleteReportChannel({ id: item.id })
|
||||
await fetchNotifyChannels()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
activeTab,
|
||||
isCutTab,
|
||||
tabOptions,
|
||||
generalSettings,
|
||||
notifyChannels,
|
||||
channelTypes,
|
||||
aboutInfo,
|
||||
fetchAllSettings,
|
||||
handleSaveGeneralSettings,
|
||||
openAddEmailChannelModal,
|
||||
handleEnableChange,
|
||||
editChannelConfig,
|
||||
testChannelConfig,
|
||||
confirmDeleteChannel,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 常用设置表单控制器
|
||||
* @function useGeneralSettingsController
|
||||
* @description 提供常用设置表单的配置、规则和组件
|
||||
* @returns {object} 返回表单相关配置、规则和组件
|
||||
*/
|
||||
export const useGeneralSettingsController = () => {
|
||||
/**
|
||||
* 表单验证规则
|
||||
* @type {FormRules}
|
||||
*/
|
||||
const rules: FormRules = {
|
||||
timeout: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
trigger: ['input', 'blur'],
|
||||
message: '请输入超时时间',
|
||||
},
|
||||
secure: {
|
||||
required: true,
|
||||
trigger: ['input', 'blur'],
|
||||
message: '请输入安全入口',
|
||||
},
|
||||
username: {
|
||||
required: true,
|
||||
trigger: ['input', 'blur'],
|
||||
message: '请输入管理员账号',
|
||||
},
|
||||
password: {
|
||||
trigger: ['input', 'blur'],
|
||||
message: '请输入管理员密码',
|
||||
},
|
||||
cert: {
|
||||
required: true,
|
||||
trigger: 'input',
|
||||
message: '请输入SSL证书',
|
||||
},
|
||||
key: {
|
||||
required: true,
|
||||
trigger: 'input',
|
||||
message: '请输入SSL密钥',
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单配置
|
||||
* @type {ComputedRef<FormConfig>}
|
||||
* @description 动态生成表单项配置,根据SSL启用状态显示或隐藏SSL相关字段
|
||||
*/
|
||||
const config = computed(() => {
|
||||
const options = [
|
||||
useFormInputNumber('超时时间 (秒)', 'timeout', { class: 'w-full' }),
|
||||
useFormInput('安全入口', 'secure'),
|
||||
useFormInput('管理员账号', 'username'),
|
||||
useFormInput('管理员密码', 'password', { type: 'password', showPasswordOn: 'click' }),
|
||||
useFormSwitch('启用SSL', 'https', {
|
||||
checkedValue: '1',
|
||||
uncheckedValue: '0',
|
||||
}),
|
||||
]
|
||||
if (Number(generalSettings.value.https) === 1) {
|
||||
options.push(useFormTextarea('SSL证书', 'cert', { rows: 3 }), useFormTextarea('SSL密钥', 'key', { rows: 3 }))
|
||||
}
|
||||
return options
|
||||
})
|
||||
|
||||
/**
|
||||
* 创建表单组件
|
||||
* @type {Object}
|
||||
*/
|
||||
const { component: GeneralForm } = useForm({
|
||||
config,
|
||||
defaultValue: generalSettings,
|
||||
rules,
|
||||
})
|
||||
|
||||
return {
|
||||
GeneralForm,
|
||||
config,
|
||||
rules,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱通知渠道表单控制器
|
||||
* @function useEmailChannelFormController
|
||||
* @description 提供邮箱通知渠道表单的配置、规则和提交方法
|
||||
* @returns {object} 返回表单相关配置、规则和方法
|
||||
*/
|
||||
export const useEmailChannelFormController = () => {
|
||||
const { open: openLoad, close: closeLoad } = useLoadingMask({ text: $t('t_0_1746667592819') })
|
||||
/**
|
||||
* 表单验证规则
|
||||
* @type {FormRules}
|
||||
*/
|
||||
const rules: FormRules = {
|
||||
name: {
|
||||
required: true,
|
||||
trigger: ['input', 'blur'],
|
||||
message: $t('t_25_1746773349596'),
|
||||
},
|
||||
|
||||
smtpHost: {
|
||||
required: true,
|
||||
trigger: ['input', 'blur'],
|
||||
message: $t('t_15_1745833940280'),
|
||||
},
|
||||
smtpPort: {
|
||||
required: true,
|
||||
trigger: 'input',
|
||||
validator: (rule: FormItemRule, value: string) => {
|
||||
const port = Number(value)
|
||||
if (isNaN(port) || port < 1 || port > 65535) {
|
||||
return new Error($t('t_26_1746773353409'))
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
password: {
|
||||
required: true,
|
||||
trigger: ['input', 'blur'],
|
||||
message: $t('t_27_1746773352584'),
|
||||
},
|
||||
sender: {
|
||||
required: true,
|
||||
trigger: ['input', 'blur'],
|
||||
type: 'email',
|
||||
message: $t('t_28_1746773354048'),
|
||||
},
|
||||
receiver: {
|
||||
required: true,
|
||||
trigger: ['input', 'blur'],
|
||||
type: 'email',
|
||||
message: $t('t_29_1746773351834'),
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单配置
|
||||
* @type {ComputedRef<FormConfig>}
|
||||
* @description 生成邮箱通知渠道表单的字段配置
|
||||
*/
|
||||
const config = computed(() => [
|
||||
useFormInput($t('t_2_1745289353944'), 'name'),
|
||||
useFormSlot('smtp-template'),
|
||||
useFormSlot('username-template'),
|
||||
useFormInput($t('t_30_1746773350013'), 'sender'),
|
||||
useFormInput($t('t_31_1746773349857'), 'receiver'),
|
||||
])
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
* @async
|
||||
* @function submitForm
|
||||
* @description 验证并提交邮箱通知渠道表单
|
||||
* @param {any} params - 表单参数
|
||||
* @param {Ref<FormInst>} formRef - 表单实例引用
|
||||
* @returns {Promise<boolean>} 提交成功返回true,失败返回false
|
||||
*/
|
||||
const submitForm = async (
|
||||
{ config, ...other }: AddReportParams<ReportMail>,
|
||||
formRef: Ref<FormInst | null>,
|
||||
id?: number,
|
||||
) => {
|
||||
try {
|
||||
openLoad()
|
||||
if (id) {
|
||||
await updateReportChannel({ id, config: JSON.stringify(config), ...other })
|
||||
} else {
|
||||
await addReportChannel({ config: JSON.stringify(config), ...other })
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
return false
|
||||
} finally {
|
||||
closeLoad()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
config,
|
||||
rules,
|
||||
emailChannelForm,
|
||||
submitForm,
|
||||
}
|
||||
}
|
||||
244
frontend/apps/allin-ssl/src/views/settings/useStore.tsx
Normal file
244
frontend/apps/allin-ssl/src/views/settings/useStore.tsx
Normal file
@@ -0,0 +1,244 @@
|
||||
import { useError } from '@baota/hooks/error'
|
||||
import { $t } from '@locales/index'
|
||||
import {
|
||||
getSystemSetting,
|
||||
saveSystemSetting,
|
||||
getReportList,
|
||||
updateReport,
|
||||
deleteReport,
|
||||
addReport,
|
||||
testReport,
|
||||
} from '@/api/setting'
|
||||
import type {
|
||||
SaveSettingParams,
|
||||
SystemSetting,
|
||||
ReportType,
|
||||
GetReportListParams,
|
||||
AddReportParams,
|
||||
UpdateReportParams,
|
||||
DeleteReportParams,
|
||||
ReportMail,
|
||||
TestReportParams,
|
||||
} from '@/types/setting'
|
||||
|
||||
const { handleError } = useError()
|
||||
/**
|
||||
* 设置页面状态 Store
|
||||
* @description 用于管理设置相关的状态和操作
|
||||
*/
|
||||
export const useSettingsStore = defineStore('settings-store', () => {
|
||||
// -------------------- 状态定义 --------------------
|
||||
// 当前激活的标签页
|
||||
const activeTab = ref<'general' | 'notification' | 'about'>('general')
|
||||
|
||||
// 标签页选项
|
||||
const tabOptions = ref([
|
||||
{ key: 'general', title: '常用设置', icon: 'SettingOutlined' },
|
||||
{ key: 'notification', title: '告警通知', icon: 'BellOutlined' },
|
||||
{ key: 'about', title: '关于我们', icon: 'InfoCircleOutlined' },
|
||||
])
|
||||
|
||||
const generalSettings = ref<SystemSetting>({
|
||||
timeout: 30,
|
||||
secure: '',
|
||||
username: 'admin',
|
||||
password: '',
|
||||
https: 0,
|
||||
key: '',
|
||||
cert: '',
|
||||
})
|
||||
// // 通知设置表单数据
|
||||
// const notificationSettings = ref<CertEndNoticeTemplate>({
|
||||
// title: '【AllIn SSL】系统告警通知', // 通知标题
|
||||
// content: '尊敬的用户,您的系统出现了以下警告:{{content}},请及时处理。', // 通知内容模板
|
||||
// })
|
||||
|
||||
// 通知渠道列表
|
||||
const notifyChannels = ref<ReportType<ReportMail>[]>([])
|
||||
|
||||
// 通知渠道类型
|
||||
const channelTypes = ref<Record<string, string>>({
|
||||
mail: $t('t_68_1745289354676'),
|
||||
dingtalk: $t('t_32_1746773348993'),
|
||||
wecom: $t('t_33_1746773350932'),
|
||||
feishu: $t('t_34_1746773350153'),
|
||||
webhook: 'WebHook',
|
||||
})
|
||||
|
||||
// 邮箱通知渠道表单
|
||||
const emailChannelForm = ref<ReportMail>({
|
||||
name: '',
|
||||
enabled: '1',
|
||||
receiver: '', // 接受邮箱
|
||||
sender: '', // 发送邮箱
|
||||
smtpHost: '', // SMTP服务器
|
||||
smtpPort: '465', //SMTP端口
|
||||
smtpTLS: 'false', // TLS协议,加密
|
||||
password: '',
|
||||
})
|
||||
|
||||
// 关于页面数据
|
||||
const aboutInfo = ref({
|
||||
version: '1.0.0',
|
||||
hasUpdate: false,
|
||||
latestVersion: '',
|
||||
updateLog: '',
|
||||
qrcode: {
|
||||
service: 'https://example.com/service_qr.png',
|
||||
wechat: 'https://example.com/wechat_qr.png',
|
||||
},
|
||||
description: $t(
|
||||
'ALLinSSL \n\r开源免费的 SSL 证书自动化管理平台 \n\r一键自动化申请、续期、部署、监控所有 SSL/TLS 证书,支持跨云环境和多 CA (coding~),告别繁琐配置和高昂费用。',
|
||||
),
|
||||
})
|
||||
|
||||
// -------------------- 工具方法 --------------------
|
||||
/**
|
||||
* 获取系统设置
|
||||
*/
|
||||
const fetchGeneralSettings = async () => {
|
||||
try {
|
||||
const { data } = await getSystemSetting().fetch()
|
||||
generalSettings.value = { ...generalSettings.value, ...(data || {}) }
|
||||
} catch (error) {
|
||||
handleError(error).default($t('t_0_1745464080226'))
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 保存系统设置
|
||||
*/
|
||||
const saveGeneralSettings = async (params: SaveSettingParams) => {
|
||||
try {
|
||||
const { fetch, message } = saveSystemSetting(params)
|
||||
message.value = true
|
||||
await fetch()
|
||||
} catch (error) {
|
||||
handleError(error).default($t('t_1_1745464079590'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通知渠道列表
|
||||
*/
|
||||
const fetchNotifyChannels = async (params: GetReportListParams = { p: 1, search: '', limit: 1000 }) => {
|
||||
try {
|
||||
const { data } = await getReportList(params).fetch()
|
||||
notifyChannels.value = (data || []).map(({ config, ...otherwise }) => {
|
||||
console.log(config)
|
||||
return {
|
||||
config: JSON.parse(config),
|
||||
...otherwise,
|
||||
}
|
||||
})
|
||||
|
||||
console.log(notifyChannels.value)
|
||||
} catch (error) {
|
||||
notifyChannels.value = []
|
||||
handleError(error).default($t('t_4_1745464075382'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加通知渠道
|
||||
*/
|
||||
const addReportChannel = async (params: AddReportParams) => {
|
||||
try {
|
||||
const { fetch, message } = addReport(params)
|
||||
message.value = true
|
||||
await fetch()
|
||||
} catch (error) {
|
||||
handleError(error).default($t('t_5_1745464086047'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新通知渠道
|
||||
*/
|
||||
const updateReportChannel = async (params: UpdateReportParams) => {
|
||||
try {
|
||||
const { fetch, message } = updateReport(params)
|
||||
message.value = true
|
||||
await fetch()
|
||||
} catch (error) {
|
||||
handleError(error).default($t('t_6_1745464075714'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试通知渠道
|
||||
*/
|
||||
const testReportChannel = async (params: TestReportParams) => {
|
||||
try {
|
||||
const { fetch, message } = testReport(params)
|
||||
message.value = true
|
||||
await fetch()
|
||||
} catch (error) {
|
||||
handleError(error).default($t('t_0_1746676862189'))
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 删除通知渠道
|
||||
*/
|
||||
const deleteReportChannel = async ({ id }: DeleteReportParams) => {
|
||||
try {
|
||||
const { fetch, message } = deleteReport({ id })
|
||||
message.value = true
|
||||
await fetch()
|
||||
await fetchNotifyChannels()
|
||||
} catch (error) {
|
||||
handleError(error).default($t('t_7_1745464073330'))
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 检查版本更新
|
||||
// */
|
||||
// const checkUpdate = async () => {
|
||||
// try {
|
||||
// const res = await systemUpdate().fetch()
|
||||
// // 实际应用中可能需要修改API或类型定义
|
||||
// aboutInfo.value = {
|
||||
// ...aboutInfo.value,
|
||||
// ...(res.data || {
|
||||
// hasUpdate: false,
|
||||
// latestVersion: '--',
|
||||
// updateLog: '--',
|
||||
// }),
|
||||
// }
|
||||
// } catch (error) {
|
||||
// handleError(error).default($t('t_8_1745464081472'))
|
||||
// return null
|
||||
// }
|
||||
// }
|
||||
|
||||
return {
|
||||
// 状态
|
||||
activeTab,
|
||||
tabOptions,
|
||||
generalSettings,
|
||||
notifyChannels,
|
||||
channelTypes,
|
||||
emailChannelForm,
|
||||
aboutInfo,
|
||||
|
||||
// 方法
|
||||
fetchGeneralSettings,
|
||||
saveGeneralSettings,
|
||||
|
||||
fetchNotifyChannels,
|
||||
addReportChannel,
|
||||
updateReportChannel,
|
||||
deleteReportChannel,
|
||||
testReportChannel,
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 组合式 API 使用 Store
|
||||
* @description 提供对设置页面 Store 的访问,并返回响应式引用
|
||||
* @returns {Object} 包含所有 store 状态和方法的对象
|
||||
*/
|
||||
export const useStore = () => {
|
||||
const store = useSettingsStore()
|
||||
return { ...store, ...storeToRefs(store) }
|
||||
}
|
||||
Reference in New Issue
Block a user