【优化】重绘各类厂商和资源来源图标

【新增】CA类型SSL.COM
【新增】消息通知渠道-webhook
【新增】消息通知渠道-飞书
【新增】消息通知渠道-钉钉
【新增】huaweicloud-cdn部署类型
【修改】版本号
This commit is contained in:
chudong
2025-05-29 14:29:11 +08:00
parent c7e8dee436
commit 29e5cc8304
170 changed files with 12084 additions and 13703 deletions

View File

@@ -31,7 +31,18 @@ import { isEmail, isIp, isPort, isUrl } from '@baota/utils/business'
import { $t } from '@locales/index'
import { useStore } from './useStore'
import { ApiProjectConfig } from '@config/data'
import type { AccessItem, AccessListParams, AddAccessParams, SshAccessConfig, UpdateAccessParams } from '@/types/access'
import type {
AccessItem,
AccessListParams,
AddAccessParams,
SshAccessConfig,
UpdateAccessParams,
NamecheapAccessConfig,
NS1AccessConfig,
CloudnsAccessConfig,
AwsAccessConfig,
AzureAccessConfig,
} from '@/types/access'
import type { VNode, Ref } from 'vue'
import { testAccess } from '@/api/access'
@@ -363,6 +374,8 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
btpanel: $t('t_1_1747042969705'),
btwaf: $t('t_1_1747300384579'),
godaddy: $t('t_0_1747984137443'),
ns1: '请输入API Key',
namecheap: '请输入API Key',
}
return callback(new Error(mapTips[param.value.type as keyof typeof mapTips]))
}
@@ -394,6 +407,41 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
message: $t('t_5_1745317315285'),
trigger: 'input',
},
secret_access_key: {
required: true,
message: '请输入Secret Access Key',
trigger: 'input',
},
api_user: {
required: true,
message: '请输入API User',
trigger: 'input',
},
auth_id: {
required: true,
message: '请输入Auth ID',
trigger: 'input',
},
auth_password: {
required: true,
message: '请输入Auth Password',
trigger: 'input',
},
tenant_id: {
required: true,
message: '请输入Tenant ID',
trigger: 'input',
},
client_id: {
required: true,
message: '请输入Client ID',
trigger: 'input',
},
client_secret: {
required: true,
message: '请输入Client Secret',
trigger: 'input',
},
secret_id: {
required: true,
message: $t('t_6_1745317313383'),
@@ -586,6 +634,35 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
useFormInput('AccessSecret', 'config.access_secret', { allowInput: noSideSpace }),
)
break
case 'namecheap':
items.push(
useFormInput('API User', 'config.api_user', { allowInput: noSideSpace }),
useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }),
)
break
case 'ns1':
items.push(useFormInput('API Key', 'config.api_key', { allowInput: noSideSpace }))
break
case 'cloudns':
items.push(
useFormInput('Auth ID', 'config.auth_id', { allowInput: noSideSpace }),
useFormInput('Auth Password', 'config.auth_password', { allowInput: noSideSpace }),
)
break
case 'aws':
items.push(
useFormInput('Access Key ID', 'config.access_key_id', { allowInput: noSideSpace }),
useFormInput('Secret Access Key', 'config.secret_access_key', { allowInput: noSideSpace }),
)
break
case 'azure':
items.push(
useFormInput('Tenant ID', 'config.tenant_id', { allowInput: noSideSpace }),
useFormInput('Client ID', 'config.client_id', { allowInput: noSideSpace }),
useFormInput('Client Secret', 'config.client_secret', { allowInput: noSideSpace }),
useFormInput('Environment', 'config.environment', { allowInput: noSideSpace, placeholder: 'public' }),
)
break
default:
break
}
@@ -604,7 +681,7 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
user: 'root',
mode: 'password',
password: '',
}
} as SshAccessConfig
break
case '1panel':
case 'btpanel':
@@ -652,6 +729,37 @@ export const useApiFormController = (props: ApiFormControllerProps): ApiFormCont
access_secret: '',
}
break
case 'namecheap':
param.value.config = {
api_user: '',
api_key: '',
} as NamecheapAccessConfig
break
case 'ns1':
param.value.config = {
api_key: '',
} as NS1AccessConfig
break
case 'cloudns':
param.value.config = {
auth_id: '',
auth_password: '',
} as CloudnsAccessConfig
break
case 'aws':
param.value.config = {
access_key_id: '',
secret_access_key: '',
} as AwsAccessConfig
break
case 'azure':
param.value.config = {
tenant_id: '',
client_id: '',
client_secret: '',
environment: '',
} as AzureAccessConfig
break
}
},
)

View File

@@ -93,8 +93,8 @@ export default defineComponent({
)
},
},
useFormInput($t('邮件'), 'email', {
placeholder: $t('请输入邮箱信息,用于接收证书验证邮件'),
useFormInput($t('t_68_1745289354676'), 'email', {
placeholder: $t('t_2_1748052862259'),
allowInput: noSideSpace,
}),

View File

@@ -121,11 +121,17 @@ export default defineComponent({
valueType: 'value' as const,
isAddMode: true,
'onUpdate:value': (val: { value: number | string; type: string }) => {
if (val.value !== '' && param.value.provider_id !== '' && param.value.provider_id !== val.value) {
if (
val.value !== '' &&
param.value.provider_id !== '' &&
param.value.provider_id !== val.value &&
param.provider === 'btpanel-site'
) {
param.value.siteName = []
}
param.value.provider_id = val.value
},
param.value.type = val.type
},
}
return (<DnsProviderSelect {...dnsProviderProps} />) as VNode
}),
@@ -144,6 +150,7 @@ export default defineComponent({
}),
)
console.log(param.value.provider)
// 根据不同的部署类型添加不同的表单配置
switch (param.value.provider) {
case 'localhost':
@@ -179,6 +186,7 @@ export default defineComponent({
case 'baidu-cdn':
case 'qiniu-cdn':
case 'qiniu-oss':
case 'huaweicloud-cdn':
config.push(...formConfig.cdnDeploy())
break
case 'aliyun-waf':
@@ -195,6 +203,15 @@ export default defineComponent({
return config
})
watch(
() => param.value.provider_id,
() => {
if (param.value.provider === 'btpanel-site') {
handleSiteSearch('')
}
},
)
/**
* 处理网站搜索
* @param query 搜索关键字
@@ -230,9 +247,10 @@ export default defineComponent({
if (!param.value.provider) return message.error($t('t_0_1746858920894'))
if (param.value.provider === 'localhost') {
delete param.value.provider_id
} else {
param.value.provider_id = props.node.config.provider_id
}
// else {
// param.value.provider_id = props.node.config.provider_id
// }
// 加载证书来源选项
certOptions.value = findApplyUploadNodesUp(props.node.id).map((item) => {
return { label: item.name, value: item.id }
@@ -251,16 +269,6 @@ export default defineComponent({
next.value = false
}
/**
* 上一步
*/
const prevStep = (): void => {
current.value--
next.value = true
param.value.provider_id = ''
param.value.provider = ''
}
// 表单组件
const { component: Form, example } = useForm<DeployNodeConfig>({
config: nodeFormConfig,
@@ -268,14 +276,16 @@ export default defineComponent({
rules: verifyRules,
})
watch(
() => param.value.provider_id,
() => {
if (param.value.provider === 'btpanel-site') {
handleSiteSearch('')
}
},
)
/**
* 上一步
*/
const prevStep = (): void => {
current.value--
next.value = true
param.value = {}
param.value.provider_id = ''
param.value.provider = ''
}
/**
* 提交

View File

@@ -20,6 +20,7 @@ export default defineComponent({
// 使用通用节点验证
const renderContent = (valid: boolean, config: NotifyNodeConfig) => {
if (config.provider) {
console.log(config.provider)
return <TypeIcon icon={config.provider} type={valid ? 'success' : 'warning'} />
}
return $t('t_9_1745735765287')

View File

@@ -19,7 +19,6 @@ export default defineComponent({
},
setup(props) {
const { CATable, CATablePage, handleOpenAddForm, total } = useCAManageController(props)
return () => (
<BaseComponent
v-slots={{

View File

@@ -184,7 +184,7 @@ export default defineComponent({
)}
<div class={styles.systemInfo}>
<NBadge value={1} show={false} dot>
<span class="px-1 sm:px-[.5rem] cursor-pointer">v1.0.3</span>
<span class="px-1 sm:px-[.5rem] cursor-pointer">v1.0.4</span>
</NBadge>
</div>
</NLayoutHeader>

View File

@@ -14,7 +14,7 @@ export default defineComponent({
<NDescriptions bordered>
<NDescriptionsItem label={$t('t_5_1745833933241')}>
<div class="flex items-center">
<span class="text-[2rem] font-medium">v1.0.3</span>
<span class="text-[2rem] font-medium">v1.0.4</span>
</div>
</NDescriptionsItem>
<NDescriptionsItem label={$t('t_29_1746667589773')}>

View File

@@ -0,0 +1,71 @@
import { useForm, useModalHooks } from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { useDingtalkChannelFormController } from './useController'
import { useStore } from '@settings/useStore'
import type { ReportDingtalk, ReportType } from '@/types/setting'
/**
* 钉钉通知渠道表单组件
*/
export default defineComponent({
name: 'DingtalkChannelModel',
props: {
data: {
type: Object as PropType<ReportType<ReportDingtalk> | null>,
default: () => null,
},
},
setup(props: { data: ReportType<ReportDingtalk> | null }) {
const { handleError } = useError()
const { confirm } = useModalHooks()
const { fetchNotifyChannels } = useStore()
const { config, rules, dingtalkChannelForm, submitForm } = useDingtalkChannelFormController()
if (props.data) {
const { name, config } = props.data
dingtalkChannelForm.value = {
name,
...config,
}
}
// 使用表单hooks
const {
component: DingtalkForm,
example,
data,
} = useForm({
config,
defaultValue: dingtalkChannelForm,
rules,
})
// 关联确认按钮
confirm(async (close) => {
try {
const { name, ...other } = data.value
await example.value?.validate()
const res = await submitForm(
{
type: 'dingtalk',
name: name || '',
config: other,
},
example,
props.data?.id,
)
fetchNotifyChannels()
if (res) close()
} catch (error) {
handleError(error)
}
})
return () => (
<div class="dingtalk-channel-form">
<DingtalkForm labelPlacement="top"></DingtalkForm>
</div>
)
},
})

View File

@@ -2,8 +2,8 @@ import { NGrid, NFormItemGi, NInput, NSwitch, NTooltip } 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 { useEmailChannelFormController } from './useController'
import { useStore } from '@settings/useStore'
import type { ReportMail, ReportType } from '@/types/setting'
@@ -11,7 +11,7 @@ import type { ReportMail, ReportType } from '@/types/setting'
*
*/
export default defineComponent({
name: 'EmailChannelForm',
name: 'EmailChannelModel',
props: {
data: {
type: Object as PropType<ReportType<ReportMail> | null>,

View File

@@ -0,0 +1,71 @@
import { useForm, useModalHooks } from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { useFeishuChannelFormController } from './useController'
import { useStore } from '@settings/useStore'
import type { ReportFeishu, ReportType } from '@/types/setting'
/**
* 飞书通知渠道表单组件
*/
export default defineComponent({
name: 'FeishuChannelModel',
props: {
data: {
type: Object as PropType<ReportType<ReportFeishu> | null>,
default: () => null,
},
},
setup(props: { data: ReportType<ReportFeishu> | null }) {
const { handleError } = useError()
const { confirm } = useModalHooks()
const { fetchNotifyChannels } = useStore()
const { config, rules, feishuChannelForm, submitForm } = useFeishuChannelFormController()
if (props.data) {
const { name, config } = props.data
feishuChannelForm.value = {
name,
...config,
}
}
// 使用表单hooks
const {
component: FeishuForm,
example,
data,
} = useForm({
config,
defaultValue: feishuChannelForm,
rules,
})
// 关联确认按钮
confirm(async (close) => {
try {
const { name, ...other } = data.value
await example.value?.validate()
const res = await submitForm(
{
type: 'feishu',
name: name || '',
config: other,
},
example,
props.data?.id,
)
fetchNotifyChannels()
if (res) close()
} catch (error) {
handleError(error)
}
})
return () => (
<div class="feishu-channel-form">
<FeishuForm labelPlacement="top"></FeishuForm>
</div>
)
},
})

View File

@@ -0,0 +1,71 @@
import { useForm, useModalHooks } from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { useWebhookChannelFormController } from './useController'
import { useStore } from '@settings/useStore'
import type { ReportWebhook, ReportType } from '@/types/setting'
/**
* Webhook通知渠道表单组件
*/
export default defineComponent({
name: 'WebhookChannelModel',
props: {
data: {
type: Object as PropType<ReportType<ReportWebhook> | null>,
default: () => null,
},
},
setup(props: { data: ReportType<ReportWebhook> | null }) {
const { handleError } = useError()
const { confirm } = useModalHooks()
const { fetchNotifyChannels } = useStore()
const { config, rules, webhookChannelForm, submitForm } = useWebhookChannelFormController()
if (props.data) {
const { name, config } = props.data
webhookChannelForm.value = {
name,
...config,
}
}
// 使用表单hooks
const {
component: WebhookForm,
example,
data,
} = useForm({
config,
defaultValue: webhookChannelForm,
rules,
})
// 关联确认按钮
confirm(async (close) => {
try {
const { name, ...other } = data.value
await example.value?.validate()
const res = await submitForm(
{
type: 'webhook',
name: name || '',
config: other,
},
example,
props.data?.id,
)
fetchNotifyChannels()
if (res) close()
} catch (error) {
handleError(error)
}
})
return () => (
<div class="webhook-channel-form">
<WebhookForm labelPlacement="top"></WebhookForm>
</div>
)
},
})

View File

@@ -0,0 +1,351 @@
import { FormInst, FormItemRule, FormRules } from 'naive-ui'
import { useFormHooks, useLoadingMask } from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { $t } from '@locales/index'
import { useStore } from '@settings/useStore'
import type { ReportMail, ReportFeishu, ReportWebhook, ReportDingtalk, AddReportParams } from '@/types/setting'
const {
emailChannelForm,
feishuChannelForm,
webhookChannelForm,
dingtalkChannelForm,
addReportChannel,
updateReportChannel,
} = useStore()
const { handleError } = useError()
const { useFormInput, useFormSwitch, useFormTextarea, useFormSelect, useFormSlot } = useFormHooks()
/**
* 邮箱通知渠道表单控制器
* @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,
}
}
/**
* 飞书通知渠道表单控制器
* @function useFeishuChannelFormController
* @description 提供飞书通知渠道表单的配置、规则和提交方法
* @returns {object} 返回表单相关配置、规则和方法
*/
export const useFeishuChannelFormController = () => {
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'),
},
webhook: {
required: true,
trigger: ['input', 'blur'],
message: '请输入飞书webhook地址',
},
}
/**
* 表单配置
* @type {ComputedRef<FormConfig>}
* @description 生成飞书通知渠道表单的字段配置
*/
const config = computed(() => [
useFormInput($t('t_2_1745289353944'), 'name'),
useFormInput('飞书WebHook地址', 'webhook'),
useFormInput('飞书WebHook密钥可选', 'secret', {}, { showRequireMark: false }),
])
/**
* 提交表单
* @async
* @function submitForm
* @description 验证并提交飞书通知渠道表单
* @param {any} params - 表单参数
* @param {Ref<FormInst>} formRef - 表单实例引用
* @returns {Promise<boolean>} 提交成功返回true失败返回false
*/
const submitForm = async (
{ config, ...other }: AddReportParams<ReportFeishu>,
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,
feishuChannelForm,
submitForm,
}
}
/**
* Webhook通知渠道表单控制器
* @function useWebhookChannelFormController
* @description 提供Webhook通知渠道表单的配置、规则和提交方法
* @returns {object} 返回表单相关配置、规则和方法
*/
export const useWebhookChannelFormController = () => {
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'),
},
url: {
required: true,
trigger: ['input', 'blur'],
message: '请输入WebHook回调地址',
},
}
/**
* 表单配置
* @type {ComputedRef<FormConfig>}
* @description 生成Webhook通知渠道表单的字段配置
*/
const config = computed(() => [
useFormInput($t('t_2_1745289353944'), 'name'),
useFormInput('WebHook回调地址', 'url'),
useFormTextarea('WebHook推送通知回调数据可选', 'data', { rows: 3 }, { showRequireMark: false }),
useFormSelect('请求方式', 'method', [
{ label: 'POST', value: 'post' },
{ label: 'GET', value: 'get' },
]),
useFormTextarea('WebHook请求头可选', 'headers', { rows: 3 }, { showRequireMark: false }),
useFormSwitch('忽略SSL/TLS证书错误', 'ignore_ssl'),
])
/**
* 提交表单
* @async
* @function submitForm
* @description 验证并提交Webhook通知渠道表单
* @param {any} params - 表单参数
* @param {Ref<FormInst>} formRef - 表单实例引用
* @returns {Promise<boolean>} 提交成功返回true失败返回false
*/
const submitForm = async (
{ config, ...other }: AddReportParams<ReportWebhook>,
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,
webhookChannelForm,
submitForm,
}
}
/**
* 钉钉通知渠道表单控制器
* @function useDingtalkChannelFormController
* @description 提供钉钉通知渠道表单的配置、规则和提交方法
* @returns {object} 返回表单相关配置、规则和方法
*/
export const useDingtalkChannelFormController = () => {
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'),
},
webhook: {
required: true,
trigger: ['input', 'blur'],
message: '请输入钉钉webhook地址',
},
}
/**
* 表单配置
* @type {ComputedRef<FormConfig>}
* @description 生成钉钉通知渠道表单的字段配置
*/
const config = computed(() => [
useFormInput($t('t_2_1745289353944'), 'name'),
useFormInput('钉钉WebHook地址', 'webhook'),
useFormInput('钉钉WebHook密钥可选', 'secret', {}, { showRequireMark: false }),
])
/**
* 提交表单
* @async
* @function submitForm
* @description 验证并提交钉钉通知渠道表单
* @param {any} params - 表单参数
* @param {Ref<FormInst>} formRef - 表单实例引用
* @returns {Promise<boolean>} 提交成功返回true失败返回false
*/
const submitForm = async (
{ config, ...other }: AddReportParams<ReportDingtalk>,
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,
dingtalkChannelForm,
submitForm,
}
}

View File

@@ -1,6 +1,6 @@
import { NCard, NButton, NList, NListItem, NTag, NSpace, NGrid, NGridItem, NSwitch } from 'naive-ui'
import { useController } from '../useController'
import { useStore } from '../useStore'
import { useController } from '@settings/useController'
import { useStore } from '@settings/useStore'
import SvgIcon from '@components/SvgIcon'
import { $t } from '@locales/index'
@@ -11,8 +11,16 @@ export default defineComponent({
name: 'NotificationSettings',
setup() {
const { notifyChannels, channelTypes } = useStore()
const { openAddEmailChannelModal, editChannelConfig, testChannelConfig, confirmDeleteChannel, handleEnableChange } =
useController()
const {
openAddEmailChannelModal,
openAddFeishuChannelModal,
openAddWebhookChannelModal,
openAddDingtalkChannelModal,
editChannelConfig,
testChannelConfig,
confirmDeleteChannel,
handleEnableChange,
} = useController()
// 获取已配置的渠道数量
const getConfiguredCount = (type: string) => {
@@ -26,13 +34,36 @@ export default defineComponent({
// 根据渠道类型和配置状态获取操作按钮
const getChannelActionButton = (type: string) => {
// 目前只有邮件通知是可配置的
// 根据类型返回对应的按钮
if (type === 'mail') {
return (
<NButton strong secondary type="primary" onClick={() => openAddEmailChannelModal(getConfiguredCount(type))}>
{$t('t_1_1746676859550')}
</NButton>
)
} else if (type === 'feishu') {
return (
<NButton strong secondary type="primary" onClick={() => openAddFeishuChannelModal(getConfiguredCount(type))}>
{$t('t_1_1746676859550')}
</NButton>
)
} else if (type === 'webhook') {
return (
<NButton strong secondary type="primary" onClick={() => openAddWebhookChannelModal(getConfiguredCount(type))}>
{$t('t_1_1746676859550')}
</NButton>
)
} else if (type === 'dingtalk') {
return (
<NButton
strong
secondary
type="primary"
onClick={() => openAddDingtalkChannelModal(getConfiguredCount(type))}
>
{$t('t_1_1746676859550')}
</NButton>
)
}
// 其他渠道暂未支持
return (
@@ -50,18 +81,6 @@ export default defineComponent({
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'),
@@ -74,6 +93,18 @@ export default defineComponent({
description: $t('t_12_1746676860503'),
color: '#531dab',
},
{
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',
},
]
return () => (
<div class="notification-settings">

View File

@@ -1,13 +1,16 @@
import { FormInst, FormItemRule, FormRules } from 'naive-ui'
import { FormRules } from 'naive-ui'
import md5 from 'crypto-js/md5'
import { useFormHooks, useModal, useDialog, useForm, useMessage, useLoadingMask } from '@baota/naive-ui/hooks'
import { clearCookie, clearLocal, clearSession } from '@baota/utils/browser'
import { clearCookie, clearSession } from '@baota/utils/browser'
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'
import EmailChannelModel from './components/channel/EmailChannelModel'
import FeishuChannelModel from './components/channel/FeishuChannelModel'
import WebhookChannelModel from './components/channel/WebhookChannelModel'
import DingtalkChannelModel from './components/channel/DingtalkChannelModel'
import type { ReportMail, SaveSettingParams, ReportType } from '@/types/setting'
const {
// 标签页
@@ -22,16 +25,13 @@ const {
// 通知设置
fetchNotifyChannels,
notifyChannels,
// 邮箱通知渠道表单
emailChannelForm,
addReportChannel,
updateReportChannel,
testReportChannel,
deleteReportChannel,
} = useStore()
const message = useMessage()
const { handleError } = useError()
const { useFormInput, useFormInputNumber, useFormSwitch, useFormTextarea, useFormSlot } = useFormHooks()
const { useFormInput, useFormInputNumber, useFormSwitch, useFormTextarea } = useFormHooks()
/**
* 设置页面业务逻辑控制器
@@ -116,7 +116,64 @@ export const useController = () => {
useModal({
title: $t('t_18_1745457490931'),
area: 650,
component: EmailChannelForm,
component: EmailChannelModel,
footer: true,
})
}
/**
* 打开添加飞书通知渠道弹窗
* @function openAddFeishuChannelModal
* @description 打开添加飞书通知渠道的模态框,并在关闭后刷新通知渠道列表
* @returns {void} 无返回值
*/
const openAddFeishuChannelModal = (limit: number = 1) => {
if (limit >= 1) {
message.warning($t('t_16_1746773356568'))
return
}
useModal({
title: $t('t_9_1746676857164'),
area: 650,
component: FeishuChannelModel,
footer: true,
})
}
/**
* 打开添加Webhook通知渠道弹窗
* @function openAddWebhookChannelModal
* @description 打开添加Webhook通知渠道的模态框并在关闭后刷新通知渠道列表
* @returns {void} 无返回值
*/
const openAddWebhookChannelModal = (limit: number = 1) => {
if (limit >= 1) {
message.warning($t('t_16_1746773356568'))
return
}
useModal({
title: $t('t_11_1746676859158'),
area: 650,
component: WebhookChannelModel,
footer: true,
})
}
/**
* 打开添加钉钉通知渠道弹窗
* @function openAddDingtalkChannelModal
* @description 打开添加钉钉通知渠道的模态框,并在关闭后刷新通知渠道列表
* @returns {void} 无返回值
*/
const openAddDingtalkChannelModal = (limit: number = 1) => {
if (limit >= 1) {
message.warning($t('t_16_1746773356568'))
return
}
useModal({
title: '添加钉钉通知',
area: 650,
component: DingtalkChannelModel,
footer: true,
})
}
@@ -160,13 +217,46 @@ export const useController = () => {
* @param {ReportType<ReportMail>} item - 要查看的通知渠道对象
* @returns {void} 无返回值
*/
const editChannelConfig = (item: ReportType<ReportMail>) => {
const editChannelConfig = (item: ReportType<any>) => {
console.log(item)
if (item.type === 'mail') {
useModal({
title: $t('t_0_1745895057404'),
area: 650,
component: EmailChannelForm,
component: EmailChannelModel,
componentProps: {
data: item,
},
footer: true,
onClose: () => fetchNotifyChannels(),
})
} else if (item.type === 'feishu') {
useModal({
title: $t('t_9_1746676857164'),
area: 650,
component: FeishuChannelModel,
componentProps: {
data: item,
},
footer: true,
onClose: () => fetchNotifyChannels(),
})
} else if (item.type === 'webhook') {
useModal({
title: $t('t_11_1746676859158'),
area: 650,
component: WebhookChannelModel,
componentProps: {
data: item,
},
footer: true,
onClose: () => fetchNotifyChannels(),
})
} else if (item.type === 'dingtalk') {
useModal({
title: '编辑钉钉通知',
area: 650,
component: DingtalkChannelModel,
componentProps: {
data: item,
},
@@ -183,8 +273,8 @@ export const useController = () => {
* @param {ReportType<ReportMail>} item - 要测试的通知渠道对象
* @returns {void} 无返回值
*/
const testChannelConfig = (item: ReportType<ReportMail>) => {
if (item.type !== 'mail') {
const testChannelConfig = (item: ReportType<any>) => {
if (item.type !== 'mail' && item.type !== 'feishu' && item.type !== 'webhook') {
message.warning($t('t_19_1746773352558'))
return
}
@@ -239,6 +329,9 @@ export const useController = () => {
fetchAllSettings,
handleSaveGeneralSettings,
openAddEmailChannelModal,
openAddFeishuChannelModal,
openAddWebhookChannelModal,
openAddDingtalkChannelModal,
handleEnableChange,
editChannelConfig,
testChannelConfig,
@@ -328,109 +421,3 @@ export const useGeneralSettingsController = () => {
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,
}
}

View File

@@ -19,6 +19,9 @@ import type {
DeleteReportParams,
ReportMail,
TestReportParams,
ReportFeishu,
ReportWebhook,
ReportDingtalk,
} from '@/types/setting'
const { handleError } = useError()
@@ -77,6 +80,33 @@ export const useSettingsStore = defineStore('settings-store', () => {
password: '',
})
// 飞书通知渠道表单
const feishuChannelForm = ref<ReportFeishu>({
name: '',
enabled: '1',
webhook: '', // 飞书webhook地址
secret: '', // 飞书webhook加密密钥可选
})
// Webhook通知渠道表单
const webhookChannelForm = ref<ReportWebhook>({
name: '',
enabled: '1',
url: '', // WebHook回调地址
data: '', // WebHook推送通知回调数据可选
method: 'post', // 请求方式
headers: '', // WebHook请求头可选
ignore_ssl: false, // 忽略SSL/TLS证书错误
})
// 钉钉通知渠道表单
const dingtalkChannelForm = ref<ReportDingtalk>({
name: '',
enabled: '1',
webhook: '', // 钉钉webhook地址
secret: '', // 钉钉webhook加密密钥可选
})
// 关于页面数据
const aboutInfo = ref({
version: '1.0.0',
@@ -217,6 +247,9 @@ export const useSettingsStore = defineStore('settings-store', () => {
notifyChannels,
channelTypes,
emailChannelForm,
feishuChannelForm,
webhookChannelForm,
dingtalkChannelForm,
aboutInfo,
// 方法