refactor(notify): 重构通知设置,支持自定义Webhook管理与模板配置
This commit is contained in:
@@ -9,19 +9,22 @@ export type { OpenAPIConfig } from './core/OpenAPI';
|
||||
|
||||
export type { ComboBoxItem } from './models/ComboBoxItem';
|
||||
export type { ComboBoxOut } from './models/ComboBoxOut';
|
||||
export type { CustomWebhook } from './models/CustomWebhook';
|
||||
export type { DispatchIn } from './models/DispatchIn';
|
||||
export type { GeneralConfig } from './models/GeneralConfig';
|
||||
export type { GeneralConfig_Game } from './models/GeneralConfig_Game';
|
||||
export type { GeneralConfig_Info } from './models/GeneralConfig_Info';
|
||||
export type { GeneralConfig_Run } from './models/GeneralConfig_Run';
|
||||
export type { GeneralConfig_Script } from './models/GeneralConfig_Script';
|
||||
export type { GeneralUserConfig } from './models/GeneralUserConfig';
|
||||
export type { GeneralUserConfig_Data } from './models/GeneralUserConfig_Data';
|
||||
export type { GeneralUserConfig_Info } from './models/GeneralUserConfig_Info';
|
||||
export type { GeneralUserConfig_Input } from './models/GeneralUserConfig_Input';
|
||||
export type { GeneralUserConfig_Output } from './models/GeneralUserConfig_Output';
|
||||
export { GetStageIn } from './models/GetStageIn';
|
||||
export type { GlobalConfig } from './models/GlobalConfig';
|
||||
export type { GlobalConfig_Function } from './models/GlobalConfig_Function';
|
||||
export type { GlobalConfig_Input } from './models/GlobalConfig_Input';
|
||||
export type { GlobalConfig_Notify } from './models/GlobalConfig_Notify';
|
||||
export type { GlobalConfig_Output } from './models/GlobalConfig_Output';
|
||||
export type { GlobalConfig_Start } from './models/GlobalConfig_Start';
|
||||
export type { GlobalConfig_UI } from './models/GlobalConfig_UI';
|
||||
export type { GlobalConfig_Update } from './models/GlobalConfig_Update';
|
||||
@@ -40,9 +43,10 @@ export type { MaaConfig_Run } from './models/MaaConfig_Run';
|
||||
export type { MaaPlanConfig } from './models/MaaPlanConfig';
|
||||
export type { MaaPlanConfig_Info } from './models/MaaPlanConfig_Info';
|
||||
export type { MaaPlanConfig_Item } from './models/MaaPlanConfig_Item';
|
||||
export type { MaaUserConfig } from './models/MaaUserConfig';
|
||||
export type { MaaUserConfig_Data } from './models/MaaUserConfig_Data';
|
||||
export type { MaaUserConfig_Info } from './models/MaaUserConfig_Info';
|
||||
export type { MaaUserConfig_Input } from './models/MaaUserConfig_Input';
|
||||
export type { MaaUserConfig_Output } from './models/MaaUserConfig_Output';
|
||||
export type { MaaUserConfig_Task } from './models/MaaUserConfig_Task';
|
||||
export type { NoticeOut } from './models/NoticeOut';
|
||||
export type { OutBase } from './models/OutBase';
|
||||
|
||||
35
frontend/src/api/models/CustomWebhook.ts
Normal file
35
frontend/src/api/models/CustomWebhook.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type CustomWebhook = {
|
||||
/**
|
||||
* Webhook唯一标识
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Webhook名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Webhook URL
|
||||
*/
|
||||
url: string;
|
||||
/**
|
||||
* 消息模板
|
||||
*/
|
||||
template: string;
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
* 自定义请求头
|
||||
*/
|
||||
headers?: (Record<string, string> | null);
|
||||
/**
|
||||
* 请求方法
|
||||
*/
|
||||
method?: ('POST' | 'GET' | null);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import type { GeneralUserConfig_Data } from './GeneralUserConfig_Data';
|
||||
import type { GeneralUserConfig_Info } from './GeneralUserConfig_Info';
|
||||
import type { UserConfig_Notify } from './UserConfig_Notify';
|
||||
export type GeneralUserConfig = {
|
||||
export type GeneralUserConfig_Input = {
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
22
frontend/src/api/models/GeneralUserConfig_Output.ts
Normal file
22
frontend/src/api/models/GeneralUserConfig_Output.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { GeneralUserConfig_Data } from './GeneralUserConfig_Data';
|
||||
import type { GeneralUserConfig_Info } from './GeneralUserConfig_Info';
|
||||
import type { UserConfig_Notify } from './UserConfig_Notify';
|
||||
export type GeneralUserConfig_Output = {
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
Info?: (GeneralUserConfig_Info | null);
|
||||
/**
|
||||
* 用户数据
|
||||
*/
|
||||
Data?: (GeneralUserConfig_Data | null);
|
||||
/**
|
||||
* 单独通知
|
||||
*/
|
||||
Notify?: (UserConfig_Notify | null);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { GlobalConfig_Start } from './GlobalConfig_Start';
|
||||
import type { GlobalConfig_UI } from './GlobalConfig_UI';
|
||||
import type { GlobalConfig_Update } from './GlobalConfig_Update';
|
||||
import type { GlobalConfig_Voice } from './GlobalConfig_Voice';
|
||||
export type GlobalConfig = {
|
||||
export type GlobalConfig_Input = {
|
||||
/**
|
||||
* 功能相关配置
|
||||
*/
|
||||
@@ -2,6 +2,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CustomWebhook } from './CustomWebhook';
|
||||
export type GlobalConfig_Notify = {
|
||||
/**
|
||||
* 任务结果推送时机
|
||||
@@ -48,12 +49,8 @@ export type GlobalConfig_Notify = {
|
||||
*/
|
||||
ServerChanKey?: (string | null);
|
||||
/**
|
||||
* 是否使用企微Webhook推送
|
||||
* 自定义Webhook列表
|
||||
*/
|
||||
IfCompanyWebHookBot?: (boolean | null);
|
||||
/**
|
||||
* 企微Webhook Bot URL
|
||||
*/
|
||||
CompanyWebHookBotUrl?: (string | null);
|
||||
CustomWebhooks?: (Array<CustomWebhook> | null);
|
||||
};
|
||||
|
||||
|
||||
37
frontend/src/api/models/GlobalConfig_Output.ts
Normal file
37
frontend/src/api/models/GlobalConfig_Output.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { GlobalConfig_Function } from './GlobalConfig_Function';
|
||||
import type { GlobalConfig_Notify } from './GlobalConfig_Notify';
|
||||
import type { GlobalConfig_Start } from './GlobalConfig_Start';
|
||||
import type { GlobalConfig_UI } from './GlobalConfig_UI';
|
||||
import type { GlobalConfig_Update } from './GlobalConfig_Update';
|
||||
import type { GlobalConfig_Voice } from './GlobalConfig_Voice';
|
||||
export type GlobalConfig_Output = {
|
||||
/**
|
||||
* 功能相关配置
|
||||
*/
|
||||
Function?: (GlobalConfig_Function | null);
|
||||
/**
|
||||
* 语音相关配置
|
||||
*/
|
||||
Voice?: (GlobalConfig_Voice | null);
|
||||
/**
|
||||
* 启动相关配置
|
||||
*/
|
||||
Start?: (GlobalConfig_Start | null);
|
||||
/**
|
||||
* 界面相关配置
|
||||
*/
|
||||
UI?: (GlobalConfig_UI | null);
|
||||
/**
|
||||
* 通知相关配置
|
||||
*/
|
||||
Notify?: (GlobalConfig_Notify | null);
|
||||
/**
|
||||
* 更新相关配置
|
||||
*/
|
||||
Update?: (GlobalConfig_Update | null);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { MaaUserConfig_Data } from './MaaUserConfig_Data';
|
||||
import type { MaaUserConfig_Info } from './MaaUserConfig_Info';
|
||||
import type { MaaUserConfig_Task } from './MaaUserConfig_Task';
|
||||
import type { UserConfig_Notify } from './UserConfig_Notify';
|
||||
export type MaaUserConfig = {
|
||||
export type MaaUserConfig_Input = {
|
||||
/**
|
||||
* 基础信息
|
||||
*/
|
||||
27
frontend/src/api/models/MaaUserConfig_Output.ts
Normal file
27
frontend/src/api/models/MaaUserConfig_Output.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { MaaUserConfig_Data } from './MaaUserConfig_Data';
|
||||
import type { MaaUserConfig_Info } from './MaaUserConfig_Info';
|
||||
import type { MaaUserConfig_Task } from './MaaUserConfig_Task';
|
||||
import type { UserConfig_Notify } from './UserConfig_Notify';
|
||||
export type MaaUserConfig_Output = {
|
||||
/**
|
||||
* 基础信息
|
||||
*/
|
||||
Info?: (MaaUserConfig_Info | null);
|
||||
/**
|
||||
* 用户数据
|
||||
*/
|
||||
Data?: (MaaUserConfig_Data | null);
|
||||
/**
|
||||
* 任务列表
|
||||
*/
|
||||
Task?: (MaaUserConfig_Task | null);
|
||||
/**
|
||||
* 单独通知
|
||||
*/
|
||||
Notify?: (UserConfig_Notify | null);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { GlobalConfig } from './GlobalConfig';
|
||||
import type { GlobalConfig_Output } from './GlobalConfig_Output';
|
||||
export type SettingGetOut = {
|
||||
/**
|
||||
* 状态码
|
||||
@@ -19,6 +19,6 @@ export type SettingGetOut = {
|
||||
/**
|
||||
* 全局设置数据
|
||||
*/
|
||||
data: GlobalConfig;
|
||||
data: GlobalConfig_Output;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { GlobalConfig } from './GlobalConfig';
|
||||
import type { GlobalConfig_Input } from './GlobalConfig_Input';
|
||||
export type SettingUpdateIn = {
|
||||
/**
|
||||
* 全局设置需要更新的数据
|
||||
*/
|
||||
data: GlobalConfig;
|
||||
data: GlobalConfig_Input;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CustomWebhook } from './CustomWebhook';
|
||||
export type UserConfig_Notify = {
|
||||
/**
|
||||
* 是否启用通知
|
||||
@@ -32,12 +33,8 @@ export type UserConfig_Notify = {
|
||||
*/
|
||||
ServerChanKey?: (string | null);
|
||||
/**
|
||||
* 是否使用Webhook推送
|
||||
* 用户自定义Webhook列表
|
||||
*/
|
||||
IfCompanyWebHookBot?: (boolean | null);
|
||||
/**
|
||||
* 企微Webhook Bot URL
|
||||
*/
|
||||
CompanyWebHookBotUrl?: (string | null);
|
||||
CustomWebhooks?: (Array<CustomWebhook> | null);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { GeneralUserConfig } from './GeneralUserConfig';
|
||||
import type { MaaUserConfig } from './MaaUserConfig';
|
||||
import type { GeneralUserConfig_Output } from './GeneralUserConfig_Output';
|
||||
import type { MaaUserConfig_Output } from './MaaUserConfig_Output';
|
||||
export type UserCreateOut = {
|
||||
/**
|
||||
* 状态码
|
||||
@@ -24,6 +24,6 @@ export type UserCreateOut = {
|
||||
/**
|
||||
* 用户配置数据
|
||||
*/
|
||||
data: (MaaUserConfig | GeneralUserConfig);
|
||||
data: (MaaUserConfig_Output | GeneralUserConfig_Output);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { GeneralUserConfig } from './GeneralUserConfig';
|
||||
import type { MaaUserConfig } from './MaaUserConfig';
|
||||
import type { GeneralUserConfig_Output } from './GeneralUserConfig_Output';
|
||||
import type { MaaUserConfig_Output } from './MaaUserConfig_Output';
|
||||
import type { UserIndexItem } from './UserIndexItem';
|
||||
export type UserGetOut = {
|
||||
/**
|
||||
@@ -25,6 +25,6 @@ export type UserGetOut = {
|
||||
/**
|
||||
* 用户数据字典, key来自于index列表的uid
|
||||
*/
|
||||
data: Record<string, (MaaUserConfig | GeneralUserConfig)>;
|
||||
data: Record<string, (MaaUserConfig_Output | GeneralUserConfig_Output)>;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { GeneralUserConfig } from './GeneralUserConfig';
|
||||
import type { MaaUserConfig } from './MaaUserConfig';
|
||||
import type { GeneralUserConfig_Input } from './GeneralUserConfig_Input';
|
||||
import type { MaaUserConfig_Input } from './MaaUserConfig_Input';
|
||||
export type UserUpdateIn = {
|
||||
/**
|
||||
* 所属脚本ID
|
||||
@@ -16,6 +16,6 @@ export type UserUpdateIn = {
|
||||
/**
|
||||
* 用户更新数据
|
||||
*/
|
||||
data: (MaaUserConfig | GeneralUserConfig);
|
||||
data: (MaaUserConfig_Input | GeneralUserConfig_Input);
|
||||
};
|
||||
|
||||
|
||||
@@ -994,6 +994,86 @@ export class Service {
|
||||
url: '/api/setting/test_notify',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 创建自定义Webhook
|
||||
* 创建自定义Webhook
|
||||
* @param requestBody
|
||||
* @returns OutBase Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createWebhookApiSettingWebhookCreatePost(
|
||||
requestBody: Record<string, any>,
|
||||
): CancelablePromise<OutBase> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/api/setting/webhook/create',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 更新自定义Webhook
|
||||
* 更新自定义Webhook
|
||||
* @param requestBody
|
||||
* @returns OutBase Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static updateWebhookApiSettingWebhookUpdatePost(
|
||||
requestBody: Record<string, any>,
|
||||
): CancelablePromise<OutBase> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/api/setting/webhook/update',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 删除自定义Webhook
|
||||
* 删除自定义Webhook
|
||||
* @param requestBody
|
||||
* @returns OutBase Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteWebhookApiSettingWebhookDeletePost(
|
||||
requestBody: Record<string, any>,
|
||||
): CancelablePromise<OutBase> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/api/setting/webhook/delete',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 测试自定义Webhook
|
||||
* 测试自定义Webhook
|
||||
* @param requestBody
|
||||
* @returns OutBase Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static testWebhookApiSettingWebhookTestPost(
|
||||
requestBody: Record<string, any>,
|
||||
): CancelablePromise<OutBase> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/api/setting/webhook/test',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 检查更新
|
||||
* @param requestBody
|
||||
|
||||
524
frontend/src/components/WebhookManager.vue
Normal file
524
frontend/src/components/WebhookManager.vue
Normal file
@@ -0,0 +1,524 @@
|
||||
<template>
|
||||
<div class="webhook-manager">
|
||||
<div class="webhook-header">
|
||||
<h3>自定义 Webhook 通知</h3>
|
||||
<a-button type="primary" @click="showAddModal" size="middle">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
添加 Webhook
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<!-- Webhook 列表 -->
|
||||
<div class="webhook-list" v-if="webhooks.length > 0">
|
||||
<div
|
||||
v-for="webhook in webhooks"
|
||||
:key="webhook.id"
|
||||
class="webhook-item"
|
||||
:class="{ 'webhook-disabled': !webhook.enabled }"
|
||||
>
|
||||
<div class="webhook-info">
|
||||
<div class="webhook-name">
|
||||
<span class="name-text">{{ webhook.name }}</span>
|
||||
<a-tag :color="webhook.enabled ? 'green' : 'red'" size="small">
|
||||
{{ webhook.enabled ? '启用' : '禁用' }}
|
||||
</a-tag>
|
||||
</div>
|
||||
<div class="webhook-url">{{ webhook.url }}</div>
|
||||
</div>
|
||||
<div class="webhook-actions">
|
||||
<a-button type="text" size="small" @click="testWebhook(webhook)" :loading="testingWebhooks[webhook.id]">
|
||||
<template #icon>
|
||||
<PlayCircleOutlined />
|
||||
</template>
|
||||
测试
|
||||
</a-button>
|
||||
<a-button type="text" size="small" @click="editWebhook(webhook)">
|
||||
<template #icon>
|
||||
<EditOutlined />
|
||||
</template>
|
||||
编辑
|
||||
</a-button>
|
||||
<a-button type="text" size="small" danger @click="deleteWebhook(webhook)">
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
删除
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<ApiOutlined />
|
||||
</div>
|
||||
<div class="empty-text">暂无自定义 Webhook</div>
|
||||
<div class="empty-description">点击上方按钮添加您的第一个 Webhook</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑 Webhook 弹窗 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
:title="isEditing ? '编辑 Webhook' : '添加 Webhook'"
|
||||
width="800px"
|
||||
:ok-text="isEditing ? '更新' : '添加'"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
:confirm-loading="submitting"
|
||||
>
|
||||
<a-form :model="formData" layout="vertical" ref="formRef">
|
||||
<!-- 模板选择放在最上面 -->
|
||||
<a-form-item label="选择模板">
|
||||
<a-select
|
||||
v-model:value="selectedTemplate"
|
||||
placeholder="选择预设模板或自定义"
|
||||
@change="applyTemplate"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option v-for="template in WEBHOOK_TEMPLATES" :key="template.name" :value="template.name">
|
||||
{{ template.name }} - {{ template.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="Webhook 名称" name="name" :rules="[{ required: true, message: '请输入 Webhook 名称' }]">
|
||||
<a-input v-model:value="formData.name" placeholder="请输入 Webhook 名称" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="请求方法" name="method">
|
||||
<a-select v-model:value="formData.method">
|
||||
<a-select-option value="POST">POST</a-select-option>
|
||||
<a-select-option value="GET">GET</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item label="Webhook URL" name="url" :rules="[{ required: true, message: '请输入 Webhook URL' }]">
|
||||
<a-input v-model:value="formData.url" placeholder="https://your-webhook-url.com/api/notify" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="消息模板">
|
||||
<a-textarea
|
||||
v-model:value="formData.template"
|
||||
:rows="6"
|
||||
placeholder="请输入消息模板,支持变量: {title}, {content}, {datetime}, {date}, {time}"
|
||||
/>
|
||||
<div class="template-help">
|
||||
<a-typography-text type="secondary" style="font-size: 12px;">
|
||||
支持的变量:
|
||||
<a-tag size="small" v-for="variable in TEMPLATE_VARIABLES" :key="variable.name">
|
||||
{{ variable.name }}
|
||||
</a-tag>
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="自定义请求头 (可选)">
|
||||
<div class="headers-input">
|
||||
<div v-for="(header, index) in formData.headersList" :key="index" class="header-row">
|
||||
<a-input
|
||||
v-model:value="header.key"
|
||||
placeholder="Header 名称"
|
||||
style="width: 40%; margin-right: 8px;"
|
||||
/>
|
||||
<a-input
|
||||
v-model:value="header.value"
|
||||
placeholder="Header 值"
|
||||
style="width: 40%; margin-right: 8px;"
|
||||
/>
|
||||
<a-button type="text" danger @click="removeHeader(index)" size="small">
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
<a-button type="dashed" @click="addHeader" size="small" style="width: 100%; margin-top: 8px;">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
添加请求头
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-checkbox v-model:checked="formData.enabled">启用此 Webhook</a-checkbox>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
PlayCircleOutlined,
|
||||
ApiOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import type { CustomWebhook } from '@/types/settings'
|
||||
import { WEBHOOK_TEMPLATES, TEMPLATE_VARIABLES } from '@/utils/webhookTemplates'
|
||||
import { Service } from '@/api/services/Service'
|
||||
|
||||
const props = defineProps<{
|
||||
webhooks: CustomWebhook[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:webhooks': [webhooks: CustomWebhook[]]
|
||||
'change': []
|
||||
}>()
|
||||
|
||||
// 响应式数据
|
||||
const modalVisible = ref(false)
|
||||
const isEditing = ref(false)
|
||||
const submitting = ref(false)
|
||||
const selectedTemplate = ref<string>()
|
||||
const testingWebhooks = ref<Record<string, boolean>>({})
|
||||
const formRef = ref()
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
url: '',
|
||||
template: '',
|
||||
method: 'POST' as 'POST' | 'GET',
|
||||
enabled: true,
|
||||
headersList: [] as Array<{ key: string; value: string }>
|
||||
})
|
||||
|
||||
// 计算属性
|
||||
const webhooks = computed({
|
||||
get: () => props.webhooks || [],
|
||||
set: (value) => emit('update:webhooks', value)
|
||||
})
|
||||
|
||||
// 监听 webhooks 变化
|
||||
watch(() => props.webhooks, (newWebhooks) => {
|
||||
// 可以在这里处理 webhooks 变化的逻辑
|
||||
}, { deep: true })
|
||||
|
||||
// 显示添加弹窗
|
||||
const showAddModal = () => {
|
||||
isEditing.value = false
|
||||
resetForm()
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
// 编辑 Webhook
|
||||
const editWebhook = (webhook: CustomWebhook) => {
|
||||
isEditing.value = true
|
||||
formData.id = webhook.id
|
||||
formData.name = webhook.name
|
||||
formData.url = webhook.url
|
||||
formData.template = webhook.template
|
||||
formData.method = webhook.method || 'POST'
|
||||
formData.enabled = webhook.enabled
|
||||
|
||||
// 转换 headers 为列表格式
|
||||
formData.headersList = webhook.headers
|
||||
? Object.entries(webhook.headers).map(([key, value]) => ({ key, value }))
|
||||
: []
|
||||
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
// 删除 Webhook
|
||||
const deleteWebhook = (webhook: CustomWebhook) => {
|
||||
const newWebhooks = webhooks.value.filter(w => w.id !== webhook.id)
|
||||
webhooks.value = newWebhooks
|
||||
emit('change')
|
||||
message.success('Webhook 删除成功')
|
||||
}
|
||||
|
||||
// 测试 Webhook
|
||||
const testWebhook = async (webhook: CustomWebhook) => {
|
||||
testingWebhooks.value[webhook.id] = true
|
||||
|
||||
try {
|
||||
const response = await Service.testWebhookApiSettingWebhookTestPost({
|
||||
id: webhook.id,
|
||||
name: webhook.name,
|
||||
url: webhook.url,
|
||||
template: webhook.template,
|
||||
method: webhook.method || 'POST',
|
||||
enabled: webhook.enabled,
|
||||
headers: webhook.headers || {}
|
||||
})
|
||||
|
||||
if (response.code === 200) {
|
||||
message.success(`Webhook "${webhook.name}" 测试成功`)
|
||||
} else {
|
||||
message.error(`Webhook 测试失败: ${response.message || '未知错误'}`)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Webhook测试错误:', error)
|
||||
message.error(`Webhook 测试失败: ${error.response?.data?.message || error.message || '网络错误'}`)
|
||||
} finally {
|
||||
testingWebhooks.value[webhook.id] = false
|
||||
}
|
||||
}
|
||||
|
||||
// 应用模板
|
||||
const applyTemplate = (templateName: string) => {
|
||||
if (!templateName) {
|
||||
// 清空模板时不做任何操作
|
||||
return
|
||||
}
|
||||
|
||||
const template = WEBHOOK_TEMPLATES.find(t => t.name === templateName)
|
||||
if (template) {
|
||||
// 强制清空所有内容再应用新模板
|
||||
formData.name = ''
|
||||
formData.url = template.example || ''
|
||||
formData.template = template.template
|
||||
formData.method = template.method
|
||||
formData.headersList = []
|
||||
|
||||
// 设置默认请求头
|
||||
if (template.headers) {
|
||||
formData.headersList = Object.entries(template.headers).map(([key, value]) => ({ key, value }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加请求头
|
||||
const addHeader = () => {
|
||||
formData.headersList.push({ key: '', value: '' })
|
||||
}
|
||||
|
||||
// 删除请求头
|
||||
const removeHeader = (index: number) => {
|
||||
formData.headersList.splice(index, 1)
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formData.id = ''
|
||||
formData.name = ''
|
||||
formData.url = ''
|
||||
formData.template = ''
|
||||
formData.method = 'POST'
|
||||
formData.enabled = true
|
||||
formData.headersList = []
|
||||
selectedTemplate.value = undefined
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
submitting.value = true
|
||||
|
||||
// 转换 headersList 为 headers 对象
|
||||
const headers: Record<string, string> = {}
|
||||
formData.headersList.forEach(header => {
|
||||
if (header.key && header.value) {
|
||||
headers[header.key] = header.value
|
||||
}
|
||||
})
|
||||
|
||||
const webhookData: CustomWebhook = {
|
||||
id: formData.id || `webhook_${Date.now()}`,
|
||||
name: formData.name,
|
||||
url: formData.url,
|
||||
template: formData.template,
|
||||
method: formData.method,
|
||||
enabled: formData.enabled,
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined
|
||||
}
|
||||
|
||||
let newWebhooks: CustomWebhook[]
|
||||
|
||||
if (isEditing.value) {
|
||||
// 更新现有 Webhook
|
||||
newWebhooks = webhooks.value.map(w =>
|
||||
w.id === webhookData.id ? webhookData : w
|
||||
)
|
||||
message.success('Webhook 更新成功')
|
||||
} else {
|
||||
// 添加新 Webhook
|
||||
newWebhooks = [...webhooks.value, webhookData]
|
||||
message.success('Webhook 添加成功')
|
||||
}
|
||||
|
||||
webhooks.value = newWebhooks
|
||||
emit('change')
|
||||
modalVisible.value = false
|
||||
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
modalVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.webhook-manager {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.webhook-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.webhook-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.webhook-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.webhook-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: 8px;
|
||||
background: var(--ant-color-bg-container);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.webhook-item:hover {
|
||||
border-color: var(--ant-color-primary);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.webhook-item.webhook-disabled {
|
||||
opacity: 0.6;
|
||||
background: var(--ant-color-bg-layout);
|
||||
}
|
||||
|
||||
.webhook-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.webhook-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.name-text {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.webhook-url {
|
||||
font-size: 12px;
|
||||
color: var(--ant-color-text-secondary);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.webhook-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 48px 24px;
|
||||
color: var(--ant-color-text-secondary);
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.empty-description {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.template-help {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.headers-input {
|
||||
border: 1px solid var(--ant-color-border);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
background: var(--ant-color-bg-layout);
|
||||
}
|
||||
|
||||
.header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 深色模式适配 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.webhook-item {
|
||||
border-color: var(--ant-color-border-secondary);
|
||||
}
|
||||
|
||||
.webhook-item.webhook-disabled {
|
||||
background: var(--ant-color-bg-base);
|
||||
}
|
||||
|
||||
.headers-input {
|
||||
background: var(--ant-color-bg-base);
|
||||
border-color: var(--ant-color-border-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.webhook-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.webhook-actions {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.webhook-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -154,8 +154,7 @@ export function useScriptApi() {
|
||||
ToAddress: maaUserData.Notify?.ToAddress !== undefined ? maaUserData.Notify.ToAddress : '',
|
||||
IfServerChan: maaUserData.Notify?.IfServerChan !== undefined ? maaUserData.Notify.IfServerChan : false,
|
||||
ServerChanKey: maaUserData.Notify?.ServerChanKey !== undefined ? maaUserData.Notify.ServerChanKey : '',
|
||||
IfCompanyWebHookBot: maaUserData.Notify?.IfCompanyWebHookBot !== undefined ? maaUserData.Notify.IfCompanyWebHookBot : false,
|
||||
CompanyWebHookBotUrl: maaUserData.Notify?.CompanyWebHookBotUrl !== undefined ? maaUserData.Notify.CompanyWebHookBotUrl : '',
|
||||
CustomWebhooks: maaUserData.Notify?.CustomWebhooks !== undefined ? maaUserData.Notify.CustomWebhooks : [],
|
||||
},
|
||||
Data: {
|
||||
LastAnnihilationDate: maaUserData.Data?.LastAnnihilationDate !== undefined ? maaUserData.Data.LastAnnihilationDate : '',
|
||||
@@ -188,8 +187,7 @@ export function useScriptApi() {
|
||||
ToAddress: generalUserData.Notify?.ToAddress !== undefined ? generalUserData.Notify.ToAddress : '',
|
||||
IfServerChan: generalUserData.Notify?.IfServerChan !== undefined ? generalUserData.Notify.IfServerChan : false,
|
||||
ServerChanKey: generalUserData.Notify?.ServerChanKey !== undefined ? generalUserData.Notify.ServerChanKey : '',
|
||||
IfCompanyWebHookBot: generalUserData.Notify?.IfCompanyWebHookBot !== undefined ? generalUserData.Notify.IfCompanyWebHookBot : false,
|
||||
CompanyWebHookBotUrl: generalUserData.Notify?.CompanyWebHookBotUrl !== undefined ? generalUserData.Notify.CompanyWebHookBotUrl : '',
|
||||
CustomWebhooks: generalUserData.Notify?.CustomWebhooks !== undefined ? generalUserData.Notify.CustomWebhooks : [],
|
||||
},
|
||||
Data: {
|
||||
LastProxyDate: generalUserData.Data?.LastProxyDate !== undefined ? generalUserData.Data.LastProxyDate : '',
|
||||
|
||||
@@ -109,11 +109,18 @@ export interface User {
|
||||
Status: boolean
|
||||
}
|
||||
Notify: {
|
||||
CompanyWebHookBotUrl: string
|
||||
Enabled: boolean
|
||||
IfCompanyWebHookBot: boolean
|
||||
IfSendMail: boolean
|
||||
IfSendSixStar: boolean
|
||||
CustomWebhooks: Array<{
|
||||
id: string
|
||||
name: string
|
||||
url: string
|
||||
template: string
|
||||
enabled: boolean
|
||||
headers?: Record<string, string>
|
||||
method?: 'POST' | 'GET'
|
||||
}>
|
||||
IfSendStatistic: boolean
|
||||
IfServerChan: boolean
|
||||
ServerChanChannel: string
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
// 自定义Webhook配置
|
||||
export interface CustomWebhook {
|
||||
id: string
|
||||
name: string
|
||||
url: string
|
||||
template: string
|
||||
enabled: boolean
|
||||
headers?: Record<string, string>
|
||||
method?: 'POST' | 'GET'
|
||||
}
|
||||
|
||||
// 设置相关类型定义
|
||||
export interface SettingsData {
|
||||
UI: {
|
||||
@@ -26,8 +37,7 @@ export interface SettingsData {
|
||||
ServerChanKey: string
|
||||
ServerChanChannel: string
|
||||
ServerChanTag: string
|
||||
IfCompanyWebHookBot: boolean
|
||||
CompanyWebHookBotUrl: string
|
||||
CustomWebhooks: CustomWebhook[]
|
||||
}
|
||||
Update: {
|
||||
IfAutoUpdate: boolean
|
||||
|
||||
119
frontend/src/utils/webhookTemplates.ts
Normal file
119
frontend/src/utils/webhookTemplates.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
// Webhook 模板配置
|
||||
export interface WebhookTemplate {
|
||||
name: string
|
||||
description: string
|
||||
template: string
|
||||
headers?: Record<string, string>
|
||||
method: 'POST' | 'GET'
|
||||
example?: string
|
||||
}
|
||||
|
||||
export const WEBHOOK_TEMPLATES: WebhookTemplate[] = [
|
||||
{
|
||||
name: 'Bark (iOS推送)',
|
||||
description: 'Bark是一款iOS推送通知应用',
|
||||
template: '{"title": "{title}", "body": "{content}", "sound": "default"}',
|
||||
method: 'POST',
|
||||
example: 'https://api.day.app/your_key/',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Server酱 (微信推送)',
|
||||
description: 'Server酱微信推送服务',
|
||||
template: '{"title": "{title}", "desp": "{content}"}',
|
||||
method: 'POST',
|
||||
example: 'https://sctapi.ftqq.com/your_key.send',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '企业微信机器人',
|
||||
description: '企业微信群机器人推送',
|
||||
template: '{"msgtype": "text", "text": {"content": "{title}\\n{content}"}}',
|
||||
method: 'POST',
|
||||
example: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your_key',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'DingTalk (钉钉机器人)',
|
||||
description: '钉钉群机器人推送',
|
||||
template: '{"msgtype": "text", "text": {"content": "{title}\\n{content}"}}',
|
||||
method: 'POST',
|
||||
example: 'https://oapi.dingtalk.com/robot/send?access_token=your_token',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Telegram Bot',
|
||||
description: 'Telegram机器人推送',
|
||||
template: '{"chat_id": "your_chat_id", "text": "{title}\\n{content}"}',
|
||||
method: 'POST',
|
||||
example: 'https://api.telegram.org/bot{your_bot_token}/sendMessage',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Discord Webhook',
|
||||
description: 'Discord频道Webhook推送',
|
||||
template: '{"content": "**{title}**\\n{content}"}',
|
||||
method: 'POST',
|
||||
example: 'https://discord.com/api/webhooks/your_webhook_url',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Slack Webhook',
|
||||
description: 'Slack频道Webhook推送',
|
||||
template: '{"text": "{title}\\n{content}"}',
|
||||
method: 'POST',
|
||||
example: 'https://hooks.slack.com/services/your/webhook/url',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'PushPlus (微信推送)',
|
||||
description: 'PushPlus微信推送服务',
|
||||
template: '{"token": "your_token", "title": "{title}", "content": "{content}"}',
|
||||
method: 'POST',
|
||||
example: 'http://www.pushplus.plus/send',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '自定义JSON',
|
||||
description: '自定义JSON格式推送',
|
||||
template: '{"message": "{title}: {content}", "timestamp": "{datetime}"}',
|
||||
method: 'POST',
|
||||
example: 'https://your-api.com/webhook',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '自定义GET请求',
|
||||
description: '通过GET请求发送通知',
|
||||
template: 'title={title}&content={content}&time={datetime}',
|
||||
method: 'GET',
|
||||
example: 'https://your-api.com/notify',
|
||||
headers: {}
|
||||
}
|
||||
]
|
||||
|
||||
// 获取模板变量说明
|
||||
export const TEMPLATE_VARIABLES = [
|
||||
{ name: '{title}', description: '通知标题' },
|
||||
{ name: '{content}', description: '通知内容' },
|
||||
{ name: '{datetime}', description: '完整日期时间 (YYYY-MM-DD HH:MM:SS)' },
|
||||
{ name: '{date}', description: '日期 (YYYY-MM-DD)' },
|
||||
{ name: '{time}', description: '时间 (HH:MM:SS)' }
|
||||
]
|
||||
@@ -85,85 +85,85 @@
|
||||
<div class="section-header">
|
||||
<h3>基本信息</h3>
|
||||
</div>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item name="userName" required>
|
||||
<template #label>
|
||||
<a-tooltip title="用于识别用户的显示名称">
|
||||
<span class="form-label">
|
||||
用户名
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="formData.userName"
|
||||
placeholder="请输入用户名"
|
||||
:disabled="loading"
|
||||
size="large"
|
||||
class="modern-input"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item name="status">
|
||||
<template #label>
|
||||
<a-tooltip title="是否启用该用户">
|
||||
<span class="form-label">
|
||||
启用状态
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-select v-model:value="formData.Info.Status" size="large">
|
||||
<a-select-option :value="true">是</a-select-option>
|
||||
<a-select-option :value="false">否</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item name="remainedDay">
|
||||
<template #label>
|
||||
<a-tooltip title="账号剩余的有效天数,「-1」表示无限">
|
||||
<span class="form-label">
|
||||
剩余天数
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model:value="formData.Info.RemainedDay"
|
||||
:min="-1"
|
||||
:max="9999"
|
||||
placeholder="-1"
|
||||
:disabled="loading"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<!-- 占位列 -->
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item name="userName" required>
|
||||
<template #label>
|
||||
<a-tooltip title="用于识别用户的显示名称">
|
||||
<span class="form-label">
|
||||
用户名
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="formData.userName"
|
||||
placeholder="请输入用户名"
|
||||
:disabled="loading"
|
||||
size="large"
|
||||
class="modern-input"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item name="status">
|
||||
<template #label>
|
||||
<a-tooltip title="是否启用该用户">
|
||||
<span class="form-label">
|
||||
启用状态
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-select v-model:value="formData.Info.Status" size="large">
|
||||
<a-select-option :value="true">是</a-select-option>
|
||||
<a-select-option :value="false">否</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-form-item name="remainedDay">
|
||||
<template #label>
|
||||
<a-tooltip title="账号剩余的有效天数,「-1」表示无限">
|
||||
<span class="form-label">
|
||||
剩余天数
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model:value="formData.Info.RemainedDay"
|
||||
:min="-1"
|
||||
:max="9999"
|
||||
placeholder="-1"
|
||||
:disabled="loading"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<!-- 占位列 -->
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-form-item name="notes">
|
||||
<template #label>
|
||||
<a-tooltip title="为用户添加备注信息">
|
||||
<span class="form-label">
|
||||
备注
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-textarea
|
||||
v-model:value="formData.Info.Notes"
|
||||
placeholder="请输入备注信息"
|
||||
:rows="4"
|
||||
:disabled="loading"
|
||||
class="modern-input"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="notes">
|
||||
<template #label>
|
||||
<a-tooltip title="为用户添加备注信息">
|
||||
<span class="form-label">
|
||||
备注
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-textarea
|
||||
v-model:value="formData.Info.Notes"
|
||||
placeholder="请输入备注信息"
|
||||
:rows="4"
|
||||
:disabled="loading"
|
||||
class="modern-input"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 额外脚本 -->
|
||||
@@ -171,90 +171,90 @@
|
||||
<div class="section-header">
|
||||
<h3>额外脚本</h3>
|
||||
</div>
|
||||
<a-form-item name="scriptBeforeTask">
|
||||
<template #label>
|
||||
<a-tooltip title="在任务执行前运行自定义脚本">
|
||||
<span class="form-label">
|
||||
任务前执行脚本
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-row :gutter="24" align="middle">
|
||||
<a-col :span="4">
|
||||
<a-switch
|
||||
v-model:checked="formData.Info.IfScriptBeforeTask"
|
||||
:disabled="loading"
|
||||
size="default"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="20">
|
||||
<a-input-group compact class="path-input-group">
|
||||
<a-input
|
||||
v-model:value="formData.Info.ScriptBeforeTask"
|
||||
placeholder="请选择脚本文件"
|
||||
:disabled="loading || !formData.Info.IfScriptBeforeTask"
|
||||
size="large"
|
||||
class="path-input"
|
||||
readonly
|
||||
<a-form-item name="scriptBeforeTask">
|
||||
<template #label>
|
||||
<a-tooltip title="在任务执行前运行自定义脚本">
|
||||
<span class="form-label">
|
||||
任务前执行脚本
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-row :gutter="24" align="middle">
|
||||
<a-col :span="4">
|
||||
<a-switch
|
||||
v-model:checked="formData.Info.IfScriptBeforeTask"
|
||||
:disabled="loading"
|
||||
size="default"
|
||||
/>
|
||||
<a-button
|
||||
size="large"
|
||||
@click="selectScriptBeforeTask"
|
||||
:disabled="loading || !formData.Info.IfScriptBeforeTask"
|
||||
class="path-button"
|
||||
>
|
||||
<template #icon>
|
||||
<FileOutlined />
|
||||
</template>
|
||||
选择文件
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
<a-form-item name="scriptAfterTask">
|
||||
<template #label>
|
||||
<a-tooltip title="在任务执行后运行自定义脚本">
|
||||
<span class="form-label">
|
||||
任务后执行脚本
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-row :gutter="24" align="middle">
|
||||
<a-col :span="4">
|
||||
<a-switch
|
||||
v-model:checked="formData.Info.IfScriptAfterTask"
|
||||
:disabled="loading"
|
||||
size="default"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="20">
|
||||
<a-input-group compact class="path-input-group">
|
||||
<a-input
|
||||
v-model:value="formData.Info.ScriptAfterTask"
|
||||
placeholder="请选择脚本文件"
|
||||
:disabled="loading || !formData.Info.IfScriptAfterTask"
|
||||
size="large"
|
||||
class="path-input"
|
||||
readonly
|
||||
</a-col>
|
||||
<a-col :span="20">
|
||||
<a-input-group compact class="path-input-group">
|
||||
<a-input
|
||||
v-model:value="formData.Info.ScriptBeforeTask"
|
||||
placeholder="请选择脚本文件"
|
||||
:disabled="loading || !formData.Info.IfScriptBeforeTask"
|
||||
size="large"
|
||||
class="path-input"
|
||||
readonly
|
||||
/>
|
||||
<a-button
|
||||
size="large"
|
||||
@click="selectScriptBeforeTask"
|
||||
:disabled="loading || !formData.Info.IfScriptBeforeTask"
|
||||
class="path-button"
|
||||
>
|
||||
<template #icon>
|
||||
<FileOutlined />
|
||||
</template>
|
||||
选择文件
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
<a-form-item name="scriptAfterTask">
|
||||
<template #label>
|
||||
<a-tooltip title="在任务执行后运行自定义脚本">
|
||||
<span class="form-label">
|
||||
任务后执行脚本
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-row :gutter="24" align="middle">
|
||||
<a-col :span="4">
|
||||
<a-switch
|
||||
v-model:checked="formData.Info.IfScriptAfterTask"
|
||||
:disabled="loading"
|
||||
size="default"
|
||||
/>
|
||||
<a-button
|
||||
size="large"
|
||||
@click="selectScriptAfterTask"
|
||||
:disabled="loading || !formData.Info.IfScriptAfterTask"
|
||||
class="path-button"
|
||||
>
|
||||
<template #icon>
|
||||
<FileOutlined />
|
||||
</template>
|
||||
选择文件
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="20">
|
||||
<a-input-group compact class="path-input-group">
|
||||
<a-input
|
||||
v-model:value="formData.Info.ScriptAfterTask"
|
||||
placeholder="请选择脚本文件"
|
||||
:disabled="loading || !formData.Info.IfScriptAfterTask"
|
||||
size="large"
|
||||
class="path-input"
|
||||
readonly
|
||||
/>
|
||||
<a-button
|
||||
size="large"
|
||||
@click="selectScriptAfterTask"
|
||||
:disabled="loading || !formData.Info.IfScriptAfterTask"
|
||||
class="path-button"
|
||||
>
|
||||
<template #icon>
|
||||
<FileOutlined />
|
||||
</template>
|
||||
选择文件
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 通知配置 -->
|
||||
@@ -262,92 +262,77 @@
|
||||
<div class="section-header">
|
||||
<h3>通知配置</h3>
|
||||
</div>
|
||||
<a-row :gutter="24" align="middle">
|
||||
<a-col :span="6">
|
||||
<span style="font-weight: 500">启用通知</span>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-switch v-model:checked="formData.Notify.Enabled" :disabled="loading" />
|
||||
<span class="switch-description">启用后将发送任务通知</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24" align="middle">
|
||||
<a-col :span="6">
|
||||
<span style="font-weight: 500">启用通知</span>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-switch v-model:checked="formData.Notify.Enabled" :disabled="loading" />
|
||||
<span class="switch-description">启用后将发送任务通知</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 发送统计 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<span style="font-weight: 500">通知内容</span>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfSendStatistic"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>统计信息
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 发送统计 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<span style="font-weight: 500">通知内容</span>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfSendStatistic"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>统计信息
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 邮件通知 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfSendMail"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>邮件通知
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-input
|
||||
v-model:value="formData.Notify.ToAddress"
|
||||
placeholder="请输入收件人邮箱地址"
|
||||
:disabled="loading || !formData.Notify.Enabled || !formData.Notify.IfSendMail"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
<!-- 邮件通知 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfSendMail"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>邮件通知
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-input
|
||||
v-model:value="formData.Notify.ToAddress"
|
||||
placeholder="请输入收件人邮箱地址"
|
||||
:disabled="loading || !formData.Notify.Enabled || !formData.Notify.IfSendMail"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- Server酱通知 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfServerChan"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>Server酱
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-input
|
||||
v-model:value="formData.Notify.ServerChanKey"
|
||||
placeholder="请输入SENDKEY"
|
||||
:disabled="loading || !formData.Notify.Enabled || !formData.Notify.IfServerChan"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 自定义 Webhook 通知 -->
|
||||
<div style="margin-top: 16px">
|
||||
<WebhookManager
|
||||
v-model:webhooks="formData.Notify.CustomWebhooks"
|
||||
@change="handleWebhookChange"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- Server酱通知 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfServerChan"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>Server酱
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-input
|
||||
v-model:value="formData.Notify.ServerChanKey"
|
||||
placeholder="请输入SENDKEY"
|
||||
:disabled="loading || !formData.Notify.Enabled || !formData.Notify.IfServerChan"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 企业微信群机器人通知 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfCompanyWebHookBot"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>企业微信群机器人
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-input
|
||||
v-model:value="formData.Notify.CompanyWebHookBotUrl"
|
||||
placeholder="请输入机器人Webhook地址"
|
||||
:disabled="
|
||||
loading || !formData.Notify.Enabled || !formData.Notify.IfCompanyWebHookBot
|
||||
"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
class="modern-input"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-card>
|
||||
@@ -384,6 +369,7 @@ import { useScriptApi } from '@/composables/useScriptApi'
|
||||
import { useWebSocket } from '@/composables/useWebSocket'
|
||||
import { Service } from '@/api'
|
||||
import { TaskCreateIn } from '@/api/models/TaskCreateIn'
|
||||
import WebhookManager from '@/components/WebhookManager.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@@ -426,11 +412,10 @@ const getDefaultGeneralUserData = () => ({
|
||||
IfSendMail: false,
|
||||
IfSendStatistic: false,
|
||||
IfServerChan: false,
|
||||
IfCompanyWebHookBot: false,
|
||||
ServerChanKey: '',
|
||||
ServerChanChannel: '',
|
||||
ServerChanTag: '',
|
||||
CompanyWebHookBotUrl: '',
|
||||
CustomWebhooks: [],
|
||||
},
|
||||
Data: {
|
||||
LastProxyDate: '2000-01-01',
|
||||
@@ -639,16 +624,19 @@ const handleGeneralConfig = async () => {
|
||||
message.success(`已开始配置用户 ${formData.userName} 的通用设置`)
|
||||
|
||||
// 设置 30 分钟超时自动断开
|
||||
generalConfigTimeout = window.setTimeout(() => {
|
||||
if (generalWebsocketId.value) {
|
||||
const id = generalWebsocketId.value
|
||||
unsubscribe(id)
|
||||
generalWebsocketId.value = null
|
||||
showGeneralConfigMask.value = false
|
||||
message.info(`用户 ${formData.userName} 的配置会话已超时断开`)
|
||||
}
|
||||
generalConfigTimeout = null
|
||||
}, 30 * 60 * 1000)
|
||||
generalConfigTimeout = window.setTimeout(
|
||||
() => {
|
||||
if (generalWebsocketId.value) {
|
||||
const id = generalWebsocketId.value
|
||||
unsubscribe(id)
|
||||
generalWebsocketId.value = null
|
||||
showGeneralConfigMask.value = false
|
||||
message.info(`用户 ${formData.userName} 的配置会话已超时断开`)
|
||||
}
|
||||
generalConfigTimeout = null
|
||||
},
|
||||
30 * 60 * 1000
|
||||
)
|
||||
} else {
|
||||
message.error(response?.message || '启动通用配置失败')
|
||||
}
|
||||
@@ -695,7 +683,7 @@ const selectScriptBeforeTask = async () => {
|
||||
{ name: '脚本文件', extensions: ['py', 'js', 'sh'] },
|
||||
{ name: '所有文件', extensions: ['*'] },
|
||||
])
|
||||
|
||||
|
||||
if (path && path.length > 0) {
|
||||
formData.Info.ScriptBeforeTask = path[0]
|
||||
message.success('任务前脚本路径选择成功')
|
||||
@@ -713,7 +701,7 @@ const selectScriptAfterTask = async () => {
|
||||
{ name: '脚本文件', extensions: ['py', 'js', 'sh'] },
|
||||
{ name: '所有文件', extensions: ['*'] },
|
||||
])
|
||||
|
||||
|
||||
if (path && path.length > 0) {
|
||||
formData.Info.ScriptAfterTask = path[0]
|
||||
message.success('任务后脚本路径选择成功')
|
||||
@@ -724,6 +712,13 @@ const selectScriptAfterTask = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 Webhook 变化
|
||||
const handleWebhookChange = () => {
|
||||
// 这里可以添加额外的处理逻辑,比如验证或保存
|
||||
console.log('User webhooks changed:', formData.Notify.CustomWebhooks)
|
||||
// 注意:实际保存会在用户点击保存按钮时进行,这里只是更新本地数据
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
if (generalWebsocketId.value) {
|
||||
unsubscribe(generalWebsocketId.value)
|
||||
@@ -991,4 +986,4 @@ onMounted(() => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -41,7 +41,13 @@
|
||||
|
||||
<div class="user-edit-content">
|
||||
<a-card class="config-card">
|
||||
<a-form ref="formRef" :model="formData" :rules="rules" layout="vertical" class="config-form">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
layout="vertical"
|
||||
class="config-form"
|
||||
>
|
||||
<!-- 基本信息组件 -->
|
||||
<BasicInfoSection
|
||||
:form-data="formData"
|
||||
@@ -91,22 +97,13 @@
|
||||
/>
|
||||
|
||||
<!-- 任务配置组件 -->
|
||||
<TaskConfigSection
|
||||
:form-data="formData"
|
||||
:loading="loading"
|
||||
/>
|
||||
<TaskConfigSection :form-data="formData" :loading="loading" />
|
||||
|
||||
<!-- 森空岛配置组件 -->
|
||||
<SkylandConfigSection
|
||||
:form-data="formData"
|
||||
:loading="loading"
|
||||
/>
|
||||
<SkylandConfigSection :form-data="formData" :loading="loading" />
|
||||
|
||||
<!-- 通知配置组件 -->
|
||||
<NotifyConfigSection
|
||||
:form-data="formData"
|
||||
:loading="loading"
|
||||
/>
|
||||
<NotifyConfigSection :form-data="formData" :loading="loading" />
|
||||
</a-form>
|
||||
</a-card>
|
||||
</div>
|
||||
@@ -459,11 +456,10 @@ const getDefaultMAAUserData = () => ({
|
||||
IfSendSixStar: false,
|
||||
IfSendStatistic: false,
|
||||
IfServerChan: false,
|
||||
IfCompanyWebHookBot: false,
|
||||
ServerChanKey: '',
|
||||
ServerChanChannel: '',
|
||||
ServerChanTag: '',
|
||||
CompanyWebHookBotUrl: '',
|
||||
CustomWebhooks: [],
|
||||
},
|
||||
Data: {
|
||||
CustomInfrastPlanIndex: '',
|
||||
@@ -790,7 +786,10 @@ const handleMAAConfig = async () => {
|
||||
subscribe(wsId, {
|
||||
onMessage: (wsMessage: any) => {
|
||||
if (wsMessage.type === 'error') {
|
||||
console.error(`用户 ${formData.Info?.Name || formData.userName} MAA配置错误:`, wsMessage.data)
|
||||
console.error(
|
||||
`用户 ${formData.Info?.Name || formData.userName} MAA配置错误:`,
|
||||
wsMessage.data
|
||||
)
|
||||
message.error(`MAA配置连接失败: ${wsMessage.data}`)
|
||||
unsubscribe(wsId)
|
||||
maaWebsocketId.value = null
|
||||
@@ -812,16 +811,19 @@ const handleMAAConfig = async () => {
|
||||
message.success(`已开始配置用户 ${formData.Info?.Name || formData.userName} 的MAA设置`)
|
||||
|
||||
// 设置 30 分钟超时自动断开
|
||||
maaConfigTimeout = window.setTimeout(() => {
|
||||
if (maaWebsocketId.value) {
|
||||
const id = maaWebsocketId.value
|
||||
unsubscribe(id)
|
||||
maaWebsocketId.value = null
|
||||
showMAAConfigMask.value = false
|
||||
message.info(`用户 ${formData.Info?.Name || formData.userName} 的配置会话已超时断开`)
|
||||
}
|
||||
maaConfigTimeout = null
|
||||
}, 30 * 60 * 1000)
|
||||
maaConfigTimeout = window.setTimeout(
|
||||
() => {
|
||||
if (maaWebsocketId.value) {
|
||||
const id = maaWebsocketId.value
|
||||
unsubscribe(id)
|
||||
maaWebsocketId.value = null
|
||||
showMAAConfigMask.value = false
|
||||
message.info(`用户 ${formData.Info?.Name || formData.userName} 的配置会话已超时断开`)
|
||||
}
|
||||
maaConfigTimeout = null
|
||||
},
|
||||
30 * 60 * 1000
|
||||
)
|
||||
} else {
|
||||
message.error(response?.message || '启动MAA配置失败')
|
||||
}
|
||||
@@ -1017,7 +1019,6 @@ const updateStageRemain = (value: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 初始化加载
|
||||
onMounted(() => {
|
||||
if (!scriptId) {
|
||||
|
||||
@@ -71,36 +71,30 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 企业微信群机器人通知 -->
|
||||
<a-row :gutter="24" style="margin-top: 16px">
|
||||
<a-col :span="6">
|
||||
<a-checkbox
|
||||
v-model:checked="formData.Notify.IfCompanyWebHookBot"
|
||||
:disabled="loading || !formData.Notify.Enabled"
|
||||
>企业微信群机器人
|
||||
</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<a-input
|
||||
v-model:value="formData.Notify.CompanyWebHookBotUrl"
|
||||
placeholder="请输入机器人Webhook地址"
|
||||
:disabled="
|
||||
loading || !formData.Notify.Enabled || !formData.Notify.IfCompanyWebHookBot
|
||||
"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
class="modern-input"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 自定义 Webhook 通知 -->
|
||||
<div style="margin-top: 16px">
|
||||
<WebhookManager
|
||||
v-model:webhooks="formData.Notify.CustomWebhooks"
|
||||
@change="handleWebhookChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import WebhookManager from '@/components/WebhookManager.vue'
|
||||
|
||||
defineProps<{
|
||||
formData: any
|
||||
loading: boolean
|
||||
}>()
|
||||
|
||||
// 处理 Webhook 变化
|
||||
const handleWebhookChange = () => {
|
||||
// 这里可以添加额外的处理逻辑,比如验证或保存
|
||||
console.log('User webhooks changed:', formData.Notify.CustomWebhooks)
|
||||
// 注意:实际保存会在用户点击保存按钮时进行,这里只是更新本地数据
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
|
||||
import type { SettingsData } from '@/types/settings'
|
||||
import WebhookManager from '@/components/WebhookManager.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
settings: SettingsData
|
||||
@@ -11,6 +12,11 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const { settings, sendTaskResultTimeOptions, handleSettingChange, testNotify, testingNotify } = props
|
||||
|
||||
// 处理 Webhook 变化
|
||||
const handleWebhookChange = async () => {
|
||||
await handleSettingChange('Notify', 'CustomWebhooks', settings.Notify.CustomWebhooks)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -273,54 +279,20 @@ const { settings, sendTaskResultTimeOptions, handleSettingChange, testNotify, te
|
||||
|
||||
<div class="form-section">
|
||||
<div class="section-header">
|
||||
<h3>企业微信机器人通知</h3>
|
||||
<h3>自定义 Webhook 通知</h3>
|
||||
<a
|
||||
href="https://doc.auto-mas.top/docs/advanced-features.html#%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BA%E9%80%9A%E7%9F%A5%E6%8E%A8%E9%80%81%E6%B8%A0%E9%81%93"
|
||||
href="https://doc.auto-mas.top/docs/advanced-features.html#%E8%87%AA%E5%AE%9A%E4%B9%89webhook%E9%80%9A%E7%9F%A5"
|
||||
target="_blank"
|
||||
class="section-doc-link"
|
||||
title="查看企业微信机器人配置文档"
|
||||
title="查看自定义Webhook配置文档"
|
||||
>
|
||||
文档
|
||||
</a>
|
||||
</div>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<div class="form-item-vertical">
|
||||
<div class="form-label-wrapper">
|
||||
<span class="form-label">启用企业微信机器人通知</span>
|
||||
<a-tooltip title="使用企业微信机器人推送通知">
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-select
|
||||
v-model:value="settings.Notify.IfCompanyWebHookBot"
|
||||
@change="(checked: any) => handleSettingChange('Notify', 'IfCompanyWebHookBot', checked)"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option :value="true">是</a-select-option>
|
||||
<a-select-option :value="false">否</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="form-item-vertical">
|
||||
<div class="form-label-wrapper">
|
||||
<span class="form-label">Webhook URL</span>
|
||||
<a-tooltip title="企业微信机器人的Webhook地址">
|
||||
<QuestionCircleOutlined class="help-icon" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-input
|
||||
v-model:value="settings.Notify.CompanyWebHookBotUrl"
|
||||
@blur="handleSettingChange('Notify', 'CompanyWebHookBotUrl', settings.Notify.CompanyWebHookBotUrl)"
|
||||
:disabled="!settings.Notify.IfCompanyWebHookBot"
|
||||
placeholder="请输入Webhook URL"
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<WebhookManager
|
||||
v-model:webhooks="settings.Notify.CustomWebhooks"
|
||||
@change="handleWebhookChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 测试按钮已移至“通知内容”标题右侧 -->
|
||||
|
||||
@@ -72,8 +72,7 @@ const settings = reactive<SettingsData>({
|
||||
ServerChanKey: '',
|
||||
ServerChanChannel: '',
|
||||
ServerChanTag: '',
|
||||
IfCompanyWebHookBot: false,
|
||||
CompanyWebHookBotUrl: '',
|
||||
CustomWebhooks: [],
|
||||
},
|
||||
Voice: { Enabled: false, Type: 'simple' },
|
||||
Start: { IfSelfStart: false, IfMinimizeDirectly: false },
|
||||
|
||||
Reference in New Issue
Block a user