fix: 修复前端构建报错

This commit is contained in:
imeepos
2025-08-07 17:13:28 +08:00
parent 2d3c44d5e9
commit 6b94aaf260
10 changed files with 400 additions and 118 deletions

160
Cargo.lock generated
View File

@@ -17,6 +17,17 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.8.12"
@@ -321,6 +332,21 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bit_field"
version = "0.10.2"
@@ -430,6 +456,26 @@ dependencies = [
"serde",
]
[[package]]
name = "bzip2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.13+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "cairo-rs"
version = "0.18.5"
@@ -502,6 +548,11 @@ name = "cc"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2"
dependencies = [
"jobserver",
"libc",
"once_cell",
]
[[package]]
name = "cesu8"
@@ -557,6 +608,16 @@ dependencies = [
"windows-link",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "color_quant"
version = "1.1.0"
@@ -582,6 +643,12 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -2084,6 +2151,15 @@ dependencies = [
"cfb",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "io-uring"
version = "0.7.9"
@@ -2181,6 +2257,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
dependencies = [
"getrandom 0.3.3",
"libc",
]
[[package]]
name = "jpeg-decoder"
version = "0.3.2"
@@ -2466,6 +2552,7 @@ version = "0.2.1"
dependencies = [
"anyhow",
"base64 0.22.1",
"bincode",
"chrono",
"dirs 5.0.1",
"futures-util",
@@ -2504,6 +2591,7 @@ dependencies = [
"urlencoding",
"uuid",
"winapi",
"zip",
]
[[package]]
@@ -3057,12 +3145,35 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "password-hash"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [
"base64ct",
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "pathdiff"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]]
name = "pbkdf2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [
"digest",
"hmac",
"password-hash",
"sha2",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -6474,6 +6585,55 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"aes",
"byteorder",
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils",
"flate2",
"hmac",
"pbkdf2",
"sha1",
"time",
"zstd",
]
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "5.0.2+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
dependencies = [
"libc",
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.15+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "zune-inflate"
version = "0.2.54"

View File

@@ -3,6 +3,7 @@ use crate::data::models::workflow_template::{
UpdateWorkflowTemplateRequest, WorkflowTemplateFilter
};
use crate::infrastructure::database::Database;
use crate::infrastructure::tolerant_json_parser::{CachedTolerantJsonParser, ParserConfig, RecoveryStrategy};
use anyhow::{Result, anyhow};
use chrono::{DateTime, Utc};
use rusqlite::Row;
@@ -14,12 +15,123 @@ use tracing::{debug, info, error};
#[derive(Clone)]
pub struct WorkflowTemplateRepository {
database: Arc<Database>,
json_parser: Arc<CachedTolerantJsonParser>,
}
impl WorkflowTemplateRepository {
/// 创建新的工作流模板仓库实例
pub fn new(database: Arc<Database>) -> Self {
Self { database }
// 创建带缓存的容错JSON解析器配置为适合工作流模板的解析
let json_parser = CachedTolerantJsonParser::new(Some(ParserConfig {
max_text_length: 10_000_000, // 10MB限制适合大型工作流
enable_comments: true,
enable_unquoted_keys: true,
enable_trailing_commas: true,
timeout_ms: 30000, // 30秒超时
recovery_strategies: vec![
RecoveryStrategy::StandardJson,
RecoveryStrategy::ManualFix,
RecoveryStrategy::RegexExtract,
RecoveryStrategy::PartialParse,
],
}), Some(1000)).expect("Failed to create JSON parser");
Self {
database,
json_parser: Arc::new(json_parser),
}
}
/// 安全地解析JSON字段的辅助函数使用容错JSON解析器
fn parse_json_field(&self, row: &Row, field_name: &str) -> rusqlite::Result<serde_json::Value> {
match row.get::<_, Option<String>>(field_name) {
Ok(Some(json_str)) => {
if json_str.trim().is_empty() {
Ok(serde_json::Value::Object(serde_json::Map::new()))
} else {
// 使用容错JSON解析器
match self.json_parser.parse(&json_str) {
Ok((parsed_value, _stats)) => {
debug!("Successfully parsed {} using tolerant parser", field_name);
Ok(parsed_value)
},
Err(e) => {
error!("Tolerant JSON parsing failed for {}: '{}', error: {}", field_name, json_str, e);
// 如果容错解析也失败,返回空对象而不是错误
Ok(serde_json::Value::Object(serde_json::Map::new()))
}
}
}
},
Ok(None) => Ok(serde_json::Value::Object(serde_json::Map::new())),
Err(e) => {
error!("Error getting {}: {}", field_name, e);
Err(e)
}
}
}
/// 安全地解析可选JSON字段的辅助函数使用容错JSON解析器
fn parse_optional_json_field(&self, row: &Row, field_name: &str) -> rusqlite::Result<Option<serde_json::Value>> {
match row.get::<_, Option<String>>(field_name) {
Ok(Some(json_str)) => {
if json_str.trim().is_empty() {
Ok(Some(serde_json::Value::Object(serde_json::Map::new())))
} else {
// 使用容错JSON解析器
match self.json_parser.parse(&json_str) {
Ok((parsed_value, _stats)) => {
debug!("Successfully parsed optional {} using tolerant parser", field_name);
Ok(Some(parsed_value))
},
Err(e) => {
error!("Tolerant JSON parsing failed for optional {}: '{}', error: {}", field_name, json_str, e);
// 如果容错解析也失败,返回空对象而不是错误
Ok(Some(serde_json::Value::Object(serde_json::Map::new())))
}
}
}
},
Ok(None) => Ok(None),
Err(e) => {
error!("Error getting {}: {}", field_name, e);
Err(e)
}
}
}
/// 解析多种时间戳格式的辅助函数
fn parse_datetime(&self, datetime_str: &str) -> Result<chrono::DateTime<Utc>, Box<dyn std::error::Error + Send + Sync>> {
use chrono::{DateTime, NaiveDateTime, Utc};
// 尝试多种时间格式
// 1. 尝试 RFC3339 格式 (ISO 8601)
if let Ok(dt) = DateTime::parse_from_rfc3339(datetime_str) {
return Ok(dt.with_timezone(&Utc));
}
// 2. 尝试常见的数据库格式: "YYYY-MM-DD HH:MM:SS"
if let Ok(naive_dt) = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S") {
return Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc));
}
// 3. 尝试带毫秒的格式: "YYYY-MM-DD HH:MM:SS.fff"
if let Ok(naive_dt) = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%d %H:%M:%S%.f") {
return Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc));
}
// 4. 尝试 ISO 格式但没有时区: "YYYY-MM-DDTHH:MM:SS"
if let Ok(naive_dt) = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%dT%H:%M:%S") {
return Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc));
}
// 5. 尝试 ISO 格式带毫秒但没有时区: "YYYY-MM-DDTHH:MM:SS.fff"
if let Ok(naive_dt) = NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%dT%H:%M:%S%.f") {
return Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc));
}
Err(format!("Unable to parse datetime: '{}'", datetime_str).into())
}
/// 创建工作流模板
@@ -139,8 +251,9 @@ impl WorkflowTemplateRepository {
let mut stmt = conn.prepare(&sql)
.map_err(|e| anyhow!("准备查询语句失败: {}", e))?;
let template_iter = stmt.query_map(rusqlite::params_from_iter(params.iter()), |row| self.row_to_template(row))
.map_err(|e| anyhow!("执行查询失败: {}", e))?;
let template_iter = stmt.query_map(rusqlite::params_from_iter(params.iter()), |row| {
self.row_to_template(row)
}).map_err(|e| anyhow!("执行查询失败: {}", e))?;
let mut templates = Vec::new();
for template in template_iter {
@@ -467,104 +580,86 @@ impl WorkflowTemplateRepository {
/// 将数据库行转换为WorkflowTemplate对象
fn row_to_template(&self, row: &Row) -> rusqlite::Result<WorkflowTemplate> {
// 解析基本字段
let id: Option<i64> = Some(row.get("id")?);
let name: String = row.get("name")?;
let base_name: String = row.get("base_name")?;
let version: String = row.get("version")?;
// 解析工作流类型
let type_str: String = row.get("type")?;
let name: String = row.get::<_, Option<String>>("name")?.unwrap_or_default();
if name.is_empty() {
error!("Template name is empty or null");
return Err(rusqlite::Error::InvalidColumnType(0, "name".to_string(), rusqlite::types::Type::Text));
}
let base_name: String = row.get::<_, Option<String>>("base_name")?.unwrap_or_default();
let version: String = row.get::<_, Option<String>>("version")?.unwrap_or("1.0.0".to_string());
let type_str: String = row.get::<_, Option<String>>("type")?.unwrap_or("image_generation".to_string());
let workflow_type: WorkflowType = WorkflowType::from(type_str);
let description: Option<String> = row.get("description")?;
// 解析JSON字段,处理空字符串的情况
let comfyui_workflow_json_str: String = row.get("comfyui_workflow_json")?;
let comfyui_workflow_json: serde_json::Value = if comfyui_workflow_json_str.trim().is_empty() {
serde_json::Value::Object(serde_json::Map::new())
} else {
serde_json::from_str(&comfyui_workflow_json_str)
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?
};
let ui_config_json_str: String = row.get("ui_config_json")?;
let ui_config_json: serde_json::Value = if ui_config_json_str.trim().is_empty() {
serde_json::Value::Object(serde_json::Map::new())
} else {
serde_json::from_str(&ui_config_json_str)
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?
};
let execution_config_json: Option<serde_json::Value> = row.get::<_, Option<String>>("execution_config_json")?
.map(|s| {
if s.trim().is_empty() {
Ok(serde_json::Value::Object(serde_json::Map::new()))
} else {
serde_json::from_str(&s)
}
})
.transpose()
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?;
let input_schema_json: Option<serde_json::Value> = row.get::<_, Option<String>>("input_schema_json")?
.map(|s| {
if s.trim().is_empty() {
Ok(serde_json::Value::Object(serde_json::Map::new()))
} else {
serde_json::from_str(&s)
}
})
.transpose()
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?;
let output_schema_json: Option<serde_json::Value> = row.get::<_, Option<String>>("output_schema_json")?
.map(|s| {
if s.trim().is_empty() {
Ok(serde_json::Value::Object(serde_json::Map::new()))
} else {
serde_json::from_str(&s)
}
})
.transpose()
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?;
// 解析JSON字段
let comfyui_workflow_json = self.parse_json_field(row, "comfyui_workflow_json")?;
let ui_config_json = self.parse_json_field(row, "ui_config_json")?;
let execution_config_json = self.parse_optional_json_field(row, "execution_config_json")?;
let input_schema_json = self.parse_optional_json_field(row, "input_schema_json")?;
let output_schema_json = self.parse_optional_json_field(row, "output_schema_json")?;
// 解析布尔字段
let is_active: bool = row.get("is_active")?;
let is_published: bool = row.get("is_published")?;
let tags: Option<Vec<String>> = row.get::<_, Option<String>>("tags")?
.map(|s| serde_json::from_str(&s))
.transpose()
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?;
// 解析tags字段
let tags: Option<Vec<String>> = match row.get::<_, Option<String>>("tags")? {
Some(tags_str) => {
if tags_str.trim().is_empty() {
None
} else {
// 使用容错JSON解析器解析tags
match self.json_parser.parse(&tags_str) {
Ok((parsed_value, _stats)) => {
match parsed_value.as_array() {
Some(arr) => {
let string_tags: Vec<String> = arr.iter()
.map(|v| v.as_str().unwrap_or("").to_string())
.filter(|s| !s.is_empty())
.collect();
Some(string_tags)
},
None => {
error!("Tags field is not an array: {}", tags_str);
None
}
}
},
Err(e) => {
error!("Failed to parse tags field: '{}', error: {}", tags_str, e);
None
}
}
}
},
None => None
};
let category: Option<String> = row.get("category")?;
let author: Option<String> = row.get("author")?;
// 解析时间戳
// 解析时间戳 - 处理多种时间格式
let created_at_str: String = row.get("created_at")?;
let created_at = DateTime::parse_from_rfc3339(&created_at_str)
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?
.with_timezone(&Utc);
let created_at = self.parse_datetime(&created_at_str)
.map_err(|e| {
error!("解析 created_at 时间戳失败: '{}', error: {}", created_at_str, e);
rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, e
)
})?;
let updated_at_str: String = row.get("updated_at")?;
let updated_at = DateTime::parse_from_rfc3339(&updated_at_str)
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, Box::new(e)
))?
.with_timezone(&Utc);
let updated_at = self.parse_datetime(&updated_at_str)
.map_err(|e| {
error!("解析 updated_at 时间戳失败: '{}', error: {}", updated_at_str, e);
rusqlite::Error::FromSqlConversionFailure(
0, rusqlite::types::Type::Text, e
)
})?;
Ok(WorkflowTemplate {
id,

View File

@@ -71,7 +71,7 @@ export const FolderUploadNode: React.FC<FolderUploadNodeProps> = ({ id, data })
<input
type="file"
multiple
webkitdirectory=""
{...({ webkitdirectory: "" } as any)}
onChange={handleFolderChange}
className="w-full text-xs file:mr-2 file:py-1 file:px-2 file:rounded file:border-0 file:text-xs file:bg-orange-50 file:text-orange-700 hover:file:bg-orange-100"
/>

View File

@@ -38,7 +38,7 @@ export const TextDisplayNode: React.FC<TextDisplayNodeProps> = ({ id, data }) =>
<div className="flex justify-between items-center text-xs text-gray-500">
<span>{data.result.length} </span>
<button
onClick={() => navigator.clipboard.writeText(data.result)}
onClick={() => navigator.clipboard.writeText(data.result || '')}
className="text-blue-500 hover:text-blue-700 transition-colors"
>

View File

@@ -487,8 +487,8 @@ export const DataExportManager: React.FC<DataExportManagerProps> = ({
value={exportConfig.date_range?.start || ''}
onChange={(e) => updateConfig({
date_range: {
...exportConfig.date_range,
start: e.target.value
start: e.target.value,
end: exportConfig.date_range?.end || ''
}
})}
className="px-3 py-2 border border-gray-300 rounded text-sm focus:ring-1 focus:ring-blue-500"
@@ -498,7 +498,7 @@ export const DataExportManager: React.FC<DataExportManagerProps> = ({
value={exportConfig.date_range?.end || ''}
onChange={(e) => updateConfig({
date_range: {
...exportConfig.date_range,
start: exportConfig.date_range?.start || '',
end: e.target.value
}
})}

View File

@@ -17,10 +17,8 @@ import {
Cloud,
Globe,
Server,
AlertCircle,
Info
} from 'lucide-react';
import { invoke } from '@tauri-apps/api/core';
// 环境类型枚举
type EnvironmentType = 'local_comfyui' | 'modal_cloud' | 'runpod_cloud' | 'custom';
@@ -227,7 +225,7 @@ export const EnvironmentConfigurator: React.FC<EnvironmentConfiguratorProps> = (
newErrors.api_key = 'API密钥不能为空';
}
if (formData.max_concurrent_jobs < 1) {
if ((formData.max_concurrent_jobs || 0) < 1) {
newErrors.max_concurrent_jobs = '最大并发数必须大于0';
}

View File

@@ -171,7 +171,7 @@ export const useConnectionValidation = () => {
const sourceNode = nodes.find(n => n.id === connection.source);
const targetNode = nodes.find(n => n.id === connection.target);
if (!sourceNode || !targetNode) return false;
if (!sourceNode || !targetNode || !sourceNode.type || !targetNode.type) return false;
// 基本的类型兼容性检查
const sourceType = getNodeOutputType(sourceNode.type);

View File

@@ -7,6 +7,7 @@
import React, { useState } from 'react';
import { Settings, History, Plus, Activity } from 'lucide-react';
import { invoke } from '@tauri-apps/api/core';
import { WorkflowList } from '../components/workflow/WorkflowList';
import { WorkflowExecutionModal } from '../components/workflow/WorkflowExecutionModal';
import { ExecutionHistoryList } from '../components/workflow/ExecutionHistoryList';
@@ -22,8 +23,7 @@ import type {
WorkflowTemplate,
WorkflowExecutionRecord,
WorkflowExecutionEnvironment,
CreateEnvironmentRequest,
CreateWorkflowTemplateRequest
CreateEnvironmentRequest
} from '../types/workflow';
// 所有类型定义现在从 ../types/workflow 导入
@@ -196,16 +196,39 @@ export const WorkflowPage: React.FC = () => {
const handleSaveWorkflow = async (workflowData: any) => {
try {
if (selectedWorkflowTemplate) {
// TODO: 实现更新逻辑
// 更新现有工作流模板
console.log('更新工作流:', workflowData);
await invoke('update_workflow_template', {
id: selectedWorkflowTemplate.id,
request: {
name: workflowData.name !== selectedWorkflowTemplate.name ? workflowData.name : undefined,
description: workflowData.description !== selectedWorkflowTemplate.description ? workflowData.description : undefined,
comfyui_workflow_json: JSON.stringify(workflowData.comfyui_workflow_json) !== JSON.stringify(selectedWorkflowTemplate.comfyui_workflow_json) ? workflowData.comfyui_workflow_json : undefined,
ui_config_json: JSON.stringify(workflowData.ui_config_json) !== JSON.stringify(selectedWorkflowTemplate.ui_config_json) ? workflowData.ui_config_json : undefined,
execution_config_json: workflowData.execution_config_json,
input_schema_json: workflowData.input_schema_json,
output_schema_json: workflowData.output_schema_json,
tags: workflowData.tags,
category: workflowData.category,
author: workflowData.author,
}
});
console.log('工作流模板更新成功');
} else {
// TODO: 实现创建逻辑
// 创建新工作流模板
console.log('创建工作流:', workflowData);
await invoke('create_workflow_template', {
request: workflowData
});
console.log('工作流模板创建成功');
}
setIsWorkflowCreatorOpen(false);
setSelectedWorkflowTemplate(null);
// 刷新工作流列表
window.location.reload();
} catch (error) {
console.error('保存工作流失败:', error);
alert(`保存工作流失败: ${error}`);
}
};
@@ -220,10 +243,10 @@ export const WorkflowPage: React.FC = () => {
setShowBatchOperations(workflows.length > 0);
};
const handleExecutionSelectionChange = (executions: WorkflowExecutionRecord[]) => {
setSelectedExecutions(executions);
setShowBatchOperations(executions.length > 0);
};
// const handleExecutionSelectionChange = (executions: WorkflowExecutionRecord[]) => {
// setSelectedExecutions(executions);
// setShowBatchOperations(executions.length > 0);
// };
const handleBatchOperationComplete = (task: any) => {
console.log('批量操作完成:', task);

View File

@@ -24,6 +24,12 @@ export interface DataFlow {
filename?: string;
size?: number;
timestamp?: number;
totalItems?: number;
convertedFrom?: string;
convertedFor?: string;
completedItems?: number;
originalCount?: number;
failedItems?: number;
};
}

View File

@@ -16,8 +16,8 @@
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],