新闻采集修改,完成发送邮件
This commit is contained in:
@@ -36,10 +36,10 @@ export const achievementApi = {
|
||||
/**
|
||||
* 删除成就
|
||||
* @param achievement 成就信息(包含achievementID)
|
||||
* @returns Promise<ResultDomain<void>>
|
||||
* @returns Promise<ResultDomain<Boolean>>
|
||||
*/
|
||||
async deleteAchievement(achievement: Achievement): Promise<ResultDomain<void>> {
|
||||
const response = await api.delete<void>('/achievements/achievement', achievement);
|
||||
async deleteAchievement(achievement: Achievement): Promise<ResultDomain<Boolean>> {
|
||||
const response = await api.delete<boolean>('/achievements/achievement', achievement);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -138,10 +138,10 @@ export const achievementApi = {
|
||||
* 撤销用户成就
|
||||
* @param userID 用户ID
|
||||
* @param achievementID 成就ID
|
||||
* @returns Promise<ResultDomain<void>>
|
||||
* @returns Promise<ResultDomain<Boolean>>
|
||||
*/
|
||||
async revokeAchievement(userID: string, achievementID: string): Promise<ResultDomain<void>> {
|
||||
const response = await api.delete<void>('/achievements/revoke', null, {
|
||||
async revokeAchievement(userID: string, achievementID: string): Promise<ResultDomain<Boolean>> {
|
||||
const response = await api.delete<boolean>('/achievements/revoke', null, {
|
||||
params: { userID, achievementID }
|
||||
});
|
||||
return response.data;
|
||||
|
||||
@@ -169,15 +169,15 @@ export const documentSegmentApi = {
|
||||
* @param documentId Dify文档ID
|
||||
* @param segmentId 分段ID
|
||||
* @param childChunkId 子块ID
|
||||
* @returns Promise<ResultDomain<void>>
|
||||
* @returns Promise<ResultDomain<Boolean>>
|
||||
*/
|
||||
async deleteChildChunk(
|
||||
datasetId: string,
|
||||
documentId: string,
|
||||
segmentId: string,
|
||||
childChunkId: string
|
||||
): Promise<ResultDomain<void>> {
|
||||
const response = await api.delete<void>(
|
||||
): Promise<ResultDomain<Boolean>> {
|
||||
const response = await api.delete<boolean>(
|
||||
`/ai/dify/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}`
|
||||
);
|
||||
return response.data;
|
||||
|
||||
@@ -147,15 +147,15 @@ export const fileUploadApi = {
|
||||
* @param datasetId Dify数据集ID
|
||||
* @param documentId Dify文档ID
|
||||
* @param enabled 是否启用
|
||||
* @returns Promise<ResultDomain<void>>
|
||||
* @returns Promise<ResultDomain<Boolean>>
|
||||
*/
|
||||
async updateDocumentStatus(
|
||||
datasetId: string,
|
||||
documentId: string,
|
||||
enabled: boolean
|
||||
): Promise<ResultDomain<void>> {
|
||||
): Promise<ResultDomain<Boolean>> {
|
||||
const action = enabled ? 'enable' : 'disable';
|
||||
const response = await api.post<void>(
|
||||
const response = await api.post<boolean>(
|
||||
`/ai/dify/datasets/${datasetId}/documents/status/${action}`,
|
||||
{ document_ids: [documentId] }
|
||||
);
|
||||
|
||||
@@ -5,7 +5,18 @@
|
||||
*/
|
||||
|
||||
import { api } from '@/apis/index';
|
||||
import type { CrontabTask, CrontabLog, DataCollectionItem, CrontabItem, ResultDomain, PageParam } from '@/types';
|
||||
import type {
|
||||
CrontabTask,
|
||||
CrontabLog,
|
||||
DataCollectionItem,
|
||||
CrontabItem,
|
||||
TaskMeta,
|
||||
EmailDefault,
|
||||
EmailRecipient,
|
||||
CreateTaskRequest,
|
||||
ResultDomain,
|
||||
PageParam
|
||||
} from '@/types';
|
||||
|
||||
/**
|
||||
* 定时任务API服务
|
||||
@@ -16,11 +27,11 @@ export const crontabApi = {
|
||||
// ==================== 定时任务管理 ====================
|
||||
|
||||
/**
|
||||
* 获取可创建的定时任务模板列表
|
||||
* @returns Promise<ResultDomain<CrontabItem>>
|
||||
* 获取可创建的定时任务列表(从数据库获取任务元数据)
|
||||
* @returns Promise<ResultDomain<TaskMeta>>
|
||||
*/
|
||||
async getEnabledCrontabList(): Promise<ResultDomain<CrontabItem>> {
|
||||
const response = await api.get<CrontabItem>(`${this.baseUrl}/getEnabledCrontabList`);
|
||||
async getEnabledCrontabList(): Promise<ResultDomain<TaskMeta>> {
|
||||
const response = await api.get<TaskMeta>(`${this.baseUrl}/getEnabledCrontabList`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -29,18 +40,18 @@ export const crontabApi = {
|
||||
* @param task 任务对象
|
||||
* @returns Promise<ResultDomain<CrontabTask>>
|
||||
*/
|
||||
async createTask(task: CrontabTask): Promise<ResultDomain<CrontabTask>> {
|
||||
async createTask(task: CreateTaskRequest): Promise<ResultDomain<CrontabTask>> {
|
||||
const response = await api.post<CrontabTask>(`${this.baseUrl}/crontabTask`, task);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新定时任务
|
||||
* @param task 任务对象
|
||||
* @param request 更新任务请求(包含任务信息、元数据ID等)
|
||||
* @returns Promise<ResultDomain<CrontabTask>>
|
||||
*/
|
||||
async updateTask(task: CrontabTask): Promise<ResultDomain<CrontabTask>> {
|
||||
const response = await api.put<CrontabTask>(`${this.baseUrl}/crontabTask`, task);
|
||||
async updateTask(request: CreateTaskRequest): Promise<ResultDomain<CrontabTask>> {
|
||||
const response = await api.put<CrontabTask>(`${this.baseUrl}/crontabTask`, request);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
@@ -255,5 +266,254 @@ export const crontabApi = {
|
||||
const response = await api.put<string>(`${this.baseUrl}/collection/item/${itemId}/status/${status}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
|
||||
// ==================== 任务元数据管理 ====================
|
||||
|
||||
/**
|
||||
* 创建任务元数据
|
||||
* @param taskMeta 任务元数据
|
||||
* @returns Promise<ResultDomain<TaskMeta>>
|
||||
*/
|
||||
async createTaskMeta(taskMeta: TaskMeta): Promise<ResultDomain<TaskMeta>> {
|
||||
const response = await api.post<TaskMeta>(`${this.baseUrl}/meta`, taskMeta);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新任务元数据
|
||||
* @param taskMeta 任务元数据
|
||||
* @returns Promise<ResultDomain<TaskMeta>>
|
||||
*/
|
||||
async updateTaskMeta(taskMeta: TaskMeta): Promise<ResultDomain<TaskMeta>> {
|
||||
const response = await api.put<TaskMeta>(`${this.baseUrl}/meta`, taskMeta);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除任务元数据
|
||||
* @param metaId 元数据ID
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async deleteTaskMeta(metaId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/meta/${metaId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据ID查询任务元数据
|
||||
* @param metaId 元数据ID
|
||||
* @returns Promise<ResultDomain<TaskMeta>>
|
||||
*/
|
||||
async getTaskMetaById(metaId: string): Promise<ResultDomain<TaskMeta>> {
|
||||
const response = await api.get<TaskMeta>(`${this.baseUrl}/meta/${metaId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询所有任务元数据
|
||||
* @returns Promise<ResultDomain<TaskMeta>>
|
||||
*/
|
||||
async getAllTaskMeta(): Promise<ResultDomain<TaskMeta>> {
|
||||
const response = await api.get<TaskMeta>(`${this.baseUrl}/meta/all`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据分类查询任务元数据
|
||||
* @param category 分类
|
||||
* @returns Promise<ResultDomain<TaskMeta>>
|
||||
*/
|
||||
async getTaskMetaByCategory(category: string): Promise<ResultDomain<TaskMeta>> {
|
||||
const response = await api.get<TaskMeta>(`${this.baseUrl}/meta/category/${category}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询任务元数据
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @returns Promise<ResultDomain<TaskMeta>>
|
||||
*/
|
||||
async getTaskMetaPage(filter?: Partial<TaskMeta>, pageParam?: PageParam): Promise<ResultDomain<TaskMeta>> {
|
||||
const response = await api.post<TaskMeta>(`${this.baseUrl}/meta/page`, {
|
||||
filter,
|
||||
pageParam: {
|
||||
pageNumber: pageParam?.pageNumber || 1,
|
||||
pageSize: pageParam?.pageSize || 10
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// ==================== 邮件默认接收人管理 ====================
|
||||
|
||||
/**
|
||||
* 创建默认接收人
|
||||
* @param emailDefault 默认接收人
|
||||
* @returns Promise<ResultDomain<EmailDefault>>
|
||||
*/
|
||||
async createEmailDefault(emailDefault: EmailDefault): Promise<ResultDomain<EmailDefault>> {
|
||||
const response = await api.post<EmailDefault>(`${this.baseUrl}/email/default`, emailDefault);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新默认接收人
|
||||
* @param emailDefault 默认接收人
|
||||
* @returns Promise<ResultDomain<EmailDefault>>
|
||||
*/
|
||||
async updateEmailDefault(emailDefault: EmailDefault): Promise<ResultDomain<EmailDefault>> {
|
||||
const response = await api.put<EmailDefault>(`${this.baseUrl}/email/default`, emailDefault);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除默认接收人
|
||||
* @param defaultId 默认ID
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async deleteEmailDefault(defaultId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/email/default/${defaultId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据defaultId查询
|
||||
* @param defaultId 默认ID
|
||||
* @returns Promise<ResultDomain<EmailDefault>>
|
||||
*/
|
||||
async getEmailDefaultById(defaultId: string): Promise<ResultDomain<EmailDefault>> {
|
||||
const response = await api.get<EmailDefault>(`${this.baseUrl}/email/default/${defaultId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据metaId查询默认接收人
|
||||
* @param metaId 元数据ID
|
||||
* @returns Promise<ResultDomain<EmailDefault>>
|
||||
*/
|
||||
async getEmailDefaultByMetaId(metaId: string): Promise<ResultDomain<EmailDefault>> {
|
||||
const response = await api.get<EmailDefault>(`${this.baseUrl}/email/default/meta/${metaId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// ==================== 邮件接收人管理 ====================
|
||||
|
||||
/**
|
||||
* 创建邮件接收人
|
||||
* @param recipient 邮件接收人
|
||||
* @returns Promise<ResultDomain<EmailRecipient>>
|
||||
*/
|
||||
async createEmailRecipient(recipient: EmailRecipient): Promise<ResultDomain<EmailRecipient>> {
|
||||
const response = await api.post<EmailRecipient>(`${this.baseUrl}/email/recipient`, recipient);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量创建邮件接收人
|
||||
* @param recipients 邮件接收人列表
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async batchCreateEmailRecipient(recipients: EmailRecipient[]): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>(`${this.baseUrl}/email/recipient/batch`, recipients);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新邮件接收人
|
||||
* @param recipient 邮件接收人
|
||||
* @returns Promise<ResultDomain<EmailRecipient>>
|
||||
*/
|
||||
async updateEmailRecipient(recipient: EmailRecipient): Promise<ResultDomain<EmailRecipient>> {
|
||||
const response = await api.put<EmailRecipient>(`${this.baseUrl}/email/recipient`, recipient);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除邮件接收人
|
||||
* @param recipientId 接收人ID
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async deleteEmailRecipient(recipientId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/email/recipient/${recipientId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据ID查询接收人
|
||||
* @param recipientId 接收人ID
|
||||
* @returns Promise<ResultDomain<EmailRecipient>>
|
||||
*/
|
||||
async getEmailRecipientById(recipientId: string): Promise<ResultDomain<EmailRecipient>> {
|
||||
const response = await api.get<EmailRecipient>(`${this.baseUrl}/email/recipient/${recipientId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据default_id查询接收人列表
|
||||
* @param defaultId 默认ID
|
||||
* @returns Promise<ResultDomain<EmailRecipient>>
|
||||
*/
|
||||
async getRecipientsByDefaultId(defaultId: string): Promise<ResultDomain<EmailRecipient>> {
|
||||
const response = await api.get<EmailRecipient>(`${this.baseUrl}/email/recipient/default/${defaultId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据任务ID查询接收人列表
|
||||
* @param taskId 任务ID
|
||||
* @returns Promise<ResultDomain<EmailRecipient>>
|
||||
*/
|
||||
async getRecipientsByTaskId(taskId: string): Promise<ResultDomain<EmailRecipient>> {
|
||||
const response = await api.get<EmailRecipient>(`${this.baseUrl}/email/recipient/task/${taskId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询所有启用的接收人
|
||||
* @returns Promise<ResultDomain<EmailRecipient>>
|
||||
*/
|
||||
async getAllEnabledRecipients(): Promise<ResultDomain<EmailRecipient>> {
|
||||
const response = await api.get<EmailRecipient>(`${this.baseUrl}/email/recipient/enabled`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询邮件接收人
|
||||
* @param filter 过滤条件
|
||||
* @param pageParam 分页参数
|
||||
* @returns Promise<ResultDomain<EmailRecipient>>
|
||||
*/
|
||||
async getEmailRecipientPage(filter?: Partial<EmailRecipient>, pageParam?: PageParam): Promise<ResultDomain<EmailRecipient>> {
|
||||
const response = await api.post<EmailRecipient>(`${this.baseUrl}/email/recipient/page`, {
|
||||
filter,
|
||||
pageParam: {
|
||||
pageNumber: pageParam?.pageNumber || 1,
|
||||
pageSize: pageParam?.pageSize || 10
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除default_id的所有接收人
|
||||
* @param defaultId 默认ID
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async deleteRecipientsByDefaultId(defaultId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/email/recipient/default/${defaultId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除任务的所有接收人
|
||||
* @param taskId 任务ID
|
||||
* @returns Promise<ResultDomain<boolean>>
|
||||
*/
|
||||
async deleteRecipientsByTaskId(taskId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/email/recipient/task/${taskId}`);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ export interface CrontabTask extends BaseDTO {
|
||||
taskName?: string;
|
||||
/** 任务分组 */
|
||||
taskGroup?: string;
|
||||
/** 元数据ID(关联任务元数据表) */
|
||||
metaId?: string;
|
||||
/** 是否使用默认接收人 */
|
||||
defaultRecipient?: boolean;
|
||||
/** Bean名称 */
|
||||
beanName?: string;
|
||||
/** 方法名称 */
|
||||
@@ -172,6 +176,8 @@ export interface CrontabMethod {
|
||||
excuete_method?: string;
|
||||
/** Python脚本路径 */
|
||||
path: string;
|
||||
/** 元数据ID(从数据库加载时使用) */
|
||||
metaId?: string;
|
||||
/** 参数定义列表 */
|
||||
params?: CrontabParam[];
|
||||
}
|
||||
@@ -186,3 +192,95 @@ export interface CrontabItem {
|
||||
methods: CrontabMethod[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时任务元数据
|
||||
*/
|
||||
export interface TaskMeta extends BaseDTO {
|
||||
/** 元数据ID */
|
||||
metaId?: string;
|
||||
/** 任务名称 */
|
||||
name?: string;
|
||||
/** 任务描述 */
|
||||
description?: string;
|
||||
/** 任务分类 */
|
||||
category?: string;
|
||||
/** Bean名称 */
|
||||
beanName?: string;
|
||||
/** 方法名称 */
|
||||
methodName?: string;
|
||||
/** 脚本路径 */
|
||||
scriptPath?: string;
|
||||
/** 参数模式(JSON Schema) */
|
||||
paramSchema?: string;
|
||||
/** 是否自动发布 */
|
||||
autoPublish?: boolean;
|
||||
/** 排序 */
|
||||
sortOrder?: number;
|
||||
/** 创建者 */
|
||||
creator?: string;
|
||||
/** 更新者 */
|
||||
updater?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮件默认接收人
|
||||
*/
|
||||
export interface EmailDefault extends BaseDTO {
|
||||
/** 默认ID */
|
||||
defaultId?: string;
|
||||
/** 元数据ID */
|
||||
metaId?: string;
|
||||
/** 用户ID */
|
||||
userId?: string;
|
||||
userEmail?: string;
|
||||
username?:string;
|
||||
/** 创建者 */
|
||||
creator?: string;
|
||||
/** 更新者 */
|
||||
updater?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮件接收人
|
||||
*/
|
||||
export interface EmailRecipient extends BaseDTO {
|
||||
/** 接收人ID */
|
||||
recipientId?: string;
|
||||
/** 任务ID */
|
||||
taskId?: string;
|
||||
/** 用户ID */
|
||||
userId?: string;
|
||||
/** 邮箱 */
|
||||
email?: string;
|
||||
/** 姓名 */
|
||||
name?: string;
|
||||
/** 创建者 */
|
||||
creator?: string;
|
||||
/** 更新者 */
|
||||
updater?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收人用户信息
|
||||
*/
|
||||
export interface RecipientUserInfo {
|
||||
/** 用户ID */
|
||||
userId: string;
|
||||
/** 用户邮箱 */
|
||||
userEmail: string;
|
||||
/** 用户名称 */
|
||||
username: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建任务请求
|
||||
*/
|
||||
export interface CreateTaskRequest {
|
||||
/** 任务信息 */
|
||||
task: CrontabTask;
|
||||
/** 任务元数据ID */
|
||||
metaId: string;
|
||||
/** 额外添加的接收人列表 */
|
||||
additionalRecipients?: RecipientUserInfo[];
|
||||
}
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ export interface TaskItemVO extends LearningTask {
|
||||
username?: string;
|
||||
deptID?: string;
|
||||
deptName?: string;
|
||||
parentDeptID?: string;
|
||||
parentID?: string;
|
||||
/** 是否必修 */
|
||||
required?: boolean;
|
||||
/** 排序号 */
|
||||
|
||||
@@ -73,7 +73,7 @@ export interface UserVO extends BaseDTO {
|
||||
/** 学习等级 */
|
||||
level?: number;
|
||||
deptID?: string;
|
||||
parentDeptID?: string;
|
||||
parentID?: string;
|
||||
/** 部门名称 */
|
||||
deptName?: string;
|
||||
/** 角色名称 */
|
||||
|
||||
@@ -206,16 +206,16 @@
|
||||
<div class="form-item" v-if="selectedTemplate">
|
||||
<span class="form-label required">爬取方法</span>
|
||||
<el-select
|
||||
v-model="selectedMethod"
|
||||
v-model="selectedMethodId"
|
||||
placeholder="请选择爬取方法"
|
||||
style="width: 100%"
|
||||
|
||||
>
|
||||
<el-option
|
||||
v-for="method in selectedTemplate.methods"
|
||||
:key="method.name"
|
||||
:key="method.metaId"
|
||||
:label="method.name"
|
||||
:value="method"
|
||||
:value="method.metaId"
|
||||
/>
|
||||
</el-select>
|
||||
<span class="form-tip">
|
||||
@@ -282,15 +282,42 @@
|
||||
placeholder="请输入爬虫描述"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 邮件接收人配置 -->
|
||||
<div class="form-item">
|
||||
<span class="form-label">是否允许并发</span>
|
||||
<el-radio-group v-model="formData.concurrent">
|
||||
<el-radio :label="1">允许</el-radio>
|
||||
<el-radio :label="0">禁止</el-radio>
|
||||
</el-radio-group>
|
||||
<span class="form-tip">
|
||||
建议禁止并发,避免重复抓取
|
||||
<span class="form-label">邮件通知</span>
|
||||
<el-checkbox v-model="useDefaultRecipients">
|
||||
使用默认接收人
|
||||
</el-checkbox>
|
||||
<span class="form-tip" v-if="useDefaultRecipients && defaultRecipients.length > 0">
|
||||
默认接收人:{{ defaultRecipients.map(r => r.username).join('、') }}
|
||||
</span>
|
||||
<span class="form-tip" v-else-if="useDefaultRecipients && defaultRecipients.length === 0">
|
||||
该任务模板暂无默认接收人
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<span class="form-label">额外接收人</span>
|
||||
<div class="recipient-list">
|
||||
<el-tag
|
||||
v-for="recipient in additionalRecipients"
|
||||
:key="recipient.userId"
|
||||
closable
|
||||
@close="removeRecipient(recipient)"
|
||||
style="margin-right: 8px; margin-bottom: 8px;"
|
||||
>
|
||||
{{ recipient.username }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-button
|
||||
@click="showRecipientSelector"
|
||||
size="small"
|
||||
style="margin-top: 8px;"
|
||||
>
|
||||
选择接收人
|
||||
</el-button>
|
||||
<!-- TODO: 在这里添加自定义的用户选择组件 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -305,17 +332,38 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<GenericSelector
|
||||
v-model:visible="showUserSelector"
|
||||
title="选择邮件接收人"
|
||||
left-title="可选人员"
|
||||
right-title="已选人员"
|
||||
:fetch-available-api="fetchAllUsers"
|
||||
:initialTargetItems="selectedRecipients"
|
||||
:filter-selected="filterUsers"
|
||||
:item-config="{ id: 'userId', label: 'username', sublabel: 'userEmail' }"
|
||||
:use-tree="true"
|
||||
:tree-transform="transformUserToTree"
|
||||
:tree-props="{ children: 'children', label: 'username', id: 'userId' }"
|
||||
:only-leaf-selectable="true"
|
||||
unit-name="人"
|
||||
search-placeholder="搜索用户姓名或邮箱..."
|
||||
@confirm="handleUserConfirm"
|
||||
@cancel="resetUserSelector"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, watch } from 'vue';
|
||||
import { ref, reactive, onMounted, watch, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Plus, Search, Refresh, DocumentCopy, VideoPlay, VideoPause, Promotion, Edit, Delete } from '@element-plus/icons-vue';
|
||||
import { crontabApi } from '@/apis/crontab';
|
||||
import type { CrontabTask, CrontabItem, CrontabMethod, PageParam } from '@/types';
|
||||
import { userApi } from '@/apis/system/user';
|
||||
import type { CrontabTask, TaskMeta, CrontabItem, CrontabMethod, CrontabParam, PageParam, CreateTaskRequest, RecipientUserInfo, UserVO, ResultDomain, EmailDefault } from '@/types';
|
||||
import { AdminLayout } from '@/views/admin';
|
||||
import { GenericSelector } from '@/components';
|
||||
defineOptions({
|
||||
name: 'NewsCrawlerView'
|
||||
});
|
||||
@@ -325,12 +373,20 @@ const submitting = ref(false);
|
||||
const crawlerList = ref<CrontabTask[]>([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 爬虫模板数据
|
||||
const crawlerTemplates = ref<CrontabItem[]>([]);
|
||||
// 爬虫元数据
|
||||
const taskMetaList = ref<TaskMeta[]>([]);
|
||||
const crawlerTemplates = ref<CrontabItem[]>([]); // 转换后的模板结构
|
||||
const selectedTemplate = ref<CrontabItem | null>(null);
|
||||
const selectedMethod = ref<CrontabMethod | null>(null);
|
||||
const selectedMethodId = ref<string>(''); // 选中的方法ID(metaId)
|
||||
const selectedMetaId = ref<string>(''); // 选中的元数据ID
|
||||
const dynamicParams = ref<Record<string, any>>({});
|
||||
|
||||
// 邮件接收人相关
|
||||
const useDefaultRecipients = ref<boolean>(false);
|
||||
const defaultRecipients = ref<RecipientUserInfo[]>([]);
|
||||
const additionalRecipients = ref<RecipientUserInfo[]>([]);
|
||||
const showUserSelector = ref<boolean>(false);
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
taskName: '',
|
||||
@@ -361,40 +417,251 @@ const formData = reactive<Partial<CrontabTask>>({
|
||||
description: ''
|
||||
});
|
||||
|
||||
// 根据selectedMethodId获取完整的method对象
|
||||
const selectedMethod = computed(() => {
|
||||
if (!selectedTemplate.value || !selectedMethodId.value) {
|
||||
return null;
|
||||
}
|
||||
return selectedTemplate.value.methods.find(m => m.metaId === selectedMethodId.value) || null;
|
||||
});
|
||||
|
||||
// 计算已选接收人(包括默认接收人+额外添加的接收人)
|
||||
const selectedRecipients = computed(() => {
|
||||
if (useDefaultRecipients.value) {
|
||||
// 合并默认接收人和额外接收人,去重
|
||||
const all = [...defaultRecipients.value, ...additionalRecipients.value];
|
||||
const uniqueMap = new Map<string, RecipientUserInfo>();
|
||||
all.forEach(r => uniqueMap.set(r.userId, r));
|
||||
return Array.from(uniqueMap.values());
|
||||
} else {
|
||||
return additionalRecipients.value;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听模板选择变化
|
||||
watch(selectedTemplate, (newTemplate, oldTemplate) => {
|
||||
// 只在用户手动切换模板时重置(oldTemplate存在且不为null时才重置)
|
||||
// 编辑回填时oldTemplate为null,不会触发重置
|
||||
if (newTemplate && oldTemplate) {
|
||||
selectedMethod.value = null;
|
||||
selectedMethodId.value = '';
|
||||
dynamicParams.value = {};
|
||||
}
|
||||
});
|
||||
|
||||
// 监听方法选择变化
|
||||
watch(selectedMethod, (newMethod) => {
|
||||
if (newMethod) {
|
||||
watch(selectedMethodId, (newMethodId) => {
|
||||
if (newMethodId && selectedMethod.value) {
|
||||
// 保存metaId
|
||||
selectedMetaId.value = newMethodId;
|
||||
|
||||
dynamicParams.value = {};
|
||||
// 遍历params数组提取默认值
|
||||
if (newMethod.params && Array.isArray(newMethod.params)) {
|
||||
newMethod.params.forEach(param => {
|
||||
if (selectedMethod.value.params && Array.isArray(selectedMethod.value.params)) {
|
||||
selectedMethod.value.params.forEach((param: CrontabParam) => {
|
||||
dynamicParams.value[param.name] = param.value;
|
||||
});
|
||||
}
|
||||
|
||||
// 加载该任务模板的默认接收人
|
||||
if (selectedMetaId.value) {
|
||||
loadDefaultRecipients(selectedMetaId.value);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 加载爬虫模板
|
||||
// ==================== 人员选择器相关 ====================
|
||||
|
||||
/**
|
||||
* 1. 获取所有人员列表的接口方法
|
||||
*/
|
||||
async function fetchAllUsers(): Promise<ResultDomain<any>> {
|
||||
try {
|
||||
const result = await userApi.getUserList({});
|
||||
if (result.success && result.dataList) {
|
||||
// 转换为 GenericSelector 需要的格式
|
||||
const users = result.dataList.map((user: UserVO) => ({
|
||||
userId: user.id || '',
|
||||
username: user.username || user.email || 'Unknown',
|
||||
userEmail: user.email || '',
|
||||
deptID: user.deptID,
|
||||
deptName: user.deptName,
|
||||
parentID: user.parentID
|
||||
}));
|
||||
|
||||
return {
|
||||
...result,
|
||||
dataList: users
|
||||
};
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
ElMessage.error('获取用户列表失败');
|
||||
return {
|
||||
code: 500,
|
||||
success: false,
|
||||
login: true,
|
||||
auth: true,
|
||||
message: '获取用户列表失败',
|
||||
dataList: []
|
||||
} as ResultDomain<any>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. 过滤方法:从可选项中移除已选项
|
||||
*/
|
||||
function filterUsers(available: any[], selected: any[]): any[] {
|
||||
const selectedIds = new Set(selected.map(item => item.userId));
|
||||
return available.filter(item => !selectedIds.has(item.userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. 构建多级部门树的方法
|
||||
*/
|
||||
function transformUserToTree(flatData: any[]): any[] {
|
||||
if (!flatData || flatData.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 第一步:按部门分组,收集每个部门下的用户
|
||||
const deptMap = new Map<string, any>();
|
||||
const tree: any[] = [];
|
||||
|
||||
flatData.forEach(item => {
|
||||
if (!item.deptID) return;
|
||||
|
||||
if (!deptMap.has(item.deptID)) {
|
||||
// 创建部门节点
|
||||
deptMap.set(item.deptID, {
|
||||
userId: `dept_${item.deptID}`,
|
||||
username: item.deptName || '未分配部门',
|
||||
userEmail: '',
|
||||
deptID: item.deptID,
|
||||
deptName: item.deptName,
|
||||
parentID: item.parentID,
|
||||
children: [],
|
||||
isDept: true // 标记这是部门节点
|
||||
});
|
||||
}
|
||||
|
||||
// 添加用户到部门的children中
|
||||
const deptNode = deptMap.get(item.deptID);
|
||||
if (deptNode) {
|
||||
deptNode.children.push({
|
||||
...item,
|
||||
isDept: false // 标记这是用户节点
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 第二步:构建部门层级关系
|
||||
const allDepts = Array.from(deptMap.values());
|
||||
const deptTreeMap = new Map<string, any>();
|
||||
|
||||
// 初始化所有部门节点(创建副本)
|
||||
allDepts.forEach(dept => {
|
||||
deptTreeMap.set(dept.deptID, { ...dept });
|
||||
});
|
||||
|
||||
// 第三步:建立部门的父子关系
|
||||
allDepts.forEach(dept => {
|
||||
const node = deptTreeMap.get(dept.deptID);
|
||||
if (!node) return;
|
||||
|
||||
if (!dept.parentID || dept.parentID === '0' || dept.parentID === '') {
|
||||
// 根部门
|
||||
tree.push(node);
|
||||
} else {
|
||||
// 子部门
|
||||
const parent = deptTreeMap.get(dept.parentID);
|
||||
if (parent) {
|
||||
if (!parent.children) {
|
||||
parent.children = [];
|
||||
}
|
||||
// 保存当前节点的用户列表
|
||||
const users = node.children || [];
|
||||
node.children = [];
|
||||
// 将部门节点添加到父部门
|
||||
parent.children.push(node);
|
||||
// 恢复用户列表
|
||||
node.children = users;
|
||||
} else {
|
||||
// 找不到父节点,作为根节点
|
||||
tree.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 4. 显示用户选择器
|
||||
*/
|
||||
function showRecipientSelector() {
|
||||
showUserSelector.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 5. 确认选择用户
|
||||
*/
|
||||
|
||||
function handleUserConfirm(selected: any[]) {
|
||||
// 过滤掉部门节点,只保留用户节点
|
||||
const userItems = selected.filter(item => item.isDept !== true && !defaultRecipients.value.find(r => r.userId === item.userId));
|
||||
additionalRecipients.value = userItems.map(item => ({
|
||||
userId: item.userId,
|
||||
username: item.username,
|
||||
userEmail: item.userEmail || ''
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 6. 取消/重置选择器
|
||||
*/
|
||||
function resetUserSelector() {
|
||||
console.log('❌ 取消选择');
|
||||
// 不做任何操作,保持原有选择
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 加载爬虫模板(从数据库加载TaskMeta,转换为CrontabItem结构)
|
||||
async function loadCrawlerTemplates() {
|
||||
try {
|
||||
const result = await crontabApi.getEnabledCrontabList();
|
||||
if (result.success && result.dataList) {
|
||||
crawlerTemplates.value = result.dataList;
|
||||
taskMetaList.value = result.dataList;
|
||||
|
||||
// 将TaskMeta[]按category分组转换为CrontabItem[]
|
||||
const grouped = new Map<string, TaskMeta[]>();
|
||||
result.dataList.forEach((meta: TaskMeta) => {
|
||||
const category = meta.category || '未分类';
|
||||
if (!grouped.has(category)) {
|
||||
grouped.set(category, []);
|
||||
}
|
||||
grouped.get(category)!.push(meta);
|
||||
});
|
||||
|
||||
// 转换为CrontabItem结构
|
||||
crawlerTemplates.value = Array.from(grouped.entries()).map(([category, metas]) => ({
|
||||
name: category,
|
||||
methods: metas.map(meta => ({
|
||||
name: meta.name || '',
|
||||
clazz: meta.beanName || '',
|
||||
excuete_method: meta.methodName || '',
|
||||
path: meta.scriptPath || '',
|
||||
metaId: meta.metaId || '', // 保存metaId
|
||||
params: meta.paramSchema ? JSON.parse(meta.paramSchema) : []
|
||||
}))
|
||||
}));
|
||||
|
||||
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载爬虫模板失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载爬虫模板失败:', error);
|
||||
ElMessage.error('加载爬虫模板失败');
|
||||
}
|
||||
}
|
||||
@@ -428,7 +695,6 @@ async function loadCrawlerList() {
|
||||
total.value = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载爬虫列表失败:', error);
|
||||
ElMessage.error('加载爬虫列表失败');
|
||||
crawlerList.value = [];
|
||||
total.value = 0;
|
||||
@@ -468,50 +734,68 @@ function handleAdd() {
|
||||
isEdit.value = false;
|
||||
resetFormData();
|
||||
selectedTemplate.value = null;
|
||||
selectedMethod.value = null;
|
||||
selectedMethodId.value = '';
|
||||
dynamicParams.value = {};
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 编辑爬虫
|
||||
function handleEdit(row: CrontabTask) {
|
||||
async function handleEdit(row: CrontabTask) {
|
||||
isEdit.value = true;
|
||||
Object.assign(formData, row);
|
||||
|
||||
// 重置选择
|
||||
selectedTemplate.value = null;
|
||||
selectedMethod.value = null;
|
||||
selectedMethodId.value = '';
|
||||
dynamicParams.value = {};
|
||||
|
||||
// 尝试解析methodParams来回填表单
|
||||
if (row.methodParams) {
|
||||
|
||||
// 回填邮件接收人配置
|
||||
useDefaultRecipients.value = row.defaultRecipient || false;
|
||||
additionalRecipients.value = [];
|
||||
|
||||
// 加载该任务的额外接收人
|
||||
if (row.taskId) {
|
||||
try {
|
||||
const params = JSON.parse(row.methodParams);
|
||||
// 如果有scriptPath,尝试匹配模板和方法
|
||||
if (params.scriptPath) {
|
||||
const template = crawlerTemplates.value.find(t =>
|
||||
t.methods.some(m => m.path === params.scriptPath)
|
||||
);
|
||||
if (template) {
|
||||
const method = template.methods.find(m => m.path === params.scriptPath);
|
||||
if (method) {
|
||||
// 先设置template和method,触发watch填充默认值
|
||||
selectedTemplate.value = template;
|
||||
selectedMethod.value = method;
|
||||
|
||||
// 然后使用nextTick确保watch执行完后再覆盖为实际值
|
||||
// 回填动态参数(排除scriptPath)
|
||||
const { scriptPath, ...restParams } = params;
|
||||
const recipientsResult = await crontabApi.getRecipientsByTaskId(row.taskId);
|
||||
if (recipientsResult.success && recipientsResult.dataList) {
|
||||
additionalRecipients.value = recipientsResult.dataList.map(item => ({
|
||||
userId: item.userId || '',
|
||||
username: item.name || '',
|
||||
userEmail: item.email || ''
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载额外接收人失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 通过metaId直接匹配
|
||||
if (row.metaId) {
|
||||
// 遍历所有模板和方法,找到匹配的metaId
|
||||
for (const template of crawlerTemplates.value) {
|
||||
const method = template.methods.find(m => m.metaId === row.metaId);
|
||||
if (method) {
|
||||
// 找到匹配的方法,设置template和method
|
||||
selectedTemplate.value = template;
|
||||
selectedMethodId.value = method.metaId || '';
|
||||
selectedMetaId.value = method.metaId || '';
|
||||
|
||||
// 回填动态参数
|
||||
if (row.methodParams) {
|
||||
try {
|
||||
const params = JSON.parse(row.methodParams);
|
||||
// 排除系统参数
|
||||
const { scriptPath, taskId, logId, ...restParams } = params;
|
||||
// 延迟设置,确保watch先执行完
|
||||
setTimeout(() => {
|
||||
dynamicParams.value = restParams;
|
||||
console.log('📝 编辑回填 - template:', template.name, 'method:', method.name, 'params:', restParams);
|
||||
}, 0);
|
||||
} catch (error) {
|
||||
console.error('解析methodParams失败:', error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('解析methodParams失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,7 +813,6 @@ async function handleStart(row: CrontabTask) {
|
||||
ElMessage.error(result.message || '启动失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('启动爬虫失败:', error);
|
||||
ElMessage.error('启动爬虫失败');
|
||||
}
|
||||
}
|
||||
@@ -545,7 +828,6 @@ async function handlePause(row: CrontabTask) {
|
||||
ElMessage.error(result.message || '暂停失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('暂停爬虫失败:', error);
|
||||
ElMessage.error('暂停爬虫失败');
|
||||
}
|
||||
}
|
||||
@@ -563,15 +845,21 @@ async function handleExecute(row: CrontabTask) {
|
||||
}
|
||||
);
|
||||
|
||||
const result = await crontabApi.executeTaskOnce(row.taskId!);
|
||||
if (result.success) {
|
||||
ElMessage.success('爬虫执行成功,请稍后查看执行日志');
|
||||
} else {
|
||||
ElMessage.error(result.message || '执行失败');
|
||||
}
|
||||
// 异步执行,不等待任务完成
|
||||
crontabApi.executeTaskOnce(row.taskId!).then(result => {
|
||||
if (result.success) {
|
||||
ElMessage.success('任务已提交执行,请稍后查看执行日志');
|
||||
} else {
|
||||
ElMessage.error(result.message || '提交执行失败');
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.error('提交执行失败');
|
||||
});
|
||||
|
||||
// 立即提示用户任务已触发
|
||||
ElMessage.info('任务执行已触发,正在后台运行...');
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('执行爬虫失败:', error);
|
||||
ElMessage.error('执行爬虫失败');
|
||||
}
|
||||
}
|
||||
@@ -599,7 +887,6 @@ async function handleDelete(row: CrontabTask) {
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除爬虫失败:', error);
|
||||
ElMessage.error('删除爬虫失败');
|
||||
}
|
||||
}
|
||||
@@ -620,7 +907,6 @@ async function validateCron() {
|
||||
ElMessage.error(result.message || 'Cron表达式格式错误');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('验证Cron表达式失败:', error);
|
||||
ElMessage.error('验证失败');
|
||||
}
|
||||
}
|
||||
@@ -640,6 +926,12 @@ async function handleSubmit() {
|
||||
ElMessage.warning('请输入Cron表达式');
|
||||
return;
|
||||
}
|
||||
// 校验additionRecipients的email存在
|
||||
const recipientWithoutEmail = additionalRecipients.value.find(recipient => !recipient.userEmail);
|
||||
if (recipientWithoutEmail) {
|
||||
ElMessage.warning(`${recipientWithoutEmail.username} 邮箱不能为空`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证必填参数
|
||||
if (selectedMethod.value.params && Array.isArray(selectedMethod.value.params)) {
|
||||
@@ -659,26 +951,24 @@ async function handleSubmit() {
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
// 传递taskGroup和methodName(中文名),后端根据这两个name查找配置并填充beanName、methodName和scriptPath
|
||||
const data = {
|
||||
...formData,
|
||||
taskGroup: selectedTemplate.value.name, // 模板名称(中文)
|
||||
methodName: selectedMethod.value.name, // 方法名称(中文)
|
||||
methodParams: JSON.stringify({
|
||||
...dynamicParams.value // 只传用户输入的参数,scriptPath由后端填充
|
||||
})
|
||||
// 构建CreateTaskRequest
|
||||
const requestData: CreateTaskRequest = {
|
||||
metaId: selectedMetaId.value,
|
||||
task: {
|
||||
...formData,
|
||||
defaultRecipient: useDefaultRecipients.value,
|
||||
methodParams: JSON.stringify({
|
||||
...dynamicParams.value
|
||||
})
|
||||
} as CrontabTask,
|
||||
additionalRecipients: additionalRecipients.value
|
||||
};
|
||||
|
||||
console.log('📤 准备提交的数据:', data);
|
||||
console.log('📤 taskGroup:', selectedTemplate.value.name);
|
||||
console.log('📤 methodName:', selectedMethod.value.name);
|
||||
console.log('📤 动态参数:', dynamicParams.value);
|
||||
|
||||
let result;
|
||||
if (isEdit.value) {
|
||||
result = await crontabApi.updateTask(data as CrontabTask);
|
||||
result = await crontabApi.updateTask(requestData);
|
||||
} else {
|
||||
result = await crontabApi.createTask(data as CrontabTask);
|
||||
result = await crontabApi.createTask(requestData);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
@@ -689,7 +979,6 @@ async function handleSubmit() {
|
||||
ElMessage.error(result.message || (isEdit.value ? '更新失败' : '创建失败'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
@@ -713,8 +1002,41 @@ function resetFormData() {
|
||||
status: 0,
|
||||
concurrent: 0,
|
||||
misfirePolicy: 3,
|
||||
description: ''
|
||||
description: '',
|
||||
defaultRecipient: false
|
||||
});
|
||||
useDefaultRecipients.value = false;
|
||||
defaultRecipients.value = [];
|
||||
additionalRecipients.value = [];
|
||||
}
|
||||
|
||||
// 邮件接收人相关方法
|
||||
async function loadDefaultRecipients(metaId: string) {
|
||||
try {
|
||||
const result = await crontabApi.getEmailDefaultByMetaId(metaId);
|
||||
|
||||
if (result.success && result.dataList && result.dataList.length > 0) {
|
||||
defaultRecipients.value = result.dataList
|
||||
.map(item => ({
|
||||
userId: item.userId!,
|
||||
username: item.username!,
|
||||
userEmail: item.userEmail!
|
||||
}));
|
||||
} else {
|
||||
defaultRecipients.value = [];
|
||||
}
|
||||
} catch (error) {
|
||||
defaultRecipients.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 用户可以在这里添加自定义的邮件接收人选择逻辑
|
||||
|
||||
function removeRecipient(recipient: RecipientUserInfo) {
|
||||
const index = additionalRecipients.value.findIndex(r => r.userId === recipient.userId);
|
||||
if (index > -1) {
|
||||
additionalRecipients.value.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
|
||||
@@ -551,7 +551,7 @@ async function fetchTaskUsers() {
|
||||
username: item.username,
|
||||
deptID: item.deptID,
|
||||
deptName: item.deptName,
|
||||
parentDeptID: item.parentDeptID
|
||||
parentID: item.parentID
|
||||
}));
|
||||
return {
|
||||
success: true,
|
||||
@@ -604,7 +604,7 @@ function transformUsersToTree(flatData: any[]): any[] {
|
||||
displayName: deptName,
|
||||
deptID: deptID,
|
||||
deptName: deptName,
|
||||
parentDeptID: item.parentDeptID,
|
||||
parentID: item.parentID,
|
||||
children: [],
|
||||
isDept: true // 标记这是部门节点
|
||||
});
|
||||
@@ -635,12 +635,12 @@ function transformUsersToTree(flatData: any[]): any[] {
|
||||
const node = deptTreeMap.get(dept.deptID);
|
||||
if (!node) return;
|
||||
|
||||
if (!dept.parentDeptID || dept.parentDeptID === '0' || dept.parentDeptID === '') {
|
||||
if (!dept.parentID || dept.parentID === '0' || dept.parentID === '') {
|
||||
// 根部门
|
||||
tree.push(node);
|
||||
} else {
|
||||
// 子部门
|
||||
const parent = deptTreeMap.get(dept.parentDeptID);
|
||||
const parent = deptTreeMap.get(dept.parentID);
|
||||
if (parent) {
|
||||
// 将用户节点暂存
|
||||
const users = node.children || [];
|
||||
|
||||
@@ -588,8 +588,8 @@ function transformUsersToTree(flatData: any[]): any[] {
|
||||
flatData.forEach(item => {
|
||||
const deptID = item.deptID || 'unknown';
|
||||
const deptName = item.deptName || '未分配部门';
|
||||
// 优先使用 parentID,如果不存在则使用 parentDeptID
|
||||
const parentID = item.parentID || item.parentDeptID;
|
||||
// 优先使用 parentID,如果不存在则使用 parentID
|
||||
const parentID = item.parentID || item.parentID;
|
||||
|
||||
if (!deptMap.has(deptID)) {
|
||||
// 创建部门节点
|
||||
@@ -598,7 +598,7 @@ function transformUsersToTree(flatData: any[]): any[] {
|
||||
displayName: deptName,
|
||||
deptID: deptID,
|
||||
deptName: deptName,
|
||||
parentDeptID: parentID,
|
||||
parentID: parentID,
|
||||
children: [],
|
||||
isDept: true
|
||||
});
|
||||
@@ -629,12 +629,12 @@ function transformUsersToTree(flatData: any[]): any[] {
|
||||
const node = deptTreeMap.get(dept.deptID);
|
||||
if (!node) return;
|
||||
|
||||
if (!dept.parentDeptID || dept.parentDeptID === '0' || dept.parentDeptID === '') {
|
||||
if (!dept.parentID || dept.parentID === '0' || dept.parentID === '') {
|
||||
// 根部门
|
||||
tree.push(node);
|
||||
} else {
|
||||
// 子部门
|
||||
const parent = deptTreeMap.get(dept.parentDeptID);
|
||||
const parent = deptTreeMap.get(dept.parentID);
|
||||
if (parent) {
|
||||
// 将用户节点暂存
|
||||
const users = node.children || [];
|
||||
|
||||
@@ -607,7 +607,7 @@ async function fetchTaskUsers() {
|
||||
username: item.username,
|
||||
deptID: item.deptID,
|
||||
deptName: item.deptName,
|
||||
parentDeptID: item.parentDeptID
|
||||
parentID: item.parentID
|
||||
}));
|
||||
return {
|
||||
success: true,
|
||||
@@ -656,7 +656,7 @@ function transformUsersToTree(flatData: any[]): any[] {
|
||||
displayName: deptName,
|
||||
deptID: deptID,
|
||||
deptName: deptName,
|
||||
parentDeptID: item.parentDeptID,
|
||||
parentID: item.parentID,
|
||||
children: [],
|
||||
isDept: true // 标记这是部门节点
|
||||
});
|
||||
@@ -687,12 +687,12 @@ function transformUsersToTree(flatData: any[]): any[] {
|
||||
const node = deptTreeMap.get(dept.deptID);
|
||||
if (!node) return;
|
||||
|
||||
if (!dept.parentDeptID || dept.parentDeptID === '0' || dept.parentDeptID === '') {
|
||||
if (!dept.parentID || dept.parentID === '0' || dept.parentID === '') {
|
||||
// 根部门
|
||||
tree.push(node);
|
||||
} else {
|
||||
// 子部门
|
||||
const parent = deptTreeMap.get(dept.parentDeptID);
|
||||
const parent = deptTreeMap.get(dept.parentID);
|
||||
if (parent) {
|
||||
// 将用户节点暂存
|
||||
const users = node.children || [];
|
||||
|
||||
Reference in New Issue
Block a user