马良AI写作初始化仓库
This commit is contained in:
@@ -0,0 +1,874 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import '../../models/prompt_models.dart';
|
||||
import '../../services/api_service/repositories/impl/admin_repository_impl.dart';
|
||||
import '../../utils/logger.dart';
|
||||
import '../../services/api_service/repositories/impl/admin_repository_templates_extension.dart';
|
||||
import '../../widgets/common/loading_indicator.dart';
|
||||
import 'widgets/public_template_card.dart';
|
||||
import 'widgets/add_official_template_dialog.dart';
|
||||
import 'widgets/template_statistics_dialog.dart';
|
||||
import 'widgets/template_details_dialog.dart'; // Added import for TemplateDetailsDialog
|
||||
import 'widgets/edit_template_dialog.dart';
|
||||
|
||||
/// 公共模板管理页面
|
||||
class PublicTemplatesManagementScreen extends StatefulWidget {
|
||||
const PublicTemplatesManagementScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PublicTemplatesManagementScreen> createState() => _PublicTemplatesManagementScreenState();
|
||||
}
|
||||
|
||||
class _PublicTemplatesManagementScreenState extends State<PublicTemplatesManagementScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const PublicTemplatesManagementBody();
|
||||
}
|
||||
}
|
||||
|
||||
/// 公共模板管理页面主体
|
||||
class PublicTemplatesManagementBody extends StatefulWidget {
|
||||
const PublicTemplatesManagementBody({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PublicTemplatesManagementBody> createState() => _PublicTemplatesManagementBodyState();
|
||||
}
|
||||
|
||||
class _PublicTemplatesManagementBodyState extends State<PublicTemplatesManagementBody>
|
||||
with TickerProviderStateMixin {
|
||||
final AdminRepositoryImpl _adminRepository = AdminRepositoryImpl();
|
||||
late TabController _tabController;
|
||||
|
||||
List<PromptTemplate> _templates = [];
|
||||
List<PromptTemplate> _selectedTemplates = [];
|
||||
bool _isLoading = true;
|
||||
bool _batchMode = false;
|
||||
String? _error;
|
||||
String _searchQuery = '';
|
||||
String _currentTab = 'ALL';
|
||||
AIFeatureType? _filterFeatureType;
|
||||
bool? _filterVerified;
|
||||
bool? _filterIsPublic;
|
||||
String _sortOption = 'LATEST';
|
||||
int _pageSize = 30;
|
||||
int _currentPage = 1;
|
||||
Timer? _searchDebounce;
|
||||
|
||||
static const List<String> _tabs = ['ALL', 'OFFICIAL', 'USER_SUBMITTED', 'PENDING_REVIEW'];
|
||||
static const Map<String, String> _tabLabels = {
|
||||
'ALL': '全部模板',
|
||||
'OFFICIAL': '官方模板',
|
||||
'USER_SUBMITTED': '用户提交',
|
||||
'PENDING_REVIEW': '待审核',
|
||||
};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: _tabs.length, vsync: this);
|
||||
_tabController.addListener(_onTabChanged);
|
||||
_loadTemplates();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchDebounce?.cancel();
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onTabChanged() {
|
||||
if (!_tabController.indexIsChanging) {
|
||||
setState(() {
|
||||
_currentTab = _tabs[_tabController.index];
|
||||
_selectedTemplates.clear();
|
||||
_batchMode = false;
|
||||
});
|
||||
_loadTemplates();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 1600),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildHeader(),
|
||||
_buildTabBar(),
|
||||
Expanded(
|
||||
child: _buildContent(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 搜索框
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: TextField(
|
||||
onChanged: (value) {
|
||||
_searchDebounce?.cancel();
|
||||
_searchDebounce = Timer(const Duration(milliseconds: 400), () {
|
||||
setState(() {
|
||||
_searchQuery = value;
|
||||
_currentPage = 1;
|
||||
});
|
||||
_loadTemplates();
|
||||
});
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: '搜索模板名称或描述...',
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 16),
|
||||
|
||||
// 功能类型筛选
|
||||
SizedBox(
|
||||
width: 280,
|
||||
child: DropdownButtonFormField<AIFeatureType?>(
|
||||
value: _filterFeatureType,
|
||||
decoration: InputDecoration(
|
||||
labelText: '功能类型',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
),
|
||||
items: [
|
||||
const DropdownMenuItem<AIFeatureType?>(
|
||||
value: null,
|
||||
child: Text('全部类型'),
|
||||
),
|
||||
..._buildFeatureTypeOptions(),
|
||||
],
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_filterFeatureType = value;
|
||||
_currentPage = 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(
|
||||
width: 160,
|
||||
child: DropdownButtonFormField<bool?>(
|
||||
value: _filterVerified,
|
||||
decoration: InputDecoration(
|
||||
labelText: '认证',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
),
|
||||
items: const [
|
||||
DropdownMenuItem<bool?>(value: null, child: Text('全部')),
|
||||
DropdownMenuItem<bool?>(value: true, child: Text('认证')),
|
||||
DropdownMenuItem<bool?>(value: false, child: Text('未认证')),
|
||||
],
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_filterVerified = v;
|
||||
_currentPage = 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(
|
||||
width: 160,
|
||||
child: DropdownButtonFormField<bool?>(
|
||||
value: _filterIsPublic,
|
||||
decoration: InputDecoration(
|
||||
labelText: '可见性',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
),
|
||||
items: const [
|
||||
DropdownMenuItem<bool?>(value: null, child: Text('全部')),
|
||||
DropdownMenuItem<bool?>(value: true, child: Text('公开')),
|
||||
DropdownMenuItem<bool?>(value: false, child: Text('私有')),
|
||||
],
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_filterIsPublic = v;
|
||||
_currentPage = 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
SizedBox(
|
||||
width: 180,
|
||||
child: DropdownButtonFormField<String>(
|
||||
value: _sortOption,
|
||||
decoration: InputDecoration(
|
||||
labelText: '排序',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
),
|
||||
items: const [
|
||||
DropdownMenuItem(value: 'LATEST', child: Text('最新')),
|
||||
DropdownMenuItem(value: 'MOST_USED', child: Text('使用最多')),
|
||||
DropdownMenuItem(value: 'RATING', child: Text('评分最高')),
|
||||
],
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_sortOption = v ?? 'LATEST';
|
||||
_currentPage = 1;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
// 批量操作开关
|
||||
if (_templates.isNotEmpty) ...[
|
||||
FilterChip(
|
||||
label: Text('批量操作${_batchMode ? ' (${_selectedTemplates.length})' : ''}'),
|
||||
selected: _batchMode,
|
||||
onSelected: (selected) {
|
||||
setState(() {
|
||||
_batchMode = selected;
|
||||
if (!selected) {
|
||||
_selectedTemplates.clear();
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
|
||||
// 批量操作按钮
|
||||
if (_batchMode && _selectedTemplates.isNotEmpty) ...[
|
||||
ElevatedButton.icon(
|
||||
onPressed: _batchPublish,
|
||||
icon: const Icon(Icons.publish),
|
||||
label: const Text('批量发布'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton.icon(
|
||||
onPressed: _batchSetVerified,
|
||||
icon: const Icon(Icons.verified),
|
||||
label: const Text('批量认证'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
|
||||
// 添加官方模板按钮
|
||||
ElevatedButton.icon(
|
||||
onPressed: _showAddOfficialTemplateDialog,
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('添加官方模板'),
|
||||
),
|
||||
|
||||
const SizedBox(width: 8),
|
||||
|
||||
// 刷新按钮
|
||||
IconButton(
|
||||
onPressed: _loadTemplates,
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: '刷新',
|
||||
),
|
||||
|
||||
// 统计按钮
|
||||
IconButton(
|
||||
onPressed: _showStatisticsDialog,
|
||||
icon: const Icon(Icons.analytics),
|
||||
tooltip: '查看统计',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTabBar() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: TabBar(
|
||||
controller: _tabController,
|
||||
isScrollable: true,
|
||||
tabs: _tabs.map((tab) => Tab(
|
||||
text: _tabLabels[tab],
|
||||
)).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent() {
|
||||
if (_isLoading) {
|
||||
return const Center(child: LoadingIndicator());
|
||||
}
|
||||
|
||||
if (_error != null) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
size: 64,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'加载失败',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_error!,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color?.withOpacity(0.7),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton.icon(
|
||||
onPressed: _loadTemplates,
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('重试'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (_templates.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.article_outlined,
|
||||
size: 64,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.3),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'暂无模板',
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'当前筛选条件下没有找到模板',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton.icon(
|
||||
onPressed: _showAddOfficialTemplateDialog,
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('添加官方模板'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return TabBarView(
|
||||
controller: _tabController,
|
||||
children: _tabs.map((tab) => _buildTemplateList()).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTemplateList() {
|
||||
final filteredTemplates = _getFilteredTemplates();
|
||||
final visibleCount = (_currentPage * _pageSize).clamp(0, filteredTemplates.length);
|
||||
final items = filteredTemplates.take(visibleCount).toList();
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final template = items[index];
|
||||
return PublicTemplateCard(
|
||||
template: template,
|
||||
isSelected: _selectedTemplates.contains(template),
|
||||
batchMode: _batchMode,
|
||||
onTap: () => _onTemplateCardTap(template),
|
||||
onEdit: () => _showEditTemplateDialog(template),
|
||||
onDuplicate: () => _duplicatePublicTemplate(template),
|
||||
onReview: () => _showTemplateReviewDialog(template),
|
||||
onPublish: () => _publishTemplate(template),
|
||||
onSetVerified: () => _setTemplateVerified(template),
|
||||
onDelete: () => _deleteTemplate(template),
|
||||
onSelectionChanged: (selected) => _onTemplateSelectionChanged(template, selected),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (visibleCount < filteredTemplates.length)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_currentPage += 1;
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.expand_more),
|
||||
label: Text('加载更多(${filteredTemplates.length - visibleCount})'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _duplicatePublicTemplate(PromptTemplate template) async {
|
||||
final controller = TextEditingController(text: '${template.name} (复制)');
|
||||
final newName = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('复制模板'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
decoration: const InputDecoration(
|
||||
labelText: '新模板名称',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(null),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(controller.text.trim()),
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (newName == null || newName.isEmpty) return;
|
||||
|
||||
try {
|
||||
final now = DateTime.now();
|
||||
// 使用增强模板模型创建,以便包含 userId 等关键字段
|
||||
final enhanced = _convertToEnhancedTemplate(template).copyWith(
|
||||
id: '',
|
||||
name: newName,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
usageCount: 0,
|
||||
favoriteCount: 0,
|
||||
isFavorite: false,
|
||||
isDefault: false,
|
||||
shareCode: null,
|
||||
// 复制来源
|
||||
authorId: template.authorId ?? (template.isPublic ? 'system' : null),
|
||||
);
|
||||
|
||||
await _adminRepository.createOfficialEnhancedTemplate(enhanced);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('已复制为新模板: ${enhanced.name}')),
|
||||
);
|
||||
_loadTemplates();
|
||||
}
|
||||
} catch (e) {
|
||||
AppLogger.e('PublicTemplatesManagement', '复制模板失败', e);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('复制失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<PromptTemplate> _getFilteredTemplates() {
|
||||
List<PromptTemplate> filteredTemplates = List.from(_templates);
|
||||
|
||||
// 注意:标签页筛选现在主要在API调用层面完成
|
||||
// OFFICIAL -> getVerifiedTemplates() 获取已验证模板
|
||||
// USER_SUBMITTED -> getPublicTemplates() 获取所有公共模板(用户提交的)
|
||||
// PENDING_REVIEW -> getPendingTemplates() 获取待审核模板
|
||||
// ALL -> getPublicTemplates() 获取所有公共模板
|
||||
|
||||
// 根据搜索条件筛选
|
||||
if (_searchQuery.isNotEmpty) {
|
||||
filteredTemplates = filteredTemplates.where((template) {
|
||||
final query = _searchQuery.toLowerCase();
|
||||
return template.name.toLowerCase().contains(query) ||
|
||||
(template.description?.toLowerCase().contains(query) ?? false);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// 功能类型筛选
|
||||
if (_filterFeatureType != null) {
|
||||
filteredTemplates = filteredTemplates
|
||||
.where((t) => t.featureType == _filterFeatureType)
|
||||
.toList();
|
||||
}
|
||||
|
||||
// 认证筛选
|
||||
if (_filterVerified != null) {
|
||||
filteredTemplates = filteredTemplates.where((t) => t.isVerified == _filterVerified).toList();
|
||||
}
|
||||
|
||||
// 可见性筛选
|
||||
if (_filterIsPublic != null) {
|
||||
filteredTemplates = filteredTemplates.where((t) => t.isPublic == _filterIsPublic).toList();
|
||||
}
|
||||
|
||||
// 排序
|
||||
switch (_sortOption) {
|
||||
case 'MOST_USED':
|
||||
filteredTemplates.sort((a, b) => (b.useCount ?? 0).compareTo(a.useCount ?? 0));
|
||||
break;
|
||||
case 'RATING':
|
||||
filteredTemplates.sort((a, b) => (b.averageRating ?? 0).compareTo(a.averageRating ?? 0));
|
||||
break;
|
||||
case 'LATEST':
|
||||
default:
|
||||
filteredTemplates.sort((a, b) => b.updatedAt.compareTo(a.updatedAt));
|
||||
break;
|
||||
}
|
||||
|
||||
return filteredTemplates;
|
||||
}
|
||||
|
||||
List<DropdownMenuItem<AIFeatureType>> _buildFeatureTypeOptions() {
|
||||
final Set<AIFeatureType> featureTypes =
|
||||
_templates.map((t) => t.featureType).toSet();
|
||||
final Map<AIFeatureType, String> labels = {
|
||||
AIFeatureType.textExpansion: '文本扩写',
|
||||
AIFeatureType.textRefactor: '文本润色',
|
||||
AIFeatureType.textSummary: '文本总结',
|
||||
AIFeatureType.sceneToSummary: '场景转摘要',
|
||||
AIFeatureType.summaryToScene: '摘要转场景',
|
||||
AIFeatureType.aiChat: 'AI对话',
|
||||
AIFeatureType.novelGeneration: '小说生成',
|
||||
AIFeatureType.professionalFictionContinuation: '专业续写',
|
||||
AIFeatureType.sceneBeatGeneration: '场景节拍生成',
|
||||
};
|
||||
|
||||
final List<AIFeatureType> sorted = featureTypes.toList()
|
||||
..sort((a, b) => (labels[a] ?? a.name).compareTo(labels[b] ?? b.name));
|
||||
|
||||
return sorted
|
||||
.map((ft) => DropdownMenuItem<AIFeatureType>(
|
||||
value: ft,
|
||||
child: Text(labels[ft] ?? ft.name),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// 数据加载
|
||||
Future<void> _loadTemplates() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
});
|
||||
|
||||
try {
|
||||
List<PromptTemplate> templates;
|
||||
|
||||
switch (_currentTab) {
|
||||
case 'OFFICIAL':
|
||||
templates = await _adminRepository.getVerifiedTemplates();
|
||||
break;
|
||||
case 'PENDING_REVIEW':
|
||||
templates = await _adminRepository.getPendingTemplates();
|
||||
break;
|
||||
case 'USER_SUBMITTED':
|
||||
templates = await _adminRepository.getAllUserTemplates(
|
||||
page: 0,
|
||||
size: 100, // 暂时设置较大值,后续可以实现真正的分页
|
||||
search: _searchQuery.isEmpty ? null : _searchQuery,
|
||||
);
|
||||
break;
|
||||
case 'ALL':
|
||||
default:
|
||||
templates = await _adminRepository.getPublicTemplates(
|
||||
search: _searchQuery.isEmpty ? null : _searchQuery,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_templates = templates;
|
||||
_isLoading = false;
|
||||
});
|
||||
} catch (e) {
|
||||
AppLogger.e('PublicTemplatesManagement', '加载模板失败', e);
|
||||
setState(() {
|
||||
_error = e.toString();
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 事件处理
|
||||
void _onTemplateCardTap(PromptTemplate template) {
|
||||
if (_batchMode) {
|
||||
_onTemplateSelectionChanged(template, !_selectedTemplates.contains(template));
|
||||
} else {
|
||||
_showTemplateDetails(template);
|
||||
}
|
||||
}
|
||||
|
||||
void _onTemplateSelectionChanged(PromptTemplate template, bool selected) {
|
||||
setState(() {
|
||||
if (selected) {
|
||||
_selectedTemplates.add(template);
|
||||
} else {
|
||||
_selectedTemplates.remove(template);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 对话框显示
|
||||
void _showAddOfficialTemplateDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AddOfficialTemplateDialog(
|
||||
onSuccess: _loadTemplates,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showEditTemplateDialog(PromptTemplate template) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => EditTemplateDialog(
|
||||
template: template,
|
||||
onSuccess: _loadTemplates,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 将PromptTemplate转换为EnhancedUserPromptTemplate
|
||||
EnhancedUserPromptTemplate _convertToEnhancedTemplate(PromptTemplate template) {
|
||||
return EnhancedUserPromptTemplate(
|
||||
id: template.id,
|
||||
userId: template.authorId ?? '',
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
featureType: template.featureType,
|
||||
systemPrompt: '', // PromptTemplate没有单独的systemPrompt字段
|
||||
userPrompt: template.content,
|
||||
tags: template.templateTags ?? [],
|
||||
categories: [],
|
||||
isPublic: template.isPublic,
|
||||
shareCode: null,
|
||||
isFavorite: template.isFavorite,
|
||||
isDefault: template.isDefault,
|
||||
usageCount: template.useCount?.toInt() ?? 0,
|
||||
rating: template.averageRating ?? 0.0,
|
||||
ratingCount: template.ratingCount ?? 0,
|
||||
createdAt: template.createdAt,
|
||||
updatedAt: template.updatedAt,
|
||||
lastUsedAt: null,
|
||||
isVerified: template.isVerified,
|
||||
authorId: template.authorId,
|
||||
version: 1,
|
||||
language: 'zh',
|
||||
favoriteCount: 0,
|
||||
reviewedAt: null,
|
||||
reviewedBy: null,
|
||||
reviewComment: null,
|
||||
);
|
||||
}
|
||||
|
||||
void _showTemplateReviewDialog(PromptTemplate template) {
|
||||
// TODO: 实现PromptTemplate的审核对话框
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('模板审核功能开发中: ${template.name}')),
|
||||
);
|
||||
}
|
||||
|
||||
void _showTemplateDetails(PromptTemplate template) {
|
||||
// 将PromptTemplate转换为EnhancedUserPromptTemplate以兼容现有对话框
|
||||
final enhancedTemplate = _convertToEnhancedTemplate(template);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => TemplateDetailsDialog(
|
||||
template: enhancedTemplate,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showStatisticsDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => TemplateStatisticsDialog(),
|
||||
);
|
||||
}
|
||||
|
||||
// 操作方法
|
||||
Future<void> _publishTemplate(PromptTemplate template) async {
|
||||
try {
|
||||
await _adminRepository.publishTemplate(template.id);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('模板 "${template.name}" 发布成功')),
|
||||
);
|
||||
_loadTemplates();
|
||||
} catch (e) {
|
||||
AppLogger.e('PublicTemplatesManagement', '发布模板失败', e);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('发布失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setTemplateVerified(PromptTemplate template) async {
|
||||
try {
|
||||
await _adminRepository.setTemplateVerified(template.id, true);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('模板 "${template.name}" 已设为认证')),
|
||||
);
|
||||
_loadTemplates();
|
||||
} catch (e) {
|
||||
AppLogger.e('PublicTemplatesManagement', '设置模板认证失败', e);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('设置认证失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deleteTemplate(PromptTemplate template) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('确认删除'),
|
||||
content: Text('确定要删除模板 "${template.name}" 吗?此操作不可撤销。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
child: const Text('删除'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmed != true) return;
|
||||
|
||||
try {
|
||||
await _adminRepository.deleteTemplate(template.id);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('模板 "${template.name}" 删除成功')),
|
||||
);
|
||||
_loadTemplates();
|
||||
} catch (e) {
|
||||
AppLogger.e('PublicTemplatesManagement', '删除模板失败', e);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('删除失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量操作
|
||||
Future<void> _batchPublish() async {
|
||||
if (_selectedTemplates.isEmpty) return;
|
||||
|
||||
try {
|
||||
for (final template in _selectedTemplates) {
|
||||
await _adminRepository.publishTemplate(template.id);
|
||||
}
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('成功发布 ${_selectedTemplates.length} 个模板')),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTemplates.clear();
|
||||
_batchMode = false;
|
||||
});
|
||||
_loadTemplates();
|
||||
} catch (e) {
|
||||
AppLogger.e('PublicTemplatesManagement', '批量发布模板失败', e);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('批量发布失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _batchSetVerified() async {
|
||||
if (_selectedTemplates.isEmpty) return;
|
||||
|
||||
try {
|
||||
for (final template in _selectedTemplates) {
|
||||
await _adminRepository.setTemplateVerified(template.id, true);
|
||||
}
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('成功设置 ${_selectedTemplates.length} 个模板为认证')),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_selectedTemplates.clear();
|
||||
_batchMode = false;
|
||||
});
|
||||
_loadTemplates();
|
||||
} catch (e) {
|
||||
AppLogger.e('PublicTemplatesManagement', '批量设置模板认证失败', e);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('批量设置认证失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user