fix: template manager
This commit is contained in:
parent
aae64d4d04
commit
6b18e51f26
|
|
@ -119,19 +119,21 @@ class TemplateManager:
|
||||||
|
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(f"Found {total_templates} potential templates")
|
progress_callback(f"Found {total_templates} potential templates")
|
||||||
|
|
||||||
for i, template_dir in enumerate(subdirs):
|
for i, template_dir in enumerate(subdirs):
|
||||||
template_path = os.path.join(source_folder, template_dir)
|
template_path = os.path.join(source_folder, template_dir)
|
||||||
|
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(f"Processing template {i+1}/{total_templates}: {template_dir}")
|
progress_callback(f"Processing template {i+1}/{total_templates}: {template_dir}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
template_info = self._import_single_template(template_path, template_dir)
|
template_info = self._import_single_template(template_path, template_dir)
|
||||||
if template_info:
|
if template_info:
|
||||||
result['imported_count'] += 1
|
result['imported_count'] += 1
|
||||||
result['imported_templates'].append(template_info)
|
result['imported_templates'].append(template_info)
|
||||||
logger.info(f"Successfully imported template: {template_dir}")
|
logger.info(f"Successfully imported template: {template_dir}")
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(f"✅ Successfully imported: {template_dir}")
|
||||||
else:
|
else:
|
||||||
result['failed_count'] += 1
|
result['failed_count'] += 1
|
||||||
result['failed_templates'].append({
|
result['failed_templates'].append({
|
||||||
|
|
@ -139,7 +141,9 @@ class TemplateManager:
|
||||||
'error': 'No draft_content.json found or invalid template'
|
'error': 'No draft_content.json found or invalid template'
|
||||||
})
|
})
|
||||||
logger.warning(f"Failed to import template: {template_dir}")
|
logger.warning(f"Failed to import template: {template_dir}")
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(f"❌ Failed to import: {template_dir} (No draft_content.json found)")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result['failed_count'] += 1
|
result['failed_count'] += 1
|
||||||
result['failed_templates'].append({
|
result['failed_templates'].append({
|
||||||
|
|
@ -147,15 +151,17 @@ class TemplateManager:
|
||||||
'error': str(e)
|
'error': str(e)
|
||||||
})
|
})
|
||||||
logger.error(f"Error importing template {template_dir}: {e}")
|
logger.error(f"Error importing template {template_dir}: {e}")
|
||||||
|
if progress_callback:
|
||||||
|
progress_callback(f"❌ Error importing: {template_dir} ({str(e)})")
|
||||||
|
|
||||||
# Save metadata
|
# Save metadata
|
||||||
self._save_metadata()
|
self._save_metadata()
|
||||||
|
|
||||||
result['msg'] = f"Import completed. Success: {result['imported_count']}, Failed: {result['failed_count']}"
|
result['msg'] = f"Import completed. Success: {result['imported_count']}, Failed: {result['failed_count']}"
|
||||||
logger.info(result['msg'])
|
logger.info(result['msg'])
|
||||||
|
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(result['msg'])
|
progress_callback(f"🎉 Import completed! Success: {result['imported_count']}, Failed: {result['failed_count']}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result['status'] = False
|
result['status'] = False
|
||||||
|
|
@ -381,6 +387,37 @@ def main():
|
||||||
return
|
return
|
||||||
|
|
||||||
def progress_callback(message):
|
def progress_callback(message):
|
||||||
|
# Parse progress information from message
|
||||||
|
if "Processing template" in message:
|
||||||
|
# Extract template info from message like "Processing template 1/3: Template_Name"
|
||||||
|
parts = message.split(":")
|
||||||
|
if len(parts) >= 2:
|
||||||
|
template_name = parts[1].strip()
|
||||||
|
# Extract progress numbers
|
||||||
|
if "/" in parts[0]:
|
||||||
|
progress_part = parts[0].split("Processing template")[1].strip()
|
||||||
|
if "/" in progress_part:
|
||||||
|
current, total = progress_part.split("/")
|
||||||
|
try:
|
||||||
|
current_num = int(current.strip())
|
||||||
|
total_num = int(total.strip())
|
||||||
|
progress_percent = (current_num / total_num) * 100
|
||||||
|
|
||||||
|
progress.report(
|
||||||
|
step="import",
|
||||||
|
progress=progress_percent,
|
||||||
|
message=message,
|
||||||
|
details={
|
||||||
|
"current_template": template_name,
|
||||||
|
"total_templates": total_num,
|
||||||
|
"processed_templates": current_num
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Default progress step
|
||||||
progress.step("import", message)
|
progress.step("import", message)
|
||||||
|
|
||||||
result = manager.batch_import_templates(args.source_folder, progress_callback)
|
result = manager.batch_import_templates(args.source_folder, progress_callback)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,16 @@ interface ImportResult {
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ImportProgress {
|
||||||
|
step: string
|
||||||
|
progress: number // 0-100
|
||||||
|
message: string
|
||||||
|
currentTemplate?: string
|
||||||
|
totalTemplates?: number
|
||||||
|
processedTemplates?: number
|
||||||
|
timestamp: number
|
||||||
|
}
|
||||||
|
|
||||||
const TemplateManagePage: React.FC = () => {
|
const TemplateManagePage: React.FC = () => {
|
||||||
const [templates, setTemplates] = useState<TemplateInfo[]>([])
|
const [templates, setTemplates] = useState<TemplateInfo[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
@ -39,6 +49,9 @@ const TemplateManagePage: React.FC = () => {
|
||||||
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
|
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
|
||||||
const [selectedTemplate, setSelectedTemplate] = useState<TemplateInfo | null>(null)
|
const [selectedTemplate, setSelectedTemplate] = useState<TemplateInfo | null>(null)
|
||||||
const [importResult, setImportResult] = useState<ImportResult | null>(null)
|
const [importResult, setImportResult] = useState<ImportResult | null>(null)
|
||||||
|
const [importProgress, setImportProgress] = useState<ImportProgress | null>(null)
|
||||||
|
const [importLogs, setImportLogs] = useState<string[]>([])
|
||||||
|
const [showImportModal, setShowImportModal] = useState(false)
|
||||||
|
|
||||||
// Load templates on component mount
|
// Load templates on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -67,10 +80,38 @@ const TemplateManagePage: React.FC = () => {
|
||||||
const folderResult = await invoke<string>('select_folder')
|
const folderResult = await invoke<string>('select_folder')
|
||||||
if (!folderResult) return
|
if (!folderResult) return
|
||||||
|
|
||||||
|
// Reset states
|
||||||
setImporting(true)
|
setImporting(true)
|
||||||
setImportResult(null)
|
setImportResult(null)
|
||||||
|
setImportProgress(null)
|
||||||
|
setImportLogs([])
|
||||||
|
setShowImportModal(true)
|
||||||
|
|
||||||
|
// Progress callback
|
||||||
|
const onProgress = (progress: ImportProgress) => {
|
||||||
|
setImportProgress(progress)
|
||||||
|
setImportLogs(prev => [...prev, `[${new Date().toLocaleTimeString()}] ${progress.message}`])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate initial progress
|
||||||
|
onProgress({
|
||||||
|
step: "scanning",
|
||||||
|
progress: 10,
|
||||||
|
message: "正在扫描模板文件夹...",
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
// For now, use the regular import and simulate progress
|
||||||
|
// TODO: Switch to batchImportTemplatesWithProgress when backend is ready
|
||||||
const result = await TemplateService.batchImportTemplates(folderResult)
|
const result = await TemplateService.batchImportTemplates(folderResult)
|
||||||
|
|
||||||
|
// Simulate completion progress
|
||||||
|
onProgress({
|
||||||
|
step: "complete",
|
||||||
|
progress: 100,
|
||||||
|
message: "模板导入完成",
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
setImportResult(result)
|
setImportResult(result)
|
||||||
|
|
||||||
if (result.status && result.imported_count > 0) {
|
if (result.status && result.imported_count > 0) {
|
||||||
|
|
@ -350,6 +391,122 @@ const TemplateManagePage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Import Progress Modal */}
|
||||||
|
{showImportModal && (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
|
||||||
|
<div className="bg-white rounded-lg max-w-4xl w-full max-h-[80vh] overflow-hidden">
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h2 className="text-xl font-bold text-gray-900">模板导入进度</h2>
|
||||||
|
{!importing && (
|
||||||
|
<button
|
||||||
|
onClick={() => setShowImportModal(false)}
|
||||||
|
className="text-gray-400 hover:text-gray-600"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Progress Bar */}
|
||||||
|
{importProgress && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="text-sm font-medium text-gray-700">{importProgress.step}</span>
|
||||||
|
<span className="text-sm text-gray-500">
|
||||||
|
{importProgress.progress >= 0 ? `${Math.round(importProgress.progress)}%` : '处理中...'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||||
|
<div
|
||||||
|
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
|
||||||
|
style={{
|
||||||
|
width: importProgress.progress >= 0 ? `${importProgress.progress}%` : '50%'
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-600 mt-2">{importProgress.message}</p>
|
||||||
|
{importProgress.processedTemplates !== undefined && importProgress.totalTemplates !== undefined && (
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
已处理: {importProgress.processedTemplates} / {importProgress.totalTemplates}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Real-time Logs */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<h3 className="text-sm font-medium text-gray-700 mb-2">实时日志</h3>
|
||||||
|
<div className="bg-gray-50 rounded-lg p-4 h-64 overflow-y-auto">
|
||||||
|
<div className="space-y-1">
|
||||||
|
{importLogs.map((log, index) => (
|
||||||
|
<div key={index} className="text-xs text-gray-600 font-mono">
|
||||||
|
{log}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{importLogs.length === 0 && (
|
||||||
|
<div className="text-xs text-gray-400 italic">等待日志输出...</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Import Result */}
|
||||||
|
{importResult && (
|
||||||
|
<div className={`p-4 rounded-lg ${importResult.status ? 'bg-green-50 border border-green-200' : 'bg-red-50 border border-red-200'}`}>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 className={`font-medium ${importResult.status ? 'text-green-800' : 'text-red-800'}`}>
|
||||||
|
导入完成
|
||||||
|
</h3>
|
||||||
|
<p className={`text-sm ${importResult.status ? 'text-green-600' : 'text-red-600'}`}>
|
||||||
|
{importResult.msg}
|
||||||
|
</p>
|
||||||
|
{importResult.imported_count > 0 && (
|
||||||
|
<p className="text-sm text-green-600 mt-1">
|
||||||
|
成功导入 {importResult.imported_count} 个模板
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{importResult.failed_count > 0 && (
|
||||||
|
<details className="mt-2">
|
||||||
|
<summary className="text-sm text-red-600 cursor-pointer">
|
||||||
|
{importResult.failed_count} 个模板导入失败 (点击查看详情)
|
||||||
|
</summary>
|
||||||
|
<div className="mt-2 space-y-1">
|
||||||
|
{importResult.failed_templates.map((failed, index) => (
|
||||||
|
<div key={index} className="text-xs text-red-500">
|
||||||
|
{failed.name}: {failed.error}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<div className="flex justify-end space-x-3 pt-4 border-t border-gray-200 mt-6">
|
||||||
|
{importing ? (
|
||||||
|
<div className="flex items-center text-blue-600">
|
||||||
|
<div className="animate-spin rounded-full h-4 w-4 border-2 border-blue-600 border-t-transparent mr-2"></div>
|
||||||
|
导入中...
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => setShowImportModal(false)}
|
||||||
|
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors"
|
||||||
|
>
|
||||||
|
关闭
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Template Detail Modal */}
|
{/* Template Detail Modal */}
|
||||||
{selectedTemplate && (
|
{selectedTemplate && (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,40 @@ export class TemplateService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch import templates with progress monitoring
|
||||||
|
*/
|
||||||
|
static async batchImportTemplatesWithProgress(
|
||||||
|
sourceFolder: string,
|
||||||
|
onProgress?: (progress: any) => void
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
const request = { source_folder: sourceFolder }
|
||||||
|
|
||||||
|
// Set up progress listener if callback provided
|
||||||
|
let unlisten: (() => void) | null = null
|
||||||
|
if (onProgress) {
|
||||||
|
const { listen } = await import('@tauri-apps/api/event')
|
||||||
|
unlisten = await listen('template-import-progress', (event) => {
|
||||||
|
onProgress(event.payload)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await invoke('batch_import_templates_with_progress', { request })
|
||||||
|
return JSON.parse(result as string)
|
||||||
|
} finally {
|
||||||
|
// Clean up listener
|
||||||
|
if (unlisten) {
|
||||||
|
unlisten()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to batch import templates with progress:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all templates
|
* Get all templates
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue