模特管理

This commit is contained in:
root
2025-07-11 10:26:05 +08:00
parent f28f07240d
commit d6983ab824
12 changed files with 1094 additions and 1 deletions

View File

@@ -0,0 +1,277 @@
"""
模特管理服务
管理模特信息,包括模特编号、模特图片等
"""
import json
import uuid
import os
from pathlib import Path
from typing import List, Dict, Optional
from dataclasses import dataclass, asdict
from datetime import datetime
from python_core.config import settings
from python_core.utils.logger import logger
from python_core.utils.jsonrpc import create_response_handler
@dataclass
class Model:
"""模特数据结构"""
id: str
model_number: str # 模特编号
model_image: str # 模特图片路径
created_at: str
updated_at: str
is_active: bool = True
class ModelManager:
"""模特管理器"""
def __init__(self):
self.cache_dir = settings.temp_dir / "cache"
self.cache_dir.mkdir(parents=True, exist_ok=True)
# 模特数据文件
self.models_file = self.cache_dir / "models.json"
self.models = self._load_models()
# 如果没有数据,创建默认模特
if not self.models:
self._create_default_models()
def _load_models(self) -> List[Model]:
"""加载模特数据"""
if self.models_file.exists():
try:
with open(self.models_file, 'r', encoding='utf-8') as f:
data = json.load(f)
return [Model(**item) for item in data]
except Exception as e:
logger.error(f"Failed to load models: {e}")
return []
else:
return []
def _save_models(self, models: List[Model] = None):
"""保存模特数据"""
if models is None:
models = self.models
try:
data = [asdict(model) for model in models]
with open(self.models_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
logger.info(f"Models saved to {self.models_file}")
except Exception as e:
logger.error(f"Failed to save models: {e}")
raise
def _create_default_models(self):
"""创建默认模特数据"""
default_models = [
{
"model_number": "M001",
"model_image": "",
},
{
"model_number": "M002",
"model_image": "",
},
{
"model_number": "M003",
"model_image": "",
},
]
now = datetime.now().isoformat()
for model_data in default_models:
model = Model(
id=str(uuid.uuid4()),
model_number=model_data["model_number"],
model_image=model_data["model_image"],
created_at=now,
updated_at=now
)
self.models.append(model)
self._save_models()
logger.info("Created default models")
def get_all_models(self) -> List[Dict]:
"""获取所有模特"""
return [asdict(model) for model in self.models if model.is_active]
def get_model_by_id(self, model_id: str) -> Optional[Dict]:
"""根据ID获取模特"""
for model in self.models:
if model.id == model_id and model.is_active:
return asdict(model)
return None
def get_model_by_number(self, model_number: str) -> Optional[Dict]:
"""根据编号获取模特"""
for model in self.models:
if model.model_number == model_number and model.is_active:
return asdict(model)
return None
def create_model(self, model_number: str, model_image: str = "") -> Dict:
"""创建新模特"""
# 检查编号是否已存在
if self.get_model_by_number(model_number):
raise ValueError(f"Model number {model_number} already exists")
now = datetime.now().isoformat()
new_model = Model(
id=str(uuid.uuid4()),
model_number=model_number,
model_image=model_image,
created_at=now,
updated_at=now
)
self.models.append(new_model)
self._save_models()
logger.info(f"Created new model: {model_number}")
return asdict(new_model)
def update_model(self, model_id: str, model_number: str = None,
model_image: str = None) -> Optional[Dict]:
"""更新模特"""
for i, model in enumerate(self.models):
if model.id == model_id and model.is_active:
# 如果更新编号,检查是否与其他模特冲突
if model_number is not None and model_number != model.model_number:
existing = self.get_model_by_number(model_number)
if existing and existing['id'] != model_id:
raise ValueError(f"Model number {model_number} already exists")
model.model_number = model_number
if model_image is not None:
model.model_image = model_image
model.updated_at = datetime.now().isoformat()
self.models[i] = model
self._save_models()
logger.info(f"Updated model: {model_id}")
return asdict(model)
return None
def delete_model(self, model_id: str) -> bool:
"""删除模特(真删除)"""
for i, model in enumerate(self.models):
if model.id == model_id:
# 真删除:从列表中移除
deleted_model = self.models.pop(i)
self._save_models()
logger.info(f"Deleted model: {model_id} - {deleted_model.model_number}")
return True
return False
def search_models(self, keyword: str) -> List[Dict]:
"""搜索模特"""
keyword = keyword.lower()
results = []
for model in self.models:
if (model.is_active and
keyword in model.model_number.lower()):
results.append(asdict(model))
return results
# 全局实例
model_manager = ModelManager()
def main():
"""命令行接口 - 使用JSON-RPC协议"""
import sys
import json
# 创建响应处理器
rpc = create_response_handler()
if len(sys.argv) < 2:
rpc.error("INVALID_REQUEST", "No command specified")
return
command = sys.argv[1]
try:
if command == "get_all_models":
models = model_manager.get_all_models()
rpc.success(models)
elif command == "get_model_by_id":
if len(sys.argv) < 3:
rpc.error("INVALID_REQUEST", "Model ID required")
return
model_id = sys.argv[2]
model = model_manager.get_model_by_id(model_id)
if model:
rpc.success(model)
else:
rpc.error("NOT_FOUND", "Model not found")
elif command == "create_model":
if len(sys.argv) < 3:
rpc.error("INVALID_REQUEST", "Model number required")
return
model_number = sys.argv[2]
model_image = sys.argv[3] if len(sys.argv) > 3 else ""
result = model_manager.create_model(model_number, model_image)
rpc.success(result)
elif command == "update_model":
if len(sys.argv) < 4:
rpc.error("INVALID_REQUEST", "Model ID and update data required")
return
model_id = sys.argv[2]
update_data = json.loads(sys.argv[3])
result = model_manager.update_model(
model_id,
update_data.get('model_number'),
update_data.get('model_image')
)
if result:
rpc.success(result)
else:
rpc.error("NOT_FOUND", "Model not found")
elif command == "delete_model":
if len(sys.argv) < 3:
rpc.error("INVALID_REQUEST", "Model ID required")
return
model_id = sys.argv[2]
success = model_manager.delete_model(model_id)
rpc.success(success)
elif command == "search_models":
if len(sys.argv) < 3:
rpc.error("INVALID_REQUEST", "Search keyword required")
return
keyword = sys.argv[2]
results = model_manager.search_models(keyword)
rpc.success(results)
else:
rpc.error("INVALID_REQUEST", f"Unknown command: {command}")
except Exception as e:
logger.error(f"Command execution failed: {e}")
rpc.error("INTERNAL_ERROR", str(e))
if __name__ == "__main__":
main()

View File

@@ -6,6 +6,7 @@ pub mod file_system;
pub mod project;
pub mod template;
pub mod file_utils;
pub mod model;
pub mod resource_category;
// Re-export all commands for easy access

View File

@@ -0,0 +1,101 @@
use serde::{Deserialize, Serialize};
use tauri::{command, AppHandle};
use crate::python_executor::execute_python_command;
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateModelRequest {
pub model_number: String,
pub model_image: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UpdateModelRequest {
pub model_number: Option<String>,
pub model_image: Option<String>,
}
/// 获取所有模特
#[command]
pub async fn get_all_models(app: AppHandle) -> Result<String, String> {
let args = vec![
"-m".to_string(),
"python_core.services.model_manager".to_string(),
"get_all_models".to_string(),
];
execute_python_command(app, &args, None).await
}
/// 根据ID获取模特
#[command]
pub async fn get_model_by_id(app: AppHandle, model_id: String) -> Result<String, String> {
let args = vec![
"-m".to_string(),
"python_core.services.model_manager".to_string(),
"get_model_by_id".to_string(),
model_id,
];
execute_python_command(app, &args, None).await
}
/// 创建新模特
#[command]
pub async fn create_model(app: AppHandle, request: CreateModelRequest) -> Result<String, String> {
let args = vec![
"-m".to_string(),
"python_core.services.model_manager".to_string(),
"create_model".to_string(),
request.model_number,
request.model_image.unwrap_or_default(),
];
execute_python_command(app, &args, None).await
}
/// 更新模特
#[command]
pub async fn update_model(
app: AppHandle,
model_id: String,
request: UpdateModelRequest,
) -> Result<String, String> {
let request_json = serde_json::to_string(&request)
.map_err(|e| format!("Failed to serialize request: {}", e))?;
let args = vec![
"-m".to_string(),
"python_core.services.model_manager".to_string(),
"update_model".to_string(),
model_id,
request_json,
];
execute_python_command(app, &args, None).await
}
/// 删除模特
#[command]
pub async fn delete_model(app: AppHandle, model_id: String) -> Result<String, String> {
let args = vec![
"-m".to_string(),
"python_core.services.model_manager".to_string(),
"delete_model".to_string(),
model_id,
];
execute_python_command(app, &args, None).await
}
/// 搜索模特
#[command]
pub async fn search_models(app: AppHandle, keyword: String) -> Result<String, String> {
let args = vec![
"-m".to_string(),
"python_core.services.model_manager".to_string(),
"search_models".to_string(),
keyword,
];
execute_python_command(app, &args, None).await
}

View File

@@ -56,6 +56,12 @@ pub fn run() {
commands::delete_project,
commands::search_projects,
commands::open_project_directory,
commands::model::get_all_models,
commands::model::get_model_by_id,
commands::model::create_model,
commands::model::update_model,
commands::model::delete_model,
commands::model::search_models,
commands::file_utils::read_image_as_data_url
])
.run(tauri::generate_context!())

View File

@@ -9,6 +9,7 @@ import TemplateManagePage from './pages/TemplateManagePage'
import TemplateDetailPage from './pages/TemplateDetailPage'
import ResourceCategoryPage from './pages/ResourceCategoryPage'
import ProjectManagePage from './pages/ProjectManagePage'
import ModelManagePage from './pages/ModelManagePage'
import KVTestPage from './pages/KVTestPage'
function App() {
@@ -22,6 +23,7 @@ function App() {
<Route path="/templates/:templateId" element={<TemplateDetailPage />} />
<Route path="/resource-categories" element={<ResourceCategoryPage />} />
<Route path="/projects" element={<ProjectManagePage />} />
<Route path="/models" element={<ModelManagePage />} />
<Route path="/kv-test" element={<KVTestPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>

View File

@@ -0,0 +1,65 @@
import React from 'react'
import { Edit, Trash2, User } from 'lucide-react'
import { Model } from '../services/modelService'
import ProjectImage from './ProjectImage'
interface ModelCardProps {
model: Model
onEdit: (model: Model) => void
onDelete: (modelId: string) => void
}
const ModelCard: React.FC<ModelCardProps> = ({
model,
onEdit,
onDelete
}) => {
return (
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden hover:shadow-md transition-shadow">
{/* 模特图片 */}
<div className="h-48 bg-gray-100 overflow-hidden relative">
{model.model_image ? (
<ProjectImage
imagePath={model.model_image}
alt={`模特 ${model.model_number}`}
className="w-full h-full object-cover"
/>
) : (
<div className="w-full h-full bg-gray-200 flex items-center justify-center">
<User size={48} className="text-gray-400" />
</div>
)}
{/* 操作按钮 */}
<div className="absolute top-2 right-2 flex space-x-1">
<button
onClick={() => onEdit(model)}
className="p-2 bg-white/80 text-gray-600 rounded-lg hover:bg-white hover:text-blue-600 transition-colors"
title="编辑"
>
<Edit size={14} />
</button>
<button
onClick={() => onDelete(model.id)}
className="p-2 bg-white/80 text-gray-600 rounded-lg hover:bg-white hover:text-red-600 transition-colors"
title="删除"
>
<Trash2 size={14} />
</button>
</div>
</div>
<div className="p-4">
{/* 模特编号 */}
<div className="text-center">
<h3 className="text-lg font-semibold text-gray-900 mb-1">{model.model_number}</h3>
<p className="text-xs text-gray-400">
{new Date(model.created_at).toLocaleDateString()}
</p>
</div>
</div>
</div>
)
}
export default ModelCard

View File

@@ -0,0 +1,144 @@
import React from 'react'
import { Save, X, Package } from 'lucide-react'
import { open } from '@tauri-apps/plugin-dialog'
import ProjectImage from './ProjectImage'
interface ModelFormData {
model_number: string
model_image: string
}
interface ModelFormProps {
isOpen: boolean
isEditing: boolean
formData: ModelFormData
onFormDataChange: (data: ModelFormData) => void
onSubmit: () => void
onCancel: () => void
}
const ModelForm: React.FC<ModelFormProps> = ({
isOpen,
isEditing,
formData,
onFormDataChange,
onSubmit,
onCancel
}) => {
if (!isOpen) return null
const selectImage = async () => {
try {
const imagePath = await open({
multiple: false,
title: '选择模特图片',
filters: [
{
name: '图片文件',
extensions: ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg']
}
]
})
if (imagePath && typeof imagePath === 'string') {
onFormDataChange({ ...formData, model_image: imagePath })
}
} catch (error) {
console.error('Failed to select image:', error)
}
}
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl w-full max-w-md mx-4">
{/* 表单标题 */}
<div className="flex items-center justify-between p-6 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">
{isEditing ? '编辑模特' : '新建模特'}
</h2>
<button
onClick={onCancel}
className="text-gray-400 hover:text-gray-600"
>
<X size={24} />
</button>
</div>
{/* 表单内容 */}
<div className="p-6 space-y-4">
{/* 模特编号 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
*
</label>
<input
type="text"
value={formData.model_number}
onChange={(e) => onFormDataChange({ ...formData, model_number: e.target.value })}
placeholder="请输入模特编号M001"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
{/* 模特图片 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<div className="flex space-x-2">
<input
type="text"
value={formData.model_image}
onChange={(e) => onFormDataChange({ ...formData, model_image: e.target.value })}
placeholder="请选择模特图片文件"
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
readOnly
/>
<button
onClick={selectImage}
className="px-4 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors flex items-center space-x-1"
title="选择图片"
>
<Package size={16} />
<span className="text-sm"></span>
</button>
</div>
{/* 图片预览 */}
{formData.model_image && (
<div className="mt-2">
<p className="text-xs text-gray-500 mb-1">:</p>
<div className="w-32 h-40 border border-gray-200 rounded-lg overflow-hidden">
<ProjectImage
imagePath={formData.model_image}
alt="模特图片预览"
className="w-full h-full object-cover"
/>
</div>
</div>
)}
</div>
</div>
{/* 表单按钮 */}
<div className="flex items-center justify-end space-x-3 p-6 border-t border-gray-200">
<button
onClick={onCancel}
className="px-4 py-2 text-gray-600 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
>
</button>
<button
onClick={onSubmit}
disabled={!formData.model_number.trim()}
className="flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors"
>
<Save size={16} className="mr-2" />
{isEditing ? '保存' : '创建'}
</button>
</div>
</div>
</div>
)
}
export default ModelForm

View File

@@ -0,0 +1,80 @@
import React from 'react'
import { Plus, User } from 'lucide-react'
import { Model } from '../services/modelService'
import ModelCard from './ModelCard'
interface ModelListProps {
models: Model[]
loading: boolean
searchTerm: string
onEdit: (model: Model) => void
onDelete: (modelId: string) => void
onCreateNew: () => void
}
const ModelList: React.FC<ModelListProps> = ({
models,
loading,
searchTerm,
onEdit,
onDelete,
onCreateNew
}) => {
const filteredModels = models.filter(model =>
model.model_number.toLowerCase().includes(searchTerm.toLowerCase())
)
// 加载状态
if (loading) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{Array.from({ length: 8 }).map((_, index) => (
<div key={index} className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
<div className="h-48 bg-gray-200 animate-pulse"></div>
<div className="p-4">
<div className="h-6 bg-gray-200 rounded animate-pulse mb-2"></div>
<div className="h-3 bg-gray-200 rounded animate-pulse w-2/3 mx-auto"></div>
</div>
</div>
))}
</div>
)
}
// 空状态
if (filteredModels.length === 0) {
return (
<div className="text-center py-12">
<User className="mx-auto text-gray-400 mb-4" size={64} />
<div className="text-gray-400 mb-4">
{searchTerm ? '没有找到匹配的模特' : '暂无模特'}
</div>
{!searchTerm && (
<button
onClick={onCreateNew}
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<Plus size={16} className="mr-2" />
</button>
)}
</div>
)
}
// 模特列表
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{filteredModels.map((model) => (
<ModelCard
key={model.id}
model={model}
onEdit={onEdit}
onDelete={onDelete}
/>
))}
</div>
)
}
export default ModelList

View File

@@ -0,0 +1,39 @@
import React from 'react'
import { Search, Plus } from 'lucide-react'
interface ModelSearchBarProps {
searchTerm: string
onSearchChange: (value: string) => void
onCreateNew: () => void
}
const ModelSearchBar: React.FC<ModelSearchBarProps> = ({
searchTerm,
onSearchChange,
onCreateNew
}) => {
return (
<div className="flex items-center justify-between mb-6">
<div className="relative flex-1 max-w-md">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
<input
type="text"
placeholder="搜索模特编号..."
value={searchTerm}
onChange={(e) => onSearchChange(e.target.value)}
className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent w-full"
/>
</div>
<button
onClick={onCreateNew}
className="ml-4 flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<Plus size={20} className="mr-2" />
</button>
</div>
)
}
export default ModelSearchBar

View File

@@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, Layout, Clock, CheckCircle, XCircle, List, Tags } from 'lucide-react'
import { Home, Video, Settings, FolderOpen, Music, Image, Sparkles, Layout, Clock, CheckCircle, XCircle, List, Tags, User } from 'lucide-react'
import { clsx } from 'clsx'
const Sidebar: React.FC = () => {
@@ -25,6 +25,7 @@ const Sidebar: React.FC = () => {
{ path: '/templates', icon: Layout, label: '模板管理' },
{ path: '/resource-categories', icon: Tags, label: '分类管理' },
{ path: '/projects', icon: FolderOpen, label: '项目' },
{ path: '/models', icon: User, label: '模特管理' },
{ path: '/media', icon: Image, label: '媒体库' },
{ path: '/audio', icon: Music, label: '音频' },
{ path: '/settings', icon: Settings, label: '设置' },

View File

@@ -0,0 +1,164 @@
import React, { useState, useEffect } from 'react'
import { ModelService, Model } from '../services/modelService'
import ModelSearchBar from '../components/ModelSearchBar'
import ModelList from '../components/ModelList'
import ModelForm from '../components/ModelForm'
interface FormData {
model_number: string
model_image: string
}
const ModelManagePage: React.FC = () => {
const [models, setModels] = useState<Model[]>([])
const [loading, setLoading] = useState(true)
const [searchTerm, setSearchTerm] = useState('')
const [editingModel, setEditingModel] = useState<Model | null>(null)
const [showCreateForm, setShowCreateForm] = useState(false)
const [formData, setFormData] = useState<FormData>({
model_number: '',
model_image: ''
})
useEffect(() => {
loadModels()
}, [])
const loadModels = async () => {
try {
setLoading(true)
const response = await ModelService.getAllModels()
if (response.status && response.data) {
setModels(response.data)
} else {
console.error('Failed to load models:', response.msg)
}
} catch (error) {
console.error('Failed to load models:', error)
} finally {
setLoading(false)
}
}
const handleCreateModel = async () => {
try {
const response = await ModelService.createModel(formData)
if (response.status && response.data) {
setModels([...models, response.data])
handleCancelForm()
} else {
console.error('创建失败:', response.msg || '未知错误')
alert('创建失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
console.error('Failed to create model:', error)
alert('创建失败: ' + (error instanceof Error ? error.message : '未知错误'))
}
}
const handleUpdateModel = async () => {
if (!editingModel) return
try {
const response = await ModelService.updateModel(editingModel.id, formData)
if (response.status && response.data) {
const updatedModels = models.map(model =>
model.id === editingModel.id ? response.data! : model
)
setModels(updatedModels)
handleCancelForm()
} else {
console.error('更新失败:', response.msg || '未知错误')
alert('更新失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
console.error('Failed to update model:', error)
alert('更新失败: ' + (error instanceof Error ? error.message : '未知错误'))
}
}
const handleDeleteModel = async (modelId: string) => {
if (!confirm('确定要删除这个模特吗?')) return
try {
const response = await ModelService.deleteModel(modelId)
if (response.status) {
setModels(models.filter(model => model.id !== modelId))
} else {
console.error('删除失败:', response.msg || '未知错误')
alert('删除失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
console.error('Failed to delete model:', error)
alert('删除失败: ' + (error instanceof Error ? error.message : '未知错误'))
}
}
const handleEditModel = (model: Model) => {
setEditingModel(model)
setFormData({
model_number: model.model_number,
model_image: model.model_image
})
setShowCreateForm(true)
}
const handleCreateNew = () => {
setEditingModel(null)
setFormData({ model_number: '', model_image: '' })
setShowCreateForm(true)
}
const handleCancelForm = () => {
setEditingModel(null)
setShowCreateForm(false)
setFormData({ model_number: '', model_image: '' })
}
const handleSubmitForm = () => {
if (editingModel) {
handleUpdateModel()
} else {
handleCreateModel()
}
}
return (
<div className="p-6">
{/* 页面标题 */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900"></h1>
<p className="text-gray-600 mt-2"></p>
</div>
{/* 搜索和操作栏 */}
<ModelSearchBar
searchTerm={searchTerm}
onSearchChange={setSearchTerm}
onCreateNew={handleCreateNew}
/>
{/* 模特列表 */}
<ModelList
models={models}
loading={loading}
searchTerm={searchTerm}
onEdit={handleEditModel}
onDelete={handleDeleteModel}
onCreateNew={handleCreateNew}
/>
{/* 模特表单 */}
<ModelForm
isOpen={showCreateForm}
isEditing={!!editingModel}
formData={formData}
onFormDataChange={setFormData}
onSubmit={handleSubmitForm}
onCancel={handleCancelForm}
/>
</div>
)
}
export default ModelManagePage

View File

@@ -0,0 +1,213 @@
import { invoke } from '@tauri-apps/api/core'
export interface Model {
id: string
model_number: string
model_image: string
created_at: string
updated_at: string
is_active: boolean
}
export interface CreateModelRequest {
model_number: string
model_image?: string
}
export interface UpdateModelRequest {
model_number?: string
model_image?: string
}
export interface ApiResponse<T> {
status: boolean
data?: T
msg?: string
}
export class ModelService {
/**
* 获取所有模特
*/
static async getAllModels(): Promise<ApiResponse<Model[]>> {
try {
console.log('Calling get_all_models...')
const result = await invoke('get_all_models')
console.log('Raw result from Tauri:', result)
if (typeof result === 'string') {
try {
const parsed = JSON.parse(result)
console.log('Parsed result:', parsed)
return parsed as ApiResponse<Model[]>
} catch (parseError) {
console.error('Failed to parse JSON response:', parseError)
return {
status: false,
msg: `Invalid JSON response: ${result}`
}
}
}
return result as ApiResponse<Model[]>
} catch (error) {
console.error('Failed to get all models:', error)
return {
status: false,
msg: error instanceof Error ? error.message : 'Unknown error'
}
}
}
/**
* 根据ID获取模特
*/
static async getModelById(modelId: string): Promise<ApiResponse<Model>> {
try {
const result = await invoke('get_model_by_id', { modelId })
if (typeof result === 'string') {
try {
const parsed = JSON.parse(result)
return parsed as ApiResponse<Model>
} catch (parseError) {
return {
status: false,
msg: `Invalid JSON response: ${result}`
}
}
}
return result as ApiResponse<Model>
} catch (error) {
console.error('Failed to get model by id:', error)
return {
status: false,
msg: error instanceof Error ? error.message : 'Unknown error'
}
}
}
/**
* 创建新模特
*/
static async createModel(request: CreateModelRequest): Promise<ApiResponse<Model>> {
try {
console.log('Calling create_model with:', request)
const result = await invoke('create_model', { request })
console.log('Raw result from Tauri:', result)
if (typeof result === 'string') {
try {
const parsed = JSON.parse(result)
console.log('Parsed result:', parsed)
return parsed as ApiResponse<Model>
} catch (parseError) {
console.error('Failed to parse JSON response:', parseError)
return {
status: false,
msg: `Invalid JSON response: ${result}`
}
}
}
return result as ApiResponse<Model>
} catch (error) {
console.error('Failed to create model:', error)
return {
status: false,
msg: error instanceof Error ? error.message : 'Unknown error'
}
}
}
/**
* 更新模特
*/
static async updateModel(
modelId: string,
request: UpdateModelRequest
): Promise<ApiResponse<Model>> {
try {
const result = await invoke('update_model', { modelId, request })
if (typeof result === 'string') {
try {
const parsed = JSON.parse(result)
return parsed as ApiResponse<Model>
} catch (parseError) {
return {
status: false,
msg: `Invalid JSON response: ${result}`
}
}
}
return result as ApiResponse<Model>
} catch (error) {
console.error('Failed to update model:', error)
return {
status: false,
msg: error instanceof Error ? error.message : 'Unknown error'
}
}
}
/**
* 删除模特
*/
static async deleteModel(modelId: string): Promise<ApiResponse<boolean>> {
try {
const result = await invoke('delete_model', { modelId })
if (typeof result === 'string') {
try {
const parsed = JSON.parse(result)
return parsed as ApiResponse<boolean>
} catch (parseError) {
return {
status: false,
msg: `Invalid JSON response: ${result}`
}
}
}
return result as ApiResponse<boolean>
} catch (error) {
console.error('Failed to delete model:', error)
return {
status: false,
msg: error instanceof Error ? error.message : 'Unknown error'
}
}
}
/**
* 搜索模特
*/
static async searchModels(keyword: string): Promise<ApiResponse<Model[]>> {
try {
const result = await invoke('search_models', { keyword })
if (typeof result === 'string') {
try {
const parsed = JSON.parse(result)
return parsed as ApiResponse<Model[]>
} catch (parseError) {
return {
status: false,
msg: `Invalid JSON response: ${result}`
}
}
}
return result as ApiResponse<Model[]>
} catch (error) {
console.error('Failed to search models:', error)
return {
status: false,
msg: error instanceof Error ? error.message : 'Unknown error'
}
}
}
}