283 lines
7.9 KiB
TypeScript
283 lines
7.9 KiB
TypeScript
import React from 'react';
|
|
import { create } from 'zustand';
|
|
import { invoke } from '@tauri-apps/api/core';
|
|
import {
|
|
Template,
|
|
TemplateListResponse,
|
|
ImportTemplateRequest,
|
|
BatchImportRequest,
|
|
ImportProgress,
|
|
ImportStatus,
|
|
SegmentMatchingRule
|
|
} from '../types/template';
|
|
|
|
interface TemplateQueryParams {
|
|
search_keyword?: string;
|
|
import_status?: ImportStatus;
|
|
project_id?: string;
|
|
page?: number;
|
|
page_size?: number;
|
|
}
|
|
|
|
interface TemplateStore {
|
|
templates: Template[];
|
|
currentTemplate: Template | null;
|
|
importProgress: Record<string, ImportProgress>;
|
|
isLoading: boolean;
|
|
error: string | null;
|
|
|
|
// Actions
|
|
fetchTemplates: (params?: TemplateQueryParams) => Promise<TemplateListResponse>;
|
|
getTemplateById: (id: string) => Promise<Template | null>;
|
|
importTemplate: (request: ImportTemplateRequest) => Promise<string>;
|
|
batchImportTemplates: (request: BatchImportRequest) => Promise<void>;
|
|
deleteTemplate: (id: string) => Promise<void>;
|
|
updateTemplate: (template: Template) => Promise<void>;
|
|
getImportProgress: (templateId: string) => Promise<ImportProgress | null>;
|
|
updateSegmentMatchingRule: (segmentId: string, matchingRule: SegmentMatchingRule) => Promise<void>;
|
|
getSegmentMatchingRule: (segmentId: string) => Promise<SegmentMatchingRule>;
|
|
clearError: () => void;
|
|
setCurrentTemplate: (template: Template | null) => void;
|
|
}
|
|
|
|
export const useTemplateStore = create<TemplateStore>((set, get) => ({
|
|
templates: [],
|
|
currentTemplate: null,
|
|
importProgress: {},
|
|
isLoading: false,
|
|
error: null,
|
|
|
|
fetchTemplates: async (params = {}) => {
|
|
set({ isLoading: true, error: null });
|
|
|
|
try {
|
|
const response: TemplateListResponse = await invoke('list_templates', {
|
|
options: params
|
|
});
|
|
|
|
set({ templates: response.templates, isLoading: false });
|
|
return response;
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '获取模板列表失败';
|
|
set({ error: errorMessage, isLoading: false });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
getTemplateById: async (id: string) => {
|
|
set({ isLoading: true, error: null });
|
|
|
|
try {
|
|
const template: Template | null = await invoke('get_template_by_id', { id });
|
|
set({ currentTemplate: template, isLoading: false });
|
|
return template;
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '获取模板详情失败';
|
|
set({ error: errorMessage, isLoading: false });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
importTemplate: async (request: ImportTemplateRequest) => {
|
|
set({ isLoading: true, error: null });
|
|
|
|
try {
|
|
const templateId: string = await invoke('import_template', { request });
|
|
|
|
// 刷新模板列表
|
|
await get().fetchTemplates();
|
|
|
|
set({ isLoading: false });
|
|
return templateId;
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '导入模板失败';
|
|
set({ error: errorMessage, isLoading: false });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
batchImportTemplates: async (request: BatchImportRequest) => {
|
|
set({ isLoading: true, error: null });
|
|
|
|
try {
|
|
await invoke('batch_import_templates', { request });
|
|
|
|
// 刷新模板列表
|
|
await get().fetchTemplates();
|
|
|
|
set({ isLoading: false });
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '批量导入失败';
|
|
set({ error: errorMessage, isLoading: false });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
deleteTemplate: async (id: string) => {
|
|
set({ isLoading: true, error: null });
|
|
|
|
try {
|
|
await invoke('delete_template', { id });
|
|
|
|
// 从本地状态中移除模板
|
|
set(state => ({
|
|
templates: state.templates.filter(t => t.id !== id),
|
|
currentTemplate: state.currentTemplate?.id === id ? null : state.currentTemplate,
|
|
isLoading: false
|
|
}));
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '删除模板失败';
|
|
set({ error: errorMessage, isLoading: false });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
updateTemplate: async (template: Template) => {
|
|
set({ isLoading: true, error: null });
|
|
|
|
try {
|
|
await invoke('update_template', { template });
|
|
|
|
// 更新本地状态
|
|
set(state => ({
|
|
templates: state.templates.map(t => t.id === template.id ? template : t),
|
|
currentTemplate: state.currentTemplate?.id === template.id ? template : state.currentTemplate,
|
|
isLoading: false
|
|
}));
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '更新模板失败';
|
|
set({ error: errorMessage, isLoading: false });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
getImportProgress: async (templateId: string) => {
|
|
try {
|
|
const progress: ImportProgress | null = await invoke('get_import_progress', { templateId });
|
|
|
|
if (progress) {
|
|
set(state => ({
|
|
importProgress: {
|
|
...state.importProgress,
|
|
[templateId]: progress
|
|
}
|
|
}));
|
|
}
|
|
|
|
return progress;
|
|
} catch (error) {
|
|
console.error('获取导入进度失败:', error);
|
|
return null;
|
|
}
|
|
},
|
|
|
|
clearError: () => {
|
|
set({ error: null });
|
|
},
|
|
|
|
updateSegmentMatchingRule: async (segmentId: string, matchingRule: SegmentMatchingRule) => {
|
|
set({ error: null });
|
|
|
|
try {
|
|
await invoke('update_segment_matching_rule', {
|
|
segmentId,
|
|
matchingRule
|
|
});
|
|
|
|
// 如果当前有模板详情,刷新模板数据
|
|
const currentTemplate = get().currentTemplate;
|
|
if (currentTemplate) {
|
|
await get().getTemplateById(currentTemplate.id);
|
|
}
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '更新片段匹配规则失败';
|
|
set({ error: errorMessage });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
getSegmentMatchingRule: async (segmentId: string) => {
|
|
set({ error: null });
|
|
|
|
try {
|
|
const matchingRule: SegmentMatchingRule = await invoke('get_segment_matching_rule', {
|
|
segmentId
|
|
});
|
|
return matchingRule;
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : '获取片段匹配规则失败';
|
|
set({ error: errorMessage });
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
setCurrentTemplate: (template: Template | null) => {
|
|
set({ currentTemplate: template });
|
|
},
|
|
}));
|
|
|
|
// 导入进度监听 Hook
|
|
export const useImportProgress = (templateId?: string) => {
|
|
const { importProgress, getImportProgress } = useTemplateStore();
|
|
|
|
React.useEffect(() => {
|
|
if (!templateId) return;
|
|
|
|
let interval: NodeJS.Timeout | null = null;
|
|
let isMounted = true;
|
|
|
|
const fetchProgress = async () => {
|
|
if (!isMounted) return;
|
|
|
|
const progress = await getImportProgress(templateId);
|
|
|
|
// 如果导入完成或失败,停止轮询
|
|
if (progress && (
|
|
progress.status === ImportStatus.Completed ||
|
|
progress.status === ImportStatus.Failed
|
|
)) {
|
|
if (interval) {
|
|
clearInterval(interval);
|
|
interval = null;
|
|
}
|
|
return;
|
|
}
|
|
};
|
|
|
|
// 立即获取一次
|
|
fetchProgress();
|
|
|
|
// 开始轮询
|
|
interval = setInterval(fetchProgress, 1000);
|
|
|
|
return () => {
|
|
isMounted = false;
|
|
if (interval) {
|
|
clearInterval(interval);
|
|
}
|
|
};
|
|
}, [templateId, getImportProgress]);
|
|
|
|
return templateId ? importProgress[templateId] : null;
|
|
};
|
|
|
|
// 批量导入进度监听 Hook
|
|
export const useBatchImportProgress = () => {
|
|
const [batchProgress, setBatchProgress] = React.useState<any>(null);
|
|
|
|
React.useEffect(() => {
|
|
const interval = setInterval(async () => {
|
|
try {
|
|
const progress = await invoke('get_batch_import_progress');
|
|
setBatchProgress(progress);
|
|
} catch (error) {
|
|
// 忽略错误,可能是没有正在进行的批量导入
|
|
}
|
|
}, 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
return batchProgress;
|
|
};
|