完善自动化任务广场目标选择
自动化任务目标改为从广场发布资源获取,不再按当前空间筛选。 任务中心允许任务归档空间与发布目标空间不同,并在执行时透传组件、技能和模型参数。
This commit is contained in:
@@ -9,6 +9,7 @@ import com.xspaceagi.agent.core.adapter.dto.TryReqDto;
|
||||
import com.xspaceagi.agent.core.adapter.dto.WorkflowExecuteRequestDto;
|
||||
import com.xspaceagi.agent.core.adapter.dto.config.ModelConfigDto;
|
||||
import com.xspaceagi.agent.core.adapter.dto.config.workflow.WorkflowConfigDto;
|
||||
import com.xspaceagi.agent.core.adapter.repository.entity.AgentComponentConfig;
|
||||
import com.xspaceagi.agent.core.adapter.repository.entity.Published;
|
||||
import com.xspaceagi.agent.core.infra.component.agent.dto.AgentExecuteResult;
|
||||
import com.xspaceagi.system.application.dto.SendNotifyMessageDto;
|
||||
@@ -84,7 +85,7 @@ public class TaskCenterApplicationServiceImpl implements TaskExecuteService {
|
||||
}
|
||||
|
||||
private void executeWorkflowTask(MonoSink<Boolean> sink, ScheduleTaskDto scheduleTask) {
|
||||
WorkflowConfigDto workflowConfigDto = workflowApplicationService.queryPublishedWorkflowConfig(Long.parseLong(scheduleTask.getTargetId()), scheduleTask.getSpaceId(), true);
|
||||
WorkflowConfigDto workflowConfigDto = workflowApplicationService.queryPublishedWorkflowConfig(Long.parseLong(scheduleTask.getTargetId()), null, true);
|
||||
if (workflowConfigDto == null) {
|
||||
sink.error(BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.agentWorkflowOffline));
|
||||
return;
|
||||
@@ -133,13 +134,22 @@ public class TaskCenterApplicationServiceImpl implements TaskExecuteService {
|
||||
}
|
||||
});
|
||||
}
|
||||
mergeSelectedComponents(selectedComponents, params.get("selectedComponents"));
|
||||
TryReqDto tryReqDto = new TryReqDto();
|
||||
tryReqDto.setDebug(false);
|
||||
tryReqDto.setConversationId(conversation.getId());
|
||||
tryReqDto.setMessage(params.get("message").toString());
|
||||
tryReqDto.setSelectedComponents(selectedComponents);
|
||||
List<Long> skillIds = toLongList(params.get("skillIds"));
|
||||
if (!skillIds.isEmpty()) {
|
||||
tryReqDto.setSkillIds(skillIds);
|
||||
}
|
||||
Long modelId = toLong(params.get("modelId"));
|
||||
if (modelId != null) {
|
||||
tryReqDto.setModelId(modelId);
|
||||
}
|
||||
try {
|
||||
if (YesOrNoEnum.Y.getKey().equals(conversation.getAgent().getAllowOtherModel())) {
|
||||
if (tryReqDto.getModelId() == null && YesOrNoEnum.Y.getKey().equals(conversation.getAgent().getAllowOtherModel())) {
|
||||
List<ModelConfigDto> modelConfigDtos = agentApplicationService.queryUserCanSelectModelListForAgent(conversation.getUserId(), conversation.getAgentId());
|
||||
if (!CollectionUtils.isEmpty(modelConfigDtos)) {
|
||||
tryReqDto.setModelId(modelConfigDtos.get(0).getId());
|
||||
@@ -154,6 +164,94 @@ public class TaskCenterApplicationServiceImpl implements TaskExecuteService {
|
||||
executeAgentTask0(sink, requestContext, tryReqDto, scheduleTask, 0);
|
||||
}
|
||||
|
||||
private void mergeSelectedComponents(List<TryReqDto.SelectedComponentDto> selectedComponents, Object rawSelectedComponents) {
|
||||
if (!(rawSelectedComponents instanceof Collection<?> rawList)) {
|
||||
return;
|
||||
}
|
||||
Set<String> existing = new HashSet<>();
|
||||
for (TryReqDto.SelectedComponentDto item : selectedComponents) {
|
||||
if (item.getId() != null && item.getType() != null) {
|
||||
existing.add(item.getType().name() + ":" + item.getId());
|
||||
}
|
||||
}
|
||||
for (Object rawItem : rawList) {
|
||||
TryReqDto.SelectedComponentDto selectedComponentDto = parseSelectedComponent(rawItem);
|
||||
if (selectedComponentDto == null || selectedComponentDto.getId() == null || selectedComponentDto.getType() == null) {
|
||||
continue;
|
||||
}
|
||||
String key = selectedComponentDto.getType().name() + ":" + selectedComponentDto.getId();
|
||||
if (existing.add(key)) {
|
||||
selectedComponents.add(selectedComponentDto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TryReqDto.SelectedComponentDto parseSelectedComponent(Object rawItem) {
|
||||
if (rawItem instanceof TryReqDto.SelectedComponentDto selectedComponentDto) {
|
||||
return selectedComponentDto.getId() != null && selectedComponentDto.getType() != null ? selectedComponentDto : null;
|
||||
}
|
||||
if (!(rawItem instanceof Map<?, ?> map)) {
|
||||
return null;
|
||||
}
|
||||
Long id = toLong(map.get("id"));
|
||||
AgentComponentConfig.Type type = toComponentType(map.get("type"));
|
||||
if (id == null || type == null) {
|
||||
return null;
|
||||
}
|
||||
TryReqDto.SelectedComponentDto selectedComponentDto = new TryReqDto.SelectedComponentDto();
|
||||
selectedComponentDto.setId(id);
|
||||
selectedComponentDto.setType(type);
|
||||
return selectedComponentDto;
|
||||
}
|
||||
|
||||
private AgentComponentConfig.Type toComponentType(Object value) {
|
||||
if (value instanceof AgentComponentConfig.Type type) {
|
||||
return type;
|
||||
}
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String typeName = value.toString();
|
||||
for (AgentComponentConfig.Type type : AgentComponentConfig.Type.values()) {
|
||||
if (type.name().equalsIgnoreCase(typeName)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Long> toLongList(Object value) {
|
||||
if (!(value instanceof Collection<?> collection)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Long> result = new ArrayList<>();
|
||||
for (Object item : collection) {
|
||||
Long parsed = toLong(item);
|
||||
if (parsed != null) {
|
||||
result.add(parsed);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Long toLong(Object value) {
|
||||
if (value instanceof Number number) {
|
||||
return number.longValue();
|
||||
}
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String text = value.toString();
|
||||
if (text.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(text);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAgentTask0(MonoSink<Boolean> sink, RequestContext<Object> requestContext, TryReqDto tryReqDto, ScheduleTaskDto scheduleTask, int times) {
|
||||
RequestContext.set(requestContext);
|
||||
AtomicBoolean retry = new AtomicBoolean(false);
|
||||
|
||||
@@ -78,9 +78,6 @@ public class TaskCenterController {
|
||||
if (publishedDto == null) {
|
||||
return ReqResult.error("Target object does not exist or has been removed");
|
||||
}
|
||||
if (!publishedDto.getSpaceId().equals(scheduleTaskAddDto.getSpaceId())) {
|
||||
return ReqResult.error("Target object space ID does not match task space ID");
|
||||
}
|
||||
ScheduleTaskDto scheduleTaskDto = new ScheduleTaskDto();
|
||||
BeanUtils.copyProperties(scheduleTaskAddDto, scheduleTaskDto);
|
||||
scheduleTaskDto.setTaskId(UUID.randomUUID().toString().replace("-", ""));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,432 @@
|
||||
import { message } from "antd";
|
||||
import {
|
||||
AUTH_KEYS,
|
||||
DEFAULT_API_TIMEOUT,
|
||||
DEFAULT_SERVER_HOST,
|
||||
} from "@shared/constants";
|
||||
import { getDomainTokenKey } from "@shared/utils/domain";
|
||||
import { normalizeServerHost } from "./auth";
|
||||
import type { AiManualComponent } from "./aiChat";
|
||||
|
||||
const SUCCESS_CODE = "0000";
|
||||
|
||||
interface ApiResponse<T> {
|
||||
code: string;
|
||||
message?: string;
|
||||
data: T;
|
||||
success?: boolean;
|
||||
}
|
||||
|
||||
interface RequestOptions {
|
||||
method?: "GET" | "POST";
|
||||
data?: unknown;
|
||||
showError?: boolean;
|
||||
}
|
||||
|
||||
export interface AutomationPageResult<T> {
|
||||
records: T[];
|
||||
total: number;
|
||||
size?: number;
|
||||
current?: number;
|
||||
pages?: number;
|
||||
}
|
||||
|
||||
export interface AutomationSpace {
|
||||
id: number;
|
||||
name?: string;
|
||||
spaceName?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface AutomationCronItem {
|
||||
cron?: string;
|
||||
desc?: string;
|
||||
}
|
||||
|
||||
export interface AutomationCronGroup {
|
||||
typeName: string;
|
||||
items: AutomationCronItem[];
|
||||
}
|
||||
|
||||
export type AutomationTargetType = "Agent" | "Workflow";
|
||||
|
||||
export interface AutomationTarget {
|
||||
id: number;
|
||||
targetId: number;
|
||||
targetType: AutomationTargetType;
|
||||
name: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
category?: string;
|
||||
spaceId?: number;
|
||||
}
|
||||
|
||||
export interface AutomationArgDetail {
|
||||
key?: string | number;
|
||||
name?: string;
|
||||
displayName?: string;
|
||||
description?: string;
|
||||
dataType?: string;
|
||||
inputType?: string;
|
||||
require?: boolean;
|
||||
required?: boolean;
|
||||
enable?: boolean;
|
||||
bindValue?: string | number | boolean | null;
|
||||
children?: AutomationArgDetail[];
|
||||
subArgs?: AutomationArgDetail[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface AutomationAgentDetail extends AutomationTarget {
|
||||
variables?: AutomationArgDetail[];
|
||||
manualComponents?: AiManualComponent[];
|
||||
}
|
||||
|
||||
export interface AutomationWorkflowDetail extends AutomationTarget {
|
||||
inputArgs?: AutomationArgDetail[];
|
||||
outputArgs?: AutomationArgDetail[];
|
||||
}
|
||||
|
||||
export type AutomationTaskStatus =
|
||||
| "CREATE"
|
||||
| "EXECUTING"
|
||||
| "CONTINUE"
|
||||
| "OVERFLOW_MAX_EXEC_TIMES"
|
||||
| "COMPLETE"
|
||||
| "FAIL"
|
||||
| "CANCEL"
|
||||
| string;
|
||||
|
||||
export interface AutomationTask {
|
||||
id: number;
|
||||
spaceId: number;
|
||||
targetName?: string;
|
||||
targetIcon?: string;
|
||||
targetType: AutomationTargetType;
|
||||
targetId: string;
|
||||
taskName: string;
|
||||
taskId?: string;
|
||||
cron?: string | null;
|
||||
params?: Record<string, unknown>;
|
||||
status?: AutomationTaskStatus;
|
||||
latestExecTime?: string | null;
|
||||
lockTime?: string | null;
|
||||
execTimes?: number;
|
||||
maxExecTimes?: number | null;
|
||||
keepConversation?: 0 | 1;
|
||||
error?: string;
|
||||
modified?: string;
|
||||
created?: string;
|
||||
creator?: {
|
||||
userId?: number;
|
||||
userName?: string;
|
||||
avatar?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AutomationSelectedComponent {
|
||||
id: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface AutomationTaskCreateParams {
|
||||
id?: number;
|
||||
spaceId: number;
|
||||
targetType: AutomationTargetType;
|
||||
targetId: string;
|
||||
taskName: string;
|
||||
cron: string | null;
|
||||
lockTime?: number | null;
|
||||
maxExecTimes?: number | null;
|
||||
keepConversation?: 0 | 1;
|
||||
params: {
|
||||
message?: string;
|
||||
variables?: Record<string, unknown>;
|
||||
selectedComponents?: AutomationSelectedComponent[];
|
||||
skillIds?: number[];
|
||||
modelId?: number | null;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AutomationRunLog {
|
||||
id: string;
|
||||
requestId?: string;
|
||||
spaceId?: number;
|
||||
userId?: number;
|
||||
userName?: string;
|
||||
targetType?: string;
|
||||
targetName?: string;
|
||||
targetId?: string;
|
||||
input?: string;
|
||||
output?: string;
|
||||
status?: string;
|
||||
elapsedTime?: number;
|
||||
created?: string;
|
||||
requestTime?: string;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface AutomationTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
prompt: string;
|
||||
cronHint?: "daily" | "weekly" | "once";
|
||||
}
|
||||
|
||||
async function getBaseUrl(): Promise<string> {
|
||||
const config = (await window.electronAPI?.settings.get(
|
||||
AUTH_KEYS.STEP1_CONFIG,
|
||||
)) as { serverHost?: string } | null;
|
||||
|
||||
return normalizeServerHost(config?.serverHost || DEFAULT_SERVER_HOST);
|
||||
}
|
||||
|
||||
async function getAuthToken(domain: string): Promise<string | null> {
|
||||
const oneShotToken = (await window.electronAPI?.settings.get(
|
||||
AUTH_KEYS.AUTH_TOKEN,
|
||||
)) as string | null;
|
||||
if (oneShotToken) return oneShotToken;
|
||||
|
||||
const domainTokenKey = getDomainTokenKey(domain);
|
||||
return (await window.electronAPI?.settings.get(domainTokenKey)) as
|
||||
| string
|
||||
| null;
|
||||
}
|
||||
|
||||
function normalizeAssetUrl(
|
||||
baseUrl: string,
|
||||
value?: string,
|
||||
): string | undefined {
|
||||
if (!value) return value;
|
||||
if (/^(https?:)?\/\//i.test(value) || value.startsWith("data:")) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return new URL(value, baseUrl).toString();
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
async function automationRequest<T>(
|
||||
path: string,
|
||||
options: RequestOptions = {},
|
||||
): Promise<T> {
|
||||
const baseUrl = await getBaseUrl();
|
||||
const token = await getAuthToken(baseUrl);
|
||||
const response = await fetch(`${baseUrl}${path}`, {
|
||||
method: options.method || "GET",
|
||||
credentials: "include",
|
||||
cache: "no-store",
|
||||
headers: {
|
||||
Accept: "application/json, text/plain, */*",
|
||||
"Content-Type": "application/json",
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
signal: AbortSignal.timeout(DEFAULT_API_TIMEOUT),
|
||||
...(options.data !== undefined
|
||||
? { body: JSON.stringify(options.data) }
|
||||
: {}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
if (options.showError !== false) message.error(error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const result = (await response.json()) as ApiResponse<T>;
|
||||
if (result.code !== SUCCESS_CODE) {
|
||||
const errorMessage = result.message || "请求失败";
|
||||
if (options.showError !== false) message.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
|
||||
function normalizeTarget<T extends AutomationTarget>(
|
||||
baseUrl: string,
|
||||
item: T,
|
||||
targetType: AutomationTargetType,
|
||||
): T {
|
||||
return {
|
||||
...item,
|
||||
targetType,
|
||||
targetId: item.targetId || item.id,
|
||||
icon: normalizeAssetUrl(baseUrl, item.icon),
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchAutomationSpaces(): Promise<AutomationSpace[]> {
|
||||
return automationRequest<AutomationSpace[]>("/api/space/list", {
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchAutomationTasks(
|
||||
spaceId: number,
|
||||
): Promise<AutomationTask[]> {
|
||||
const baseUrl = await getBaseUrl();
|
||||
const tasks = await automationRequest<AutomationTask[]>(
|
||||
`/api/task/list/${spaceId}`,
|
||||
{ method: "POST" },
|
||||
);
|
||||
|
||||
return (tasks || []).map((task) => ({
|
||||
...task,
|
||||
targetIcon: normalizeAssetUrl(baseUrl, task.targetIcon),
|
||||
}));
|
||||
}
|
||||
|
||||
export async function createAutomationTask(
|
||||
params: AutomationTaskCreateParams,
|
||||
): Promise<AutomationTask> {
|
||||
return automationRequest<AutomationTask>("/api/task/create", {
|
||||
method: "POST",
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateAutomationTask(
|
||||
params: AutomationTaskCreateParams & { id: number },
|
||||
): Promise<void> {
|
||||
await automationRequest<null>("/api/task/update", {
|
||||
method: "POST",
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
export async function executeAutomationTask(id: number): Promise<void> {
|
||||
await automationRequest<null>(`/api/task/execute/${id}`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export async function enableAutomationTask(id: number): Promise<void> {
|
||||
await automationRequest<null>(`/api/task/enable/${id}`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export async function disableAutomationTask(id: number): Promise<void> {
|
||||
await automationRequest<null>(`/api/task/cancel/${id}`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteAutomationTask(id: number): Promise<void> {
|
||||
await automationRequest<null>(`/api/task/delete/${id}`, {
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchAutomationCronList(): Promise<
|
||||
AutomationCronGroup[]
|
||||
> {
|
||||
return automationRequest<AutomationCronGroup[]>("/api/task/cron/list", {
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchAutomationTargets(params: {
|
||||
targetType: AutomationTargetType;
|
||||
kw?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}): Promise<AutomationPageResult<AutomationTarget>> {
|
||||
const baseUrl = await getBaseUrl();
|
||||
const path =
|
||||
params.targetType === "Agent"
|
||||
? "/api/published/agent/list"
|
||||
: "/api/published/workflow/list";
|
||||
const page = await automationRequest<AutomationPageResult<AutomationTarget>>(
|
||||
path,
|
||||
{
|
||||
method: "POST",
|
||||
data: {
|
||||
page: params.page || 1,
|
||||
pageSize: params.pageSize || 50,
|
||||
kw: params.kw || undefined,
|
||||
...(params.targetType === "Agent"
|
||||
? { targetType: "Agent", targetSubType: "ChatBot" }
|
||||
: {}),
|
||||
},
|
||||
showError: false,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
...page,
|
||||
records: (page.records || []).map((item) =>
|
||||
normalizeTarget(baseUrl, item, params.targetType),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchAutomationAgentDetail(
|
||||
agentId: number,
|
||||
): Promise<AutomationAgentDetail> {
|
||||
const baseUrl = await getBaseUrl();
|
||||
const detail = await automationRequest<AutomationAgentDetail>(
|
||||
`/api/published/agent/${agentId}`,
|
||||
{
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
|
||||
return normalizeTarget(baseUrl, detail, "Agent");
|
||||
}
|
||||
|
||||
export async function fetchAutomationWorkflowDetail(
|
||||
workflowId: number,
|
||||
): Promise<AutomationWorkflowDetail> {
|
||||
const baseUrl = await getBaseUrl();
|
||||
const detail = await automationRequest<AutomationWorkflowDetail>(
|
||||
`/api/published/workflow/${workflowId}`,
|
||||
{
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
|
||||
return normalizeTarget(baseUrl, detail, "Workflow");
|
||||
}
|
||||
|
||||
export async function fetchAutomationRunLogs(params: {
|
||||
targetType: string;
|
||||
targetId: string | number;
|
||||
current?: number;
|
||||
pageSize?: number;
|
||||
}): Promise<AutomationPageResult<AutomationRunLog>> {
|
||||
return automationRequest<AutomationPageResult<AutomationRunLog>>(
|
||||
"/api/requestLogs/list",
|
||||
{
|
||||
method: "POST",
|
||||
data: {
|
||||
current: params.current || 1,
|
||||
pageSize: params.pageSize || 10,
|
||||
queryFilter: {
|
||||
targetType: params.targetType,
|
||||
targetId: String(params.targetId),
|
||||
from: "task_center",
|
||||
},
|
||||
},
|
||||
showError: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchAutomationRunLogDetail(
|
||||
id: string,
|
||||
): Promise<AutomationRunLog> {
|
||||
return automationRequest<AutomationRunLog>("/api/requestLogs/detail", {
|
||||
method: "POST",
|
||||
data: { id },
|
||||
showError: false,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
.page {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fbfaf8;
|
||||
color: #1f211f;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding: 36px 32px 24px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
color: #171817;
|
||||
font-size: 30px;
|
||||
line-height: 38px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 14px 0 0;
|
||||
color: #2f312f;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.headerActions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.headerActions :global(.ant-btn) {
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 0 32px 36px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
|
||||
.sectionTitle h2 {
|
||||
margin: 0;
|
||||
color: #686b66;
|
||||
font-size: 18px;
|
||||
line-height: 26px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.sectionTitle span {
|
||||
color: #9b9e98;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
min-height: 320px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.taskList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.taskItem {
|
||||
display: grid;
|
||||
grid-template-columns: 18px minmax(0, 1fr) auto 36px 36px;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
min-height: 58px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.statusDot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
margin-left: 4px;
|
||||
border-radius: 50%;
|
||||
background: #777a74;
|
||||
}
|
||||
|
||||
.running {
|
||||
background: #1677ff;
|
||||
}
|
||||
|
||||
.waiting {
|
||||
background: #74776f;
|
||||
}
|
||||
|
||||
.success {
|
||||
background: #00a870;
|
||||
}
|
||||
|
||||
.failed {
|
||||
background: #d9363e;
|
||||
}
|
||||
|
||||
.ended {
|
||||
background: #c7c9c4;
|
||||
}
|
||||
|
||||
.taskMain {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.taskTitleRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.taskTitleRow strong {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #20211f;
|
||||
font-size: 17px;
|
||||
line-height: 24px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.taskTitleRow span,
|
||||
.taskMetaRow span {
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #70736d;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.taskTitleRow span {
|
||||
padding: 2px 8px;
|
||||
border: 1px solid #e4e1da;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.taskMetaRow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.taskMetaRow span {
|
||||
padding: 3px 8px;
|
||||
border: 1px solid #e8e5de;
|
||||
border-radius: 8px;
|
||||
background: #f7f6f3;
|
||||
}
|
||||
|
||||
.taskNext {
|
||||
color: #656862;
|
||||
font-size: 15px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.templateGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.templateCard {
|
||||
min-height: 108px;
|
||||
display: grid;
|
||||
grid-template-columns: 56px minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
padding: 20px 24px;
|
||||
border: 1px solid #e6e3dc;
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.18s ease,
|
||||
box-shadow 0.18s ease,
|
||||
transform 0.18s ease;
|
||||
}
|
||||
|
||||
.templateCard:hover {
|
||||
border-color: #d8d3c8;
|
||||
box-shadow: 0 10px 24px rgba(31, 33, 31, 0.07);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.templateIcon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #73766f;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.templateCard strong {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #171817;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.templateCard small {
|
||||
display: -webkit-box;
|
||||
margin-top: 6px;
|
||||
overflow: hidden;
|
||||
color: #5f625d;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.taskModal :global(.ant-modal-content) {
|
||||
border-radius: 18px;
|
||||
padding: 34px 40px 30px;
|
||||
}
|
||||
|
||||
.taskModal :global(.ant-modal-header) {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.taskModal :global(.ant-modal-title) {
|
||||
color: #1c1d1b;
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.taskModal :global(.ant-modal-footer) {
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
.taskModal :global(.ant-modal-footer .ant-btn) {
|
||||
min-width: 144px;
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.taskModal :global(.ant-modal-footer .ant-btn-primary) {
|
||||
background: #111111;
|
||||
border-color: #111111;
|
||||
}
|
||||
|
||||
.taskForm {
|
||||
max-height: min(70vh, 800px);
|
||||
padding-right: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.taskForm :global(.ant-form-item-label > label) {
|
||||
color: #686b66;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.taskForm :global(.ant-input),
|
||||
.taskForm :global(.ant-input-affix-wrapper),
|
||||
.taskForm :global(.ant-select-selector),
|
||||
.taskForm :global(.ant-picker) {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.inlineFields {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 0.7fr) minmax(280px, 1.3fr);
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.promptInput {
|
||||
min-height: 210px !important;
|
||||
border-color: #9a9c98 !important;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.promptTools {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: -10px 0 22px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolSelect {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.toolSelectWide {
|
||||
min-width: 280px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.variablePanel {
|
||||
margin: 8px 0 20px;
|
||||
padding: 18px;
|
||||
border: 1px solid #e8e4dc;
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.formSubTitle {
|
||||
margin-bottom: 12px;
|
||||
color: #323431;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.frequencyBlock {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.fullWidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.logItem {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #e7e3dc;
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logItem strong {
|
||||
color: #20211f;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.logItem span,
|
||||
.logItem small {
|
||||
overflow: hidden;
|
||||
color: #777b74;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.logDetail {
|
||||
margin-top: 22px;
|
||||
padding-top: 18px;
|
||||
border-top: 1px solid #ece8e0;
|
||||
}
|
||||
|
||||
.logDetail h3 {
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
.logDetail pre {
|
||||
max-height: 360px;
|
||||
margin: 0;
|
||||
padding: 14px;
|
||||
overflow: auto;
|
||||
border-radius: 10px;
|
||||
background: #f6f5f2;
|
||||
color: #30322f;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.templateGrid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 860px) {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.headerActions {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolbar,
|
||||
.sectionTitle {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.taskItem {
|
||||
grid-template-columns: 18px minmax(0, 1fr) 36px 36px;
|
||||
}
|
||||
|
||||
.taskNext {
|
||||
grid-column: 2 / -1;
|
||||
}
|
||||
|
||||
.templateGrid,
|
||||
.inlineFields {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user