自习室优化ok

This commit is contained in:
zhutao
2025-11-28 18:01:09 +08:00
parent 57305c5804
commit 54bf2dcee7
38 changed files with 527 additions and 117 deletions

View File

@@ -1,8 +1,9 @@
import 'package:app/config/theme/theme.dart';
import 'package:app/global/theme/theme.dart';
import 'package:flutter/material.dart';
import '../config/config.dart';
class Button extends StatelessWidget {
final double? width;
final String text;
@@ -12,14 +13,16 @@ class Button extends StatelessWidget {
final VoidCallback? onPressed;
final bool loading;
final bool disabled;
final Widget? icon;
const Button({
super.key,
this.icon,
this.width,
this.textStyle = const TextStyle(),
this.radius = const BorderRadius.all(Radius.circular(80)),
required this.text,
this.onPressed,
this.onPressed,
this.type = ThemeType.primary,
this.loading = false,
this.disabled = false,

View File

@@ -1,4 +1,4 @@
import 'package:app/config/theme/base/app_theme_ext.dart';
import 'package:app/global/theme/base/app_theme_ext.dart';
import 'package:flutter/material.dart';
class GCard extends StatelessWidget {

View File

@@ -1,9 +1,10 @@
import 'package:app/config/theme/base/app_theme_ext.dart';
import 'package:app/global/theme/base/app_theme_ext.dart';
import 'package:flutter/material.dart';
import '../config/color.dart';
import '../config/config.dart';
class Tag extends StatelessWidget {
final String text;
final Color? color;

View File

@@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:remixicon/remixicon.dart';
import 'package:webview_flutter/webview_flutter.dart';
class BoardDialog extends StatefulWidget {
final WebViewController controller;
const BoardDialog({super.key, required this.controller});
@override
State<BoardDialog> createState() => _BoardDialogState();
}
class _BoardDialogState extends State<BoardDialog> {
bool _loading = true;
@override
void initState() {
super.initState();
// 绑定加载事件
widget.controller.setNavigationDelegate(
NavigationDelegate(
onPageStarted: (_) {
setState(() => _loading = true);
},
onPageFinished: (_) {
setState(() => _loading = false);
},
),
);
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('白板', style: Theme.of(context).textTheme.titleSmall),
IconButton(
onPressed: () => context.pop(),
icon: const Icon(RemixIcons.close_circle_fill),
),
],
),
Expanded(
child: Stack(
children: [
WebViewWidget(
controller: widget.controller,
),
if (_loading)
Container(
color: Colors.white,
child: const Center(
child: CircularProgressIndicator(),
),
),
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,101 @@
import 'package:app/global/config.dart';
import 'package:app/request/api/room_api.dart';
import 'package:app/request/dto/room/board_token_dto.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'board_dialog.dart';
class BoardManager {
BoardManager._();
static final BoardManager _instance = BoardManager._();
factory BoardManager() => _instance;
WebViewController? _webViewController;
BoardTokenDto? _boardToken;
///获取token
Future<void> _fetchToken(int roomId) async {
final now = DateTime.now();
if (_boardToken == null) {
EasyLoading.show(status: "开启白板中");
_boardToken = await getBoardTokenApi(roomId);
} else {
//离过期差多少小时
int remainSeconds = _boardToken!.expiresAt.difference(now).inSeconds;
// 剩余不足 2 小时 = 7200 秒
if (remainSeconds <= 7200) {
EasyLoading.show(status: "开启白板中");
_boardToken = await getBoardTokenApi(roomId);
}
}
EasyLoading.dismiss();
}
///生成完整 URL
/// -[uid] 用户
/// -[roomId] 房间id
/// -[isTeacher] 是否是老师
Future<String> buildUrl({
required String uid,
required int roomId,
required bool isTeacher,
}) async {
await _fetchToken(roomId);
Map<String, dynamic> params = {
"appid": _boardToken!.whiteboardAppid,
"uid": uid,
"uuid": _boardToken!.whiteboardUuid,
"room_token": _boardToken!.whiteboardToken,
"write": isTeacher ? 1 : 0,
};
String paramStr = params.entries.map((e) => "${e.key}=${e.value}").join("&");
return "${Config.webUrl}/board?$paramStr";
}
/// 获取全局唯一 WebViewController
Future<WebViewController> getController(String url) async {
if (_webViewController != null) return _webViewController!;
_webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
// ⭐ 加上加载监听BoardDialog 再 listen 一次也没关系)
NavigationDelegate(
onPageStarted: (_) {},
onPageFinished: (_) {},
),
)
..loadRequest(Uri.parse(url));
return _webViewController!;
}
///打开白板
/// -[uid] 用户名+id
/// -[roomId] 房间id
/// -[isTeacher] 是否是老师
Future<void> showBoardDialog(
BuildContext context, {
required String uid,
required int roomId,
required bool isTeacher,
}) async {
String url = await buildUrl(
uid: uid,
roomId: roomId,
isTeacher: isTeacher,
);
final controller = await getController(url);
showDialog(
context: context,
builder: (_) => BoardDialog(controller: controller),
);
}
}

View File

@@ -1,6 +1,6 @@
import 'dart:io';
import 'package:app/config/theme/base/app_theme_ext.dart';
import 'package:app/global/theme/base/app_theme_ext.dart';
import 'package:app/utils/transfer/upload.dart';
import 'package:app/widgets/base/actionSheet/action_sheet.dart';
import 'package:app/widgets/base/actionSheet/type.dart';
@@ -150,7 +150,7 @@ class _FileDrawerState extends State<FileDrawer> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${widget.name ?? ""}上传文件列表",
"${widget.name ?? ""}文件列表",
style: Theme.of(context).textTheme.titleSmall,
),
Expanded(

View File

@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
/// 举手按钮
class HandRaiseButton extends StatelessWidget {
final void Function() onTap;
const HandRaiseButton({super.key, required this.onTap});
@override
@@ -24,3 +26,41 @@ class HandRaiseButton extends StatelessWidget {
);
}
}
///topBar的操作按钮
class ActionButton extends StatelessWidget {
final IconData icon;
final String text;
final Color? color;
final void Function()? onTap;
const ActionButton({
super.key,
required this.icon,
required this.text,
this.color,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
margin: EdgeInsets.only(right: 15),
decoration: BoxDecoration(
color: color ?? Color(0xff4a4f4f),
borderRadius: BorderRadius.circular(8),
),
child: InkWell(
onTap: onTap,
child: Row(
children: [
Icon(icon, size: 16),
SizedBox(width: 8),
Text(text, style: TextStyle(fontSize: 14)),
],
),
),
);
}
}