马良AI写作初始化仓库

This commit is contained in:
邓滨杰
2025-09-10 00:07:52 +08:00
parent 3c06bb1a03
commit 39c0f8840f
1309 changed files with 318528 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
import 'package:flutter/material.dart';
import 'package:ainoval/utils/web_theme.dart';
import 'package:ainoval/widgets/common/preset_dropdown_button.dart';
import 'package:ainoval/models/preset_models.dart';
/// 选项卡项目数据
class TabItem {
/// 构造函数
const TabItem({
required this.id,
required this.label,
this.icon,
});
/// 标识符
final String id;
/// 显示文字
final String label;
/// 图标
final IconData? icon;
}
/// 自定义选项卡栏组件
/// 支持图标、文字和预设按钮
class CustomTabBar extends StatelessWidget {
/// 构造函数
const CustomTabBar({
super.key,
required this.tabs,
required this.selectedTabId,
required this.onTabChanged,
this.showPresets = false,
this.onPresetsPressed,
this.presetsLabel = '预设',
this.usePresetDropdown = false,
this.presetFeatureType,
this.currentPreset,
this.onPresetSelected,
this.onCreatePreset,
this.onManagePresets,
this.novelId,
});
/// 选项卡列表
final List<TabItem> tabs;
/// 当前选中的选项卡ID
final String selectedTabId;
/// 选项卡改变回调
final ValueChanged<String> onTabChanged;
/// 是否显示预设按钮
final bool showPresets;
/// 预设按钮点击回调
final VoidCallback? onPresetsPressed;
/// 预设按钮文字
final String presetsLabel;
/// 是否使用新的预设下拉框
final bool usePresetDropdown;
/// 预设功能类型(用于过滤预设)
final String? presetFeatureType;
/// 当前选中的预设
final AIPromptPreset? currentPreset;
/// 预设选择回调
final ValueChanged<AIPromptPreset>? onPresetSelected;
/// 创建预设回调
final VoidCallback? onCreatePreset;
/// 管理预设回调
final VoidCallback? onManagePresets;
/// 小说ID用于过滤预设
final String? novelId;
@override
Widget build(BuildContext context) {
final isDark = WebTheme.isDarkMode(context);
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: isDark ? WebTheme.darkGrey200 : WebTheme.grey200,
width: 1,
),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Row(
children: [
// 选项卡列表
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: tabs.map((tab) => _buildTab(context, tab, isDark)).toList(),
),
),
),
// 预设按钮
if (showPresets) ...[
const SizedBox(width: 8),
usePresetDropdown ? _buildPresetDropdown() : _buildPresetsButton(context, isDark),
],
],
),
),
);
}
/// 构建单个选项卡
Widget _buildTab(BuildContext context, TabItem tab, bool isDark) {
final isSelected = tab.id == selectedTabId;
return Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => onTabChanged(tab.id),
borderRadius: BorderRadius.circular(6),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 选项卡内容
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: isSelected
? (isDark ? WebTheme.darkGrey300.withValues(alpha: 0.2) : WebTheme.grey100)
: Colors.transparent,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (tab.icon != null) ...[
Icon(
tab.icon,
size: 16,
color: isSelected
? (isDark ? WebTheme.darkGrey700 : WebTheme.grey700)
: (isDark ? WebTheme.darkGrey500 : WebTheme.grey500),
),
const SizedBox(width: 8),
],
Text(
tab.label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isSelected
? (isDark ? WebTheme.darkGrey700 : WebTheme.grey700)
: (isDark ? WebTheme.darkGrey500 : WebTheme.grey500),
),
),
],
),
),
// 底部指示线
Container(
height: 2,
width: 40,
margin: const EdgeInsets.only(top: 6),
decoration: BoxDecoration(
color: isSelected
? (isDark ? WebTheme.darkGrey700 : WebTheme.grey700)
: Colors.transparent,
borderRadius: BorderRadius.circular(1),
),
),
],
),
),
),
);
}
/// 构建预设下拉框
Widget _buildPresetDropdown() {
return PresetDropdownButton(
featureType: presetFeatureType ?? '',
currentPreset: currentPreset,
onPresetSelected: onPresetSelected,
onCreatePreset: onCreatePreset,
onManagePresets: onManagePresets,
novelId: novelId,
label: presetsLabel,
);
}
/// 构建预设按钮
Widget _buildPresetsButton(BuildContext context, bool isDark) {
return Material(
type: MaterialType.transparency,
child: InkWell(
onTap: onPresetsPressed,
borderRadius: BorderRadius.circular(6),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.tune,
size: 16,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const SizedBox(width: 6),
Text(
presetsLabel,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(width: 4),
Icon(
Icons.keyboard_arrow_down,
size: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
],
),
),
),
);
}
}