feat: 静态页面开发完成

This commit is contained in:
2026-05-21 14:09:46 +08:00
parent 8156e8efbf
commit 763aef6bb9
73 changed files with 6345 additions and 1370 deletions

View File

@@ -54,6 +54,39 @@ class ApiService {
Future<ChatMessage> sendMessage(String content) async {
await Future.delayed(const Duration(milliseconds: 1200));
final tableTrigger = RegExp(r'表格|列表|清单|明细');
if (tableTrigger.hasMatch(content)) {
return ChatMessage(
id: 'msg_${DateTime.now().millisecondsSinceEpoch}',
content: _tableReport(),
type: MessageType.markdown,
sender: MessageSender.ai,
timestamp: DateTime.now(),
);
}
final imageTrigger = RegExp(r'图片|照片|图像|实景|环境照|相册');
if (imageTrigger.hasMatch(content)) {
return ChatMessage(
id: 'msg_${DateTime.now().millisecondsSinceEpoch}',
content: _imageGallery(),
type: MessageType.markdown,
sender: MessageSender.ai,
timestamp: DateTime.now(),
);
}
final chartTrigger = RegExp(r'报表|数据|营收|趋势|占比|图表|统计|分析');
if (chartTrigger.hasMatch(content)) {
return ChatMessage(
id: 'msg_${DateTime.now().millisecondsSinceEpoch}',
content: _chartReport(),
type: MessageType.markdown,
sender: MessageSender.ai,
timestamp: DateTime.now(),
);
}
final responses = [
'收到您的消息,我已记录并会尽快处理。\n\n**当前状态**:已受理\n**预计处理时间**2小时内',
'好的,我来帮您查询一下。\n\n| 项目 | 状态 | 时间 |\n|------|------|------|\n| 订单核销 | 已完成 | 10:30 |\n| 工单处理 | 进行中 | 11:00 |',
@@ -71,6 +104,75 @@ class ApiService {
);
}
String _imageGallery() {
return '''### 智念度假酒店 · 环境实景
**大堂吧** — 全息投影 + 智能引导
![大堂](https://picsum.photos/seed/zhinian-lobby/640/360)
**无边泳池** — 山景一线270° 全景视野
![泳池](https://picsum.photos/seed/zhinian-pool/640/360)
**SPA 养生** — 古法中医结合现代理疗
![SPA](https://picsum.photos/seed/zhinian-spa/640/360)
> 提示:可继续询问"客房实景""餐厅照片"查看更多。''';
}
String _tableReport() {
return '''### 今日订单明细
共 **5 条** 待跟进订单:
| 订单号 | 客户 | 商品 | 金额 | 状态 |
|---|---|---|---|---|
| ZN20240507001 | 王先生 | 风景区成人票 ×2 | ¥128 | 🟡 待核销 |
| ZN20240507002 | 李女士 | 豪华套房 ×1 | ¥888 | ⚪ 待支付 |
| ZN20240507003 | 张 family | 亲子套票 ×3 | ¥388 | 🟢 已核销 |
| ZN20240507004 | 陈先生 | SPA 养生套餐 | ¥598 | 🔴 退款中 |
| ZN20240507005 | 赵女士 | 景区+酒店套餐 | ¥1588 | ⚫ 已退款 |
**汇总**
| 维度 | 数值 |
|---|---|
| 订单总数 | 5 |
| 应收金额 | ¥3,590 |
| 已退款 | ¥1,588 |
| 净收入 | ¥2,002 |
> 提示:点击订单行可跳转至详情,或继续询问"图表"查看可视化分析。''';
}
String _chartReport() {
return '''### 今日数据报表
总订单 **128 笔**,核销率 **75%**,环比上周 **+12.4%**。
```chart
type: bar
title: 订单状态分布(笔)
labels: 已核销,待处理,待支付,退款
data: 96,32,18,2
```
```chart
type: line
title: 近7日订单趋势
labels: 周一,周二,周三,周四,周五,周六,周日
data: 88,92,105,98,120,135,128
```
```chart
type: pie
title: 营收构成占比
labels: 客房,餐饮,SPA,门票
data: 58,22,12,8
```
> 提示:可继续询问"工单趋势""营收对比"等查看更多分析。''';
}
Future<List<EventItem>> getEvents() async {
await Future.delayed(const Duration(milliseconds: 600));
return List.generate(8, (index) {
@@ -121,7 +223,7 @@ class ApiService {
description: '308房间空调制冷效果不佳需要安排维修人员检查。',
status: WorkOrderStatus.pending,
creatorName: '前台小王',
assigneeName: '维修老张',
assigneeName: '维修',
createdAt: DateTime.now().subtract(const Duration(hours: 2)),
priority: 'high',
category: '设备维修',
@@ -134,7 +236,7 @@ class ApiService {
description: '后花园草坪需要定期修剪预计耗时2小时。',
status: WorkOrderStatus.processing,
creatorName: '张老板',
assigneeName: '园丁老李',
assigneeName: '保洁部',
createdAt: DateTime.now().subtract(const Duration(hours: 5)),
priority: 'normal',
category: '环境维护',
@@ -147,7 +249,7 @@ class ApiService {
description: '厨房排风系统需要深度清洁。',
status: WorkOrderStatus.completed,
creatorName: '厨师长',
assigneeName: '清洁团队',
assigneeName: '保洁部',
createdAt: DateTime.now().subtract(const Duration(days: 1)),
completedAt: DateTime.now().subtract(const Duration(hours: 3)),
priority: 'normal',
@@ -173,7 +275,7 @@ class ApiService {
description: '下午有重要会议,需要提前调试投影设备。',
status: WorkOrderStatus.processing,
creatorName: '行政小刘',
assigneeName: 'IT小王',
assigneeName: '行政部',
createdAt: DateTime.now().subtract(const Duration(hours: 1)),
priority: 'urgent',
category: '设备调试',
@@ -192,6 +294,39 @@ class ApiService {
return orders.firstWhere((o) => o.id == id, orElse: () => orders.first);
}
Future<WorkOrder> acceptWorkOrder(String id) async {
await Future.delayed(const Duration(milliseconds: 700));
if (_random.nextInt(20) == 0) {
throw Exception('网络异常,请稍后重试');
}
final order = await getWorkOrderDetail(id);
return order.copyWith(
status: WorkOrderStatus.processing,
assigneeName: order.assigneeName ?? '维修部',
);
}
Future<WorkOrder> transferWorkOrder(String id, String department) async {
await Future.delayed(const Duration(milliseconds: 700));
if (_random.nextInt(20) == 0) {
throw Exception('网络异常,请稍后重试');
}
final order = await getWorkOrderDetail(id);
return order.copyWith(assigneeName: department);
}
Future<WorkOrder> completeWorkOrder(String id) async {
await Future.delayed(const Duration(milliseconds: 700));
if (_random.nextInt(20) == 0) {
throw Exception('网络异常,请稍后重试');
}
final order = await getWorkOrderDetail(id);
return order.copyWith(
status: WorkOrderStatus.completed,
completedAt: DateTime.now(),
);
}
Future<List<OrderItem>> getOrders(OrderStatus status) async {
await Future.delayed(const Duration(milliseconds: 600));
final allOrders = [

View File

@@ -0,0 +1,111 @@
import 'dart:async';
import 'dart:convert';
import 'package:vosk_flutter_service/vosk_flutter.dart';
class VoskVoiceService {
VoskVoiceService._();
static final VoskVoiceService instance = VoskVoiceService._();
static const String _modelAsset = 'assets/models/vosk-model-small-cn-0.22.zip';
static const int _sampleRate = 16000;
final VoskFlutterPlugin _vosk = VoskFlutterPlugin.instance();
Model? _model;
Recognizer? _recognizer;
SpeechService? _speechService;
bool _initializing = false;
bool _listening = false;
final StreamController<String> _partialController = StreamController<String>.broadcast();
final StreamController<String> _resultController = StreamController<String>.broadcast();
final StreamController<String> _errorController = StreamController<String>.broadcast();
StreamSubscription<String>? _partialSub;
StreamSubscription<String>? _resultSub;
Stream<String> get partialStream => _partialController.stream;
Stream<String> get resultStream => _resultController.stream;
Stream<String> get errorStream => _errorController.stream;
bool get isListening => _listening;
bool get isReady => _speechService != null;
Future<bool> ensureReady() async {
if (_speechService != null) return true;
if (_initializing) {
while (_initializing) {
await Future<void>.delayed(const Duration(milliseconds: 80));
}
return _speechService != null;
}
_initializing = true;
try {
final modelPath = await ModelLoader().loadFromAssets(_modelAsset);
_model = await _vosk.createModel(modelPath);
_recognizer = await _vosk.createRecognizer(
model: _model!,
sampleRate: _sampleRate,
);
_speechService = await _vosk.initSpeechService(_recognizer!);
_partialSub = _speechService!.onPartial().listen((raw) {
final text = _extractField(raw, 'partial');
if (text != null) _partialController.add(text);
});
_resultSub = _speechService!.onResult().listen((raw) {
final text = _extractField(raw, 'text');
if (text != null) _resultController.add(text);
});
return true;
} catch (e) {
_errorController.add(e.toString());
return false;
} finally {
_initializing = false;
}
}
Future<bool> start() async {
if (!await ensureReady()) return false;
if (_listening) return true;
final ok = await _speechService!.start(
onRecognitionError: (err) => _errorController.add(err.toString()),
);
_listening = ok ?? false;
return _listening;
}
Future<void> stop() async {
if (!_listening || _speechService == null) return;
await _speechService!.stop();
_listening = false;
}
Future<void> cancel() async {
if (_speechService == null) return;
await _speechService!.cancel();
_listening = false;
}
Future<void> dispose() async {
await _partialSub?.cancel();
await _resultSub?.cancel();
await _speechService?.dispose();
await _recognizer?.dispose();
_speechService = null;
_recognizer = null;
_model = null;
_listening = false;
}
String? _extractField(String raw, String key) {
try {
final map = jsonDecode(raw);
if (map is Map && map[key] is String) {
final v = (map[key] as String).trim();
return v;
}
} catch (_) {}
return null;
}
}