fix: template manager
This commit is contained in:
parent
aae64d4d04
commit
6b18e51f26
|
|
@ -119,19 +119,21 @@ class TemplateManager:
|
|||
|
||||
if progress_callback:
|
||||
progress_callback(f"Found {total_templates} potential templates")
|
||||
|
||||
|
||||
for i, template_dir in enumerate(subdirs):
|
||||
template_path = os.path.join(source_folder, template_dir)
|
||||
|
||||
|
||||
if progress_callback:
|
||||
progress_callback(f"Processing template {i+1}/{total_templates}: {template_dir}")
|
||||
|
||||
|
||||
try:
|
||||
template_info = self._import_single_template(template_path, template_dir)
|
||||
if template_info:
|
||||
result['imported_count'] += 1
|
||||
result['imported_templates'].append(template_info)
|
||||
logger.info(f"Successfully imported template: {template_dir}")
|
||||
if progress_callback:
|
||||
progress_callback(f"✅ Successfully imported: {template_dir}")
|
||||
else:
|
||||
result['failed_count'] += 1
|
||||
result['failed_templates'].append({
|
||||
|
|
@ -139,7 +141,9 @@ class TemplateManager:
|
|||
'error': 'No draft_content.json found or invalid template'
|
||||
})
|
||||
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:
|
||||
result['failed_count'] += 1
|
||||
result['failed_templates'].append({
|
||||
|
|
@ -147,15 +151,17 @@ class TemplateManager:
|
|||
'error': str(e)
|
||||
})
|
||||
logger.error(f"Error importing template {template_dir}: {e}")
|
||||
if progress_callback:
|
||||
progress_callback(f"❌ Error importing: {template_dir} ({str(e)})")
|
||||
|
||||
# Save metadata
|
||||
self._save_metadata()
|
||||
|
||||
|
||||
result['msg'] = f"Import completed. Success: {result['imported_count']}, Failed: {result['failed_count']}"
|
||||
logger.info(result['msg'])
|
||||
|
||||
|
||||
if progress_callback:
|
||||
progress_callback(result['msg'])
|
||||
progress_callback(f"🎉 Import completed! Success: {result['imported_count']}, Failed: {result['failed_count']}")
|
||||
|
||||
except Exception as e:
|
||||
result['status'] = False
|
||||
|
|
@ -381,6 +387,37 @@ def main():
|
|||
return
|
||||
|
||||
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)
|
||||
|
||||
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 [templates, setTemplates] = useState<TemplateInfo[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
|
@ -39,6 +49,9 @@ const TemplateManagePage: React.FC = () => {
|
|||
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<TemplateInfo | 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
|
||||
useEffect(() => {
|
||||
|
|
@ -67,10 +80,38 @@ const TemplateManagePage: React.FC = () => {
|
|||
const folderResult = await invoke<string>('select_folder')
|
||||
if (!folderResult) return
|
||||
|
||||
// Reset states
|
||||
setImporting(true)
|
||||
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)
|
||||
|
||||
// Simulate completion progress
|
||||
onProgress({
|
||||
step: "complete",
|
||||
progress: 100,
|
||||
message: "模板导入完成",
|
||||
timestamp: Date.now()
|
||||
})
|
||||
setImportResult(result)
|
||||
|
||||
if (result.status && result.imported_count > 0) {
|
||||
|
|
@ -350,6 +391,122 @@ const TemplateManagePage: React.FC = () => {
|
|||
</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 */}
|
||||
{selectedTemplate && (
|
||||
<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
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue