fix: template manager

This commit is contained in:
root 2025-07-10 21:21:41 +08:00
parent aae64d4d04
commit 6b18e51f26
3 changed files with 235 additions and 7 deletions

View File

@ -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)

View File

@ -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">

View File

@ -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
*/ */