本次提交新增了以下核心内容: 1. 后端管理模块:包含字体同步、插件元数据、插件用户设置、稍后读消息/共享列表的控制器、模型、验证器与多语言配置 2. Flutter数据同步模块:统一的事件总线与兼容层,替代分散的StreamController 3. 鸿蒙端路由适配:完整的路由定义、构建器与占位组件 4. 后端API接口:字体同步与插件更新的服务端API,支持自动建表与跨域请求 5. 鸿蒙权限校验脚本:用于校验module.json5与string.json的权限声明一致性
170 lines
4.3 KiB
Dart
170 lines
4.3 KiB
Dart
// ============================================================
|
||
// 闲言APP — 编辑器路由管理
|
||
// 创建时间: 2026-04-25
|
||
// 更新时间: 2026-06-18
|
||
// 作用: 统一管理编辑器内所有页面跳转和弹窗,基于 go_router + Navigator
|
||
// 上次更新: 重构为从 route_registry 统一生成 GoRoute,保留 EditorNav 扩展
|
||
// ============================================================
|
||
|
||
import 'dart:typed_data';
|
||
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
|
||
import 'package:xianyan/editor/services/core/model_catalog_service.dart';
|
||
import 'app_routes.dart';
|
||
import 'route_def.dart';
|
||
import 'route_builders.dart';
|
||
|
||
class EditorRoutes {
|
||
EditorRoutes._();
|
||
|
||
static const String imagePreview = '/editor/preview';
|
||
static const String imageCrop = '/editor/crop';
|
||
static const String draftList = '/editor/drafts';
|
||
static const String imageGallery = '/editor/gallery';
|
||
static const String model3dPreview = '/editor/3d-preview';
|
||
}
|
||
|
||
List<GoRoute> buildEditorRoutes(GlobalKey<NavigatorState> rootNavigatorKey) =>
|
||
buildModuleGoRoutes(RouteModule.editor, rootNavigatorKey);
|
||
|
||
extension EditorNav on BuildContext {
|
||
void goToImagePreview({
|
||
required ImageProvider provider,
|
||
required String heroTag,
|
||
}) {
|
||
push(
|
||
Uri(
|
||
path: EditorRoutes.imagePreview,
|
||
queryParameters: {'heroTag': heroTag},
|
||
).toString(),
|
||
extra: ImagePreviewExtra(provider: provider),
|
||
);
|
||
}
|
||
|
||
void goToImagePreviewBytes(Uint8List bytes) {
|
||
push(
|
||
Uri(path: EditorRoutes.imagePreview).toString(),
|
||
extra: ImagePreviewExtra(bytes: bytes),
|
||
);
|
||
}
|
||
|
||
Future<Uint8List?> goToImageCrop(Uint8List bytes) async {
|
||
final result = await push<Uint8List>(
|
||
Uri(path: EditorRoutes.imageCrop).toString(),
|
||
extra: ImageCropExtra(bytes: bytes),
|
||
);
|
||
return result;
|
||
}
|
||
|
||
void goToDraftList() {
|
||
push(EditorRoutes.draftList);
|
||
}
|
||
|
||
void goToModel3DPreview(Model3DItem model) {
|
||
push(Uri(path: EditorRoutes.model3dPreview).toString(), extra: model);
|
||
}
|
||
|
||
void goToImageGallery({
|
||
required List<ImageProvider> images,
|
||
int initialIndex = 0,
|
||
}) {
|
||
push(
|
||
Uri(path: EditorRoutes.imageGallery).toString(),
|
||
extra: ImageGalleryExtra(images: images, initialIndex: initialIndex),
|
||
);
|
||
}
|
||
|
||
void replaceWithEditor(Uint8List bytes, {String? initialText}) {
|
||
pushReplacement(
|
||
Uri(
|
||
path: AppRoutes.editor,
|
||
queryParameters: {if (initialText != null) 'text': initialText},
|
||
).toString(),
|
||
extra: EditorReplaceExtra(bytes: bytes),
|
||
);
|
||
}
|
||
}
|
||
|
||
Future<T?> showEditorDialog<T>({
|
||
required BuildContext context,
|
||
required String title,
|
||
required Widget content,
|
||
required List<CupertinoDialogAction> actions,
|
||
}) {
|
||
return showCupertinoDialog<T>(
|
||
context: context,
|
||
builder: (_) => CupertinoAlertDialog(
|
||
title: Text(title),
|
||
content: content,
|
||
actions: actions,
|
||
),
|
||
);
|
||
}
|
||
|
||
Future<String?> showEditorChoice({
|
||
required BuildContext context,
|
||
required String title,
|
||
required String message,
|
||
required List<(String label, String value)> choices,
|
||
String cancelLabel = '取消',
|
||
}) {
|
||
return showCupertinoDialog<String>(
|
||
context: context,
|
||
builder: (_) => CupertinoAlertDialog(
|
||
title: Text(title),
|
||
content: Text(message),
|
||
actions: [
|
||
...choices.map(
|
||
(c) => CupertinoDialogAction(
|
||
child: Text(c.$1),
|
||
onPressed: () => context.pop(c.$2),
|
||
),
|
||
),
|
||
CupertinoDialogAction(
|
||
isDestructiveAction: true,
|
||
child: Text(cancelLabel),
|
||
onPressed: () => context.pop(),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Future<T?> showEditorModalPopup<T>({
|
||
required BuildContext context,
|
||
required WidgetBuilder builder,
|
||
}) {
|
||
return showCupertinoModalPopup<T>(
|
||
context: context,
|
||
builder: (modalContext) => builder(modalContext),
|
||
);
|
||
}
|
||
|
||
class ImagePreviewExtra {
|
||
final ImageProvider? provider;
|
||
final Uint8List? bytes;
|
||
|
||
ImagePreviewExtra({this.provider, this.bytes});
|
||
}
|
||
|
||
class ImageCropExtra {
|
||
final Uint8List bytes;
|
||
|
||
ImageCropExtra({required this.bytes});
|
||
}
|
||
|
||
class ImageGalleryExtra {
|
||
final List<ImageProvider> images;
|
||
final int initialIndex;
|
||
|
||
ImageGalleryExtra({required this.images, required this.initialIndex});
|
||
}
|
||
|
||
class EditorReplaceExtra {
|
||
final Uint8List bytes;
|
||
|
||
EditorReplaceExtra({required this.bytes});
|
||
}
|