|
|
|
import 'package:common_utils/common_utils.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
|
|
import 'package:one_key/pages/home/%20phone_number_extractor.dart';
|
|
|
|
import 'package:one_key/pages/home/chinese_date_utils.dart';
|
|
|
|
import 'package:one_key/pages/home/wave_form_widget.dart';
|
|
|
|
import 'package:one_key/res/colors.dart';
|
|
|
|
import 'package:one_key/utils/dialog_util.dart';
|
|
|
|
import 'package:one_key/utils/misc.dart';
|
|
|
|
import 'package:one_key/utils/utils.dart';
|
|
|
|
import 'package:one_key/widgets/round_button.dart';
|
|
|
|
import 'package:one_key/widgets/v_empty_view.dart';
|
|
...
|
...
|
@@ -22,6 +27,20 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
List<dynamic> resultDate = [];
|
|
|
|
|
|
|
|
List<dynamic> resultPnum = [];
|
|
|
|
|
|
|
|
List<dynamic> resultName = [];
|
|
|
|
|
|
|
|
List<dynamic> resultSex = [];
|
|
|
|
List<dynamic> resultDsolt = [];
|
|
|
|
|
|
|
|
List<dynamic> resultPhone = [];
|
|
|
|
|
|
|
|
List<dynamic> resultDdate = [];
|
|
|
|
|
|
|
|
double volume = 0;
|
|
|
|
|
|
|
|
bool isListening = false; //是否正在识别
|
|
|
|
bool isInit = false; //是否初始化成功
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
// TODO: implement initState
|
|
...
|
...
|
@@ -38,10 +57,35 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
SpeechToTextAlibaba.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
_clearResultData() {
|
|
|
|
result = '';
|
|
|
|
resultDate = [];
|
|
|
|
|
|
|
|
resultPnum = [];
|
|
|
|
|
|
|
|
resultName = [];
|
|
|
|
|
|
|
|
resultSex = [];
|
|
|
|
resultDsolt = [];
|
|
|
|
|
|
|
|
resultPhone = [];
|
|
|
|
|
|
|
|
resultDdate = [];
|
|
|
|
|
|
|
|
volume = 0;
|
|
|
|
|
|
|
|
isListening = false; //是否正在识别
|
|
|
|
// bool isInit = false; //是否初始化成功
|
|
|
|
}
|
|
|
|
|
|
|
|
// Platform messages are asynchronous, so we initialize in an async method.
|
|
|
|
Future<void> initAlibaba() async {
|
|
|
|
await SpeechToTextAlibaba.initialize(
|
|
|
|
appKey: '88v9LR9mYYyvmSKw', token: '0bbc0ead105041239e2b118b47f30cde');
|
|
|
|
appKey: '88v9LR9mYYyvmSKw', token: '928034cf23c8486eaef7138e7d2cdc84');
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
isInit = true;
|
|
|
|
});
|
|
|
|
SpeechToTextAlibaba.setEngineEventHandler(
|
|
|
|
onCallBegin: () {
|
|
|
|
// setState(() {
|
|
...
|
...
|
@@ -60,25 +104,48 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
onError: (error) {
|
|
|
|
print("---------------发生错误-----------------");
|
|
|
|
Utils.showToast(error);
|
|
|
|
isListening = false;
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
// _addMessage("发生错误: $error", false);
|
|
|
|
},
|
|
|
|
onPermission: (isPermission) {
|
|
|
|
print("---------------onPermission-----------------");
|
|
|
|
// 没有权限的话弹窗提示跳转设置页
|
|
|
|
|
|
|
|
if (isPermission == false) {
|
|
|
|
//没有获得权限,跳转至系统权限设置页
|
|
|
|
DialogUtil.openAlertDialog(
|
|
|
|
context, '未获得麦克风权限', "请在手机的权限管理里,将麦克风权限开启", '去设置', 340.0,
|
|
|
|
onTap: () {
|
|
|
|
openSetting();
|
|
|
|
if (Navigator.canPop(context)) {
|
|
|
|
Navigator.pop(context, '');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onStopping: (isStop) {
|
|
|
|
if (isStop == false) {
|
|
|
|
//开始按钮亮起,结束按钮置灰
|
|
|
|
isListening = false;
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
print("---------------onNetworkQuality-----------------");
|
|
|
|
// setState(() {
|
|
|
|
// _networkQuality = quality;
|
|
|
|
// });
|
|
|
|
},
|
|
|
|
onVoiceIdChanged: (voiceId) {
|
|
|
|
print("---------------onVoiceIdChanged-----------------");
|
|
|
|
// _addMessage("AI声音已切换: $voiceId", false);
|
|
|
|
},
|
|
|
|
onRoleChanged: (role) {
|
|
|
|
print("---------------onRoleChanged-----------------");
|
|
|
|
// _addMessage("AI角色已切换: $role", false);
|
|
|
|
onNuiAudioRMSChanged: (double val) {
|
|
|
|
print(val);
|
|
|
|
// volume = val;
|
|
|
|
// setState(() {});
|
|
|
|
/**
|
|
|
|
* 音频能量值回调
|
|
|
|
音频数据能量值回调,范围-160至0,一般用于UI展示语音动效
|
|
|
|
*/
|
|
|
|
//
|
|
|
|
},
|
|
|
|
onEventAsrStarted: (state) {
|
|
|
|
print("---------------nEventAsrStarted----------------- $state");
|
|
...
|
...
|
@@ -133,9 +200,10 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
});
|
|
|
|
_getDate(result);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
await SpeechToTextAlibaba.startDialog();
|
|
|
|
// await SpeechToTextAlibaba.startDialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
Map<String, int> chineseNumberMap = {
|
|
...
|
...
|
@@ -186,10 +254,13 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
|
|
|
|
//筛选日期
|
|
|
|
_getDate(String result) {
|
|
|
|
result = result.replaceAll('幺', '1');
|
|
|
|
resultDate = [];
|
|
|
|
|
|
|
|
// result = '录入预订一个下周一的晚餐,姓名王冬妮,性别女士,电话是幺八幺六六六六六六六六,预订日期是下周一,到店时间是十一点半,十五人';
|
|
|
|
// -****************************** 预订或者变更日期 ***********************************-
|
|
|
|
// 定义正则表达式,匹配“明天”或“春节”
|
|
|
|
RegExp regExp = RegExp(r"今天 | 明天|后天|大后天|元旦|春节|除夕|国庆");
|
|
|
|
// result = '预订一个今天明天后天大后天元旦春节除夕国庆';
|
|
|
|
RegExp regExp = RegExp(r"今天|明天|后天|大后天|元旦|春节|除夕|国庆");
|
|
|
|
|
|
|
|
// 使用RegExp的allMatches方法来找到所有匹配项
|
|
|
|
Iterable<Match> matches = regExp.allMatches(result);
|
|
...
|
...
|
@@ -200,12 +271,14 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
// 打印结果
|
|
|
|
print(dates); // 输出: [明天, 春节]
|
|
|
|
if (dates.contains('春节')) {
|
|
|
|
String str = '';
|
|
|
|
// String str = '';
|
|
|
|
final lunarCalendar = LunarCalendar.from(
|
|
|
|
utcDateTime: DateTime.now().toUtc(),
|
|
|
|
);
|
|
|
|
str = '${lunarCalendar.chineseNewYearString}';
|
|
|
|
resultDate.add(str);
|
|
|
|
// str = '${lunarCalendar.chineseNewYearString}';
|
|
|
|
// resultDate.add(str);
|
|
|
|
resultDate.add(DateUtil.formatDate(lunarCalendar.chineseNewYear,
|
|
|
|
format: DateFormats.y_mo_d));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dates.contains('国庆')) {
|
|
...
|
...
|
@@ -222,21 +295,20 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
}
|
|
|
|
if (dates.contains('元旦')) {
|
|
|
|
// 获取当前年份的元旦日期
|
|
|
|
DateTime nationalDay = DateTime(DateTime.now().year, 1, 1);
|
|
|
|
DateTime newYearDay = DateTime(DateTime.now().year, 1, 1);
|
|
|
|
// 检查元旦是否已经到来
|
|
|
|
if (DateTime.now().isAfter(nationalDay)) {
|
|
|
|
if (DateTime.now().isAfter(newYearDay)) {
|
|
|
|
// '元旦已经过去,用明年的日期
|
|
|
|
nationalDay = DateTime(DateTime.now().year + 1, 1, 1);
|
|
|
|
newYearDay = DateTime(DateTime.now().year + 1, 1, 1);
|
|
|
|
}
|
|
|
|
resultDate
|
|
|
|
.add(DateUtil.formatDate(nationalDay, format: DateFormats.y_mo_d));
|
|
|
|
.add(DateUtil.formatDate(newYearDay, format: DateFormats.y_mo_d));
|
|
|
|
}
|
|
|
|
if (dates.contains('除夕')) {
|
|
|
|
final lunarCalendar = LunarCalendar.from(
|
|
|
|
utcDateTime: DateTime.now().toUtc(),
|
|
|
|
);
|
|
|
|
DateTime chuxi = DateUtil.getDateTime(lunarCalendar.chineseNewYearString)
|
|
|
|
.subtract(Duration(days: 1));
|
|
|
|
DateTime chuxi = lunarCalendar.chineseNewYear.subtract(Duration(days: 1));
|
|
|
|
resultDate.add(DateUtil.formatDate(chuxi, format: DateFormats.y_mo_d));
|
|
|
|
}
|
|
|
|
//今天
|
|
...
|
...
|
@@ -262,18 +334,46 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
//大后天
|
|
|
|
if (dates.contains('大后天')) {
|
|
|
|
// 获取当前日期和时间
|
|
|
|
DateTime houday = DateTime.now().add(Duration(days: 3));
|
|
|
|
resultDate.add(DateUtil.formatDate(houday, format: DateFormats.y_mo_d));
|
|
|
|
DateTime dhouday = DateTime.now().add(Duration(days: 3));
|
|
|
|
resultDate.add(DateUtil.formatDate(dhouday, format: DateFormats.y_mo_d));
|
|
|
|
}
|
|
|
|
|
|
|
|
print(resultDate);
|
|
|
|
|
|
|
|
// 几月几号
|
|
|
|
DateTime? dateHao = _haoDate(result);
|
|
|
|
if (dateHao != null) {
|
|
|
|
resultDate.add(DateUtil.formatDate(dateHao, format: DateFormats.y_mo_d));
|
|
|
|
|
|
|
|
// 方法1
|
|
|
|
// final dates1 = DateExtractor.extractChineseDates(text);
|
|
|
|
// print('提取到的日期:');
|
|
|
|
// for (var date in dates1) {
|
|
|
|
// if (date != null) {
|
|
|
|
// print(DateFormat('yyyy-MM-dd').format(date));
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // 方法3(推荐)
|
|
|
|
// final dates2 = ChineseDateUtils.extractAllDates(text);
|
|
|
|
// print('\n使用工具类提取:');
|
|
|
|
// for (var date in dates2) {
|
|
|
|
// print(ChineseDateUtils.formatDate(date));
|
|
|
|
// }
|
|
|
|
|
|
|
|
// result = '预订一个十一月二十号的预订';
|
|
|
|
// 提取混合文本中的日期
|
|
|
|
final mixedDates = ChineseDateUtils.extractAllDates(result);
|
|
|
|
print(mixedDates);
|
|
|
|
// print('\n混合文本中的日期:');
|
|
|
|
for (var date in mixedDates) {
|
|
|
|
resultDate.add(DateUtil.formatDate(date, format: DateFormats.y_mo_d));
|
|
|
|
// print(ChineseDateUtils.formatDate(date, format: 'MM月dd日'));
|
|
|
|
}
|
|
|
|
|
|
|
|
print(resultDate);
|
|
|
|
|
|
|
|
//2025年3月5日
|
|
|
|
_extractDatesFromText(result);
|
|
|
|
// _extractDatesFromText(result);
|
|
|
|
|
|
|
|
// result = '预订一个周一的预订';
|
|
|
|
|
|
|
|
//周几
|
|
|
|
RegExp regExpWeek = RegExp(r'周一|周二|周三|周四|周五|周六|周日');
|
|
...
|
...
|
@@ -311,24 +411,247 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
}
|
|
|
|
|
|
|
|
//下周几
|
|
|
|
// result = '预订一个下周二的预订';
|
|
|
|
RegExp regExpWeekNext = RegExp(r'下周一|下周二|下周三|下周四|下周五|下周六|下周日|下周天');
|
|
|
|
Iterable<Match> matchesWeekNext = regExpWeekNext.allMatches(result);
|
|
|
|
// 获取当前是周几(0表示周日,1表示周一,依此类推)
|
|
|
|
int weekdayNext = now.weekday;
|
|
|
|
|
|
|
|
//姓名(非必填)
|
|
|
|
//性别(非必填)识别女士、先生、公司
|
|
|
|
//手机号(非必填)识别手机号、座机号
|
|
|
|
//到店时间(必填,没有准确到店时间,有日期也行)
|
|
|
|
//餐段(必填)识别早上|早餐|早饭|上午|中午|午餐|午饭|下午|晚上|晚餐|晚饭
|
|
|
|
for (Match matcht in matchesWeekNext) {
|
|
|
|
int? targetWeekday;
|
|
|
|
String? weekdayText = matcht.group(0); // 获取匹配到的文本
|
|
|
|
if (weekdayText == '下周一') {
|
|
|
|
targetWeekday = 0; // 周三对应的数字是4(周一为1)
|
|
|
|
} else if (weekdayText == '下周二') {
|
|
|
|
targetWeekday = 1; // 周三对应的数字是4(周一为1)
|
|
|
|
} else if (weekdayText == '下周三') {
|
|
|
|
targetWeekday = 2; // 周三对应的数字是4(周一为1)
|
|
|
|
} else if (weekdayText == '下周四') {
|
|
|
|
targetWeekday = 3; // 周三对应的数字是4(周一为1)
|
|
|
|
} else if (weekdayText == '下周五') {
|
|
|
|
targetWeekday = 4; // 周三对应的数字是4(周一为1)
|
|
|
|
} else if (weekdayText == '下周六') {
|
|
|
|
targetWeekday = 5; // 周三对应的数字是4(周一为1)
|
|
|
|
} else if (weekdayText == '下周日' || weekdayText == '下周天') {
|
|
|
|
targetWeekday = 5; // 周三对应的数字是4(周一为1)
|
|
|
|
} else {
|
|
|
|
// 处理其他情况或不做任何操作。
|
|
|
|
}
|
|
|
|
if (targetWeekday != null) {
|
|
|
|
// 计算下周的某一天,例如下周二
|
|
|
|
// 注意:DateTime的星期是从0开始的,0代表星期一,1代表星期二,以此类推
|
|
|
|
DateTime nextWeek =
|
|
|
|
now.add(Duration(days: 8 - now.weekday + targetWeekday));
|
|
|
|
resultDate
|
|
|
|
.add(DateUtil.formatDate(nextWeek, format: DateFormats.y_mo_d));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 合并数组并去重
|
|
|
|
// 时间结果去重 ======== 合并数组并去重
|
|
|
|
Set setArr = new Set(); //用set进行去重
|
|
|
|
setArr.addAll(resultDate); //把数组塞进set里
|
|
|
|
resultDate = setArr.toList();
|
|
|
|
|
|
|
|
//提取人数
|
|
|
|
RegExp regExpPnum = RegExp(r'\b(几[人个位]{1,2,3})\b');
|
|
|
|
Iterable<RegExpMatch> matchesPnum = regExpPnum.allMatches(result);
|
|
|
|
for (RegExpMatch match in matchesPnum) {
|
|
|
|
resultPnum.add(match.group(1));
|
|
|
|
// -****************************** 预订者姓名 ***********************************-
|
|
|
|
|
|
|
|
//姓名(非必填)
|
|
|
|
|
|
|
|
// result = '预订人王冬妮电话18161897899';
|
|
|
|
|
|
|
|
// RegExp nameRegex =
|
|
|
|
// RegExp(r'[\u4e00-\u9fa5]{2,5}(?:[\u4e00-\u9fa5·]{1,5})?');
|
|
|
|
// Iterable<Match> matchesName = nameRegex.allMatches(result);
|
|
|
|
// for (Match matchname in matchesName) {
|
|
|
|
// // print(match[0]); // 输出: 张三 或 李四
|
|
|
|
// resultName.add(matchname[0] ?? '');
|
|
|
|
// }
|
|
|
|
|
|
|
|
// String text = "参加会议的人员有:张三、李四,还有王五。请通知他们。";
|
|
|
|
|
|
|
|
// 匹配2-4个连续的中文字符(覆盖多数单姓和复姓)
|
|
|
|
// RegExp namePattern = RegExp(r'[\u4e00-\u9fa5]{2,4}');
|
|
|
|
|
|
|
|
// RegExp(r'([李王张刘陈杨赵黄周吴徐孙胡朱高林何郭马罗梁宋郑谢韩唐冯董萧程曹袁邓许傅沈曾彭吕苏卢蒋蔡魏贾丁叶阎余潘杜戴钟姜范江孔崔覃史顾侯邵孟龙万段雷钱汤尹黎易常武乔贺龚文欧]'
|
|
|
|
// r'[\u4e00-\u9fa5]{1,2}'
|
|
|
|
// r'(?:·[\u4e00-\u9fa5]{1,3})?)'
|
|
|
|
// )
|
|
|
|
|
|
|
|
// Iterable<Match> matchesName = namePattern.allMatches(result);
|
|
|
|
// List<String> extractedNames = [];
|
|
|
|
|
|
|
|
// for (Match match in matchesName) {
|
|
|
|
// String potentialName = match.group(0)!;
|
|
|
|
// // 简单的过滤:排除明显不是姓名的常用词汇
|
|
|
|
// if (!['人员', '还有', '通知', '他们'].contains(potentialName)) {
|
|
|
|
// extractedNames.add(potentialName);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// print(extractedNames); // 输出:[张三, 李四, 王五]
|
|
|
|
|
|
|
|
// 使用捕获组 () 捕获名字
|
|
|
|
|
|
|
|
// 录入的时候需要说姓名xxx后面需要有,号或者其他非汉字符号间隔
|
|
|
|
// result = "录入一个预订,姓名王冬妮,电话18729999999";
|
|
|
|
|
|
|
|
// 使用括号 () 创建捕获组,提取“姓名是”后面的字符
|
|
|
|
// \u4e00-\u9fa5 是匹配中文字符的范围
|
|
|
|
RegExp reg = RegExp(r"姓名([\u4e00-\u9fa5]{2,4})");
|
|
|
|
Match? match = reg.firstMatch(result);
|
|
|
|
if (match != null) {
|
|
|
|
// group(0) 是完整匹配到的字符串 "姓名是张三"
|
|
|
|
// group(1) 是第一个括号内捕获的内容 "张三"
|
|
|
|
// print(match.group(1)); // 输出: 张三
|
|
|
|
resultName.add(match.group(1));
|
|
|
|
}
|
|
|
|
print(resultName);
|
|
|
|
|
|
|
|
// 合并数组并去重
|
|
|
|
Set setArrName = new Set(); //用set进行去重
|
|
|
|
setArrName.addAll(resultName); //把数组塞进set里
|
|
|
|
resultName = setArrName.toList();
|
|
|
|
|
|
|
|
// -****************************** 预订者性别 ***********************************-
|
|
|
|
|
|
|
|
//性别(非必填)识别女士、先生、公司、贵宾
|
|
|
|
|
|
|
|
RegExp sexRegex = RegExp(r'女士|先生|公司|贵宾');
|
|
|
|
Iterable<Match> matchesSex = sexRegex.allMatches(result);
|
|
|
|
for (Match match in matchesSex) {
|
|
|
|
// print(match[0]); // 输出: 张三 或 李四
|
|
|
|
resultSex.add(match[0] ?? '');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 合并数组并去重
|
|
|
|
Set setArrSex = new Set(); //用set进行去重
|
|
|
|
setArrSex.addAll(resultSex); //把数组塞进set里
|
|
|
|
resultSex = setArrSex.toList();
|
|
|
|
|
|
|
|
// -****************************** 餐段 ***********************************-
|
|
|
|
|
|
|
|
//餐段(必填)识别早上|早餐|早饭|上午|中午|午餐|午饭|下午|晚上|晚餐|晚饭
|
|
|
|
|
|
|
|
RegExp dsoltRegex = RegExp(r'早上|早餐|早饭|上午|中午|午餐|午饭|下午|晚上|晚餐|晚饭');
|
|
|
|
Iterable<Match> matchesDsolt = dsoltRegex.allMatches(result);
|
|
|
|
for (Match match in matchesDsolt) {
|
|
|
|
// print(match[0]); //
|
|
|
|
resultDsolt.add(match[0] ?? '');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 合并数组并去重
|
|
|
|
Set setArrDsolt = new Set(); //用set进行去重
|
|
|
|
setArrDsolt.addAll(resultDsolt); //把数组塞进set里
|
|
|
|
resultDsolt = setArrDsolt.toList();
|
|
|
|
|
|
|
|
// -****************************** 手机或者座机号 ***********************************-
|
|
|
|
|
|
|
|
//手机号(非必填)识别手机号、座机号
|
|
|
|
|
|
|
|
String phoneData = convertChineseNumberToArabic(result);
|
|
|
|
|
|
|
|
// String text = "我的手机号是13812345678,座机号是010-12345678。";
|
|
|
|
|
|
|
|
print(phoneData);
|
|
|
|
|
|
|
|
// final testText = '''
|
|
|
|
// 联系方式:
|
|
|
|
// 手机:13800138000、+8613800138001、008613800138002
|
|
|
|
// 座机:010-12345678、021-87654321、0755-1234567
|
|
|
|
// 带分机:028-87654321-1234
|
|
|
|
// 无区号:12345678
|
|
|
|
// 其他格式:(010)12345678、010 1234 5678
|
|
|
|
// ''';
|
|
|
|
|
|
|
|
// 提取所有电话号码
|
|
|
|
final allNumbers = PhoneNumberExtractor.extractAllPhoneNumbers(phoneData);
|
|
|
|
print('所有电话号码: $allNumbers');
|
|
|
|
resultPhone.addAll(allNumbers);
|
|
|
|
// 创建正则表达式对象
|
|
|
|
// RegExp phoneRegex = RegExp(r'(^1\d{10}$)');
|
|
|
|
// RegExp landlineRegex = RegExp(r'(\d{3,4}-\d{7,8})');
|
|
|
|
|
|
|
|
// // 查找手机号
|
|
|
|
// Iterable<Match> phoneMatches = phoneRegex.allMatches(phoneData);
|
|
|
|
// List<String?> phones = phoneMatches.map((match) => match.group(0)).toList();
|
|
|
|
// print('手机号: $phones');
|
|
|
|
// resultPhone.addAll(phones);
|
|
|
|
|
|
|
|
// 查找座机号
|
|
|
|
// Iterable<Match> landlineMatches = landlineRegex.allMatches(phoneData);
|
|
|
|
// List<String?> landlines =
|
|
|
|
// landlineMatches.map((match) => match.group(0)).toList();
|
|
|
|
// print('座机号: $landlines');
|
|
|
|
// resultPhone.addAll(landlines);
|
|
|
|
|
|
|
|
//电话号码结果去重 ========== 合并数组并去重
|
|
|
|
Set setArrPhone = new Set(); //用set进行去重
|
|
|
|
setArrPhone.addAll(resultPhone); //把数组塞进set里
|
|
|
|
resultPhone = setArrPhone.toList();
|
|
|
|
|
|
|
|
// -****************************** 到店时间 ***********************************-
|
|
|
|
|
|
|
|
// //到店时间(必填,没有准确到店时间,有日期也行)
|
|
|
|
// // 找到 hour 和 minute
|
|
|
|
// //把汉字数字转换成阿拉伯数字
|
|
|
|
// String ddateData = convertChineseNumberToArabic(result);
|
|
|
|
// // 测试字符串
|
|
|
|
// // String testStrings = '会议在9点30分开始, 午饭时间是12点半, 现在是8点, 这不是时间';
|
|
|
|
|
|
|
|
// // 定义正则表达式
|
|
|
|
// final timePattern = RegExp(r'(\d{1,2})点(\d{1,2})分|(\d{1,2})点半|(\d+)点');
|
|
|
|
// List<Match> matchesDdate = timePattern.allMatches(ddateData).toList();
|
|
|
|
// for (Match match in matchesDdate) {
|
|
|
|
// // 检查是哪种匹配模式,并提取捕获组
|
|
|
|
// if (match.group(1) != null && match.group(2) != null) {
|
|
|
|
// // 匹配了 "H点M分" 格式
|
|
|
|
// print('找到时间: ${match.group(1)}点${match.group(2)}分');
|
|
|
|
// int hour = int.parse(match.group(1)!); // 小时部分,注意使用!来断言非null
|
|
|
|
// int minute = int.parse(match.group(2)!); // 小时部分,注意使用!来断言非null
|
|
|
|
|
|
|
|
// print('$hour:$minute');
|
|
|
|
|
|
|
|
// // 上午
|
|
|
|
// resultDdate.add('$hour:$minute');
|
|
|
|
// // 下午
|
|
|
|
// resultDdate.add('${hour + 12}:$minute');
|
|
|
|
// } else if (match.group(3) != null) {
|
|
|
|
// // 匹配了 "H点半" 格式
|
|
|
|
// int hour = int.parse(match[3]!); // 小时部分,注意使用!来断言非null
|
|
|
|
// print('找到时间: ${match.group(3)}点半');
|
|
|
|
// print('$hour:30');
|
|
|
|
// // 上午
|
|
|
|
// resultDdate.add('$hour:30');
|
|
|
|
// // 下午
|
|
|
|
// resultDdate.add('${hour + 12}:30');
|
|
|
|
// } else if (match.group(4) != null) {
|
|
|
|
// // 匹配了 "H点" 格式
|
|
|
|
// print('找到时间: ${match.group(4)}点');
|
|
|
|
// int hour = int.parse(match.group(4)!); // 提取小时数并转换为整数
|
|
|
|
// print('$hour:00');
|
|
|
|
// // 上午
|
|
|
|
// resultDdate.add('$hour:00');
|
|
|
|
// // 下午
|
|
|
|
// resultDdate.add('${hour + 12}:00');
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// -****************************** 到店时间 ***********************************-
|
|
|
|
|
|
|
|
List<String> times = extractTimes(result);
|
|
|
|
resultDdate.addAll(times);
|
|
|
|
|
|
|
|
// 到店时间结果去重 ======== 合并数组并去重
|
|
|
|
Set setArrDdate = new Set(); //用set进行去重
|
|
|
|
setArrDdate.addAll(resultDdate); //把数组塞进set里
|
|
|
|
resultDdate = setArrDdate.toList();
|
|
|
|
|
|
|
|
// -****************************** 人数 ***********************************-
|
|
|
|
|
|
|
|
//提取人数
|
|
|
|
|
|
|
|
List<String> pnum = extractNumbers(result);
|
|
|
|
resultPnum.addAll(pnum);
|
|
|
|
|
|
|
|
// 合并数组并去重
|
|
|
|
Set setArrPnum = new Set(); //用set进行去重
|
|
|
|
setArrPnum.addAll(resultPnum); //把数组塞进set里
|
|
...
|
...
|
@@ -336,15 +659,294 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
|
|
|
|
List<String> extractNames(String text) {
|
|
|
|
List<String> names = [];
|
|
|
|
// 匹配一个或多个汉字
|
|
|
|
RegExp nameRegex = RegExp(r'[\u4e00-\u9fa5]+');
|
|
|
|
Iterable<Match> matches = nameRegex.allMatches(text);
|
|
|
|
|
|
|
|
for (Match match in matches) {
|
|
|
|
String name = match.group(0)!;
|
|
|
|
// 可以在这里过滤,比如只提取 "张三"
|
|
|
|
if (name == '张三') {
|
|
|
|
names.add(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<String> extractNumbers(String text) {
|
|
|
|
List<String> numbers = [];
|
|
|
|
// 匹配中文数字和阿拉伯数字
|
|
|
|
// RegExp regex = RegExp(r'(\d+|[零一二三四五六七八九十百千万亿](个人|位|人)');
|
|
|
|
|
|
|
|
final patterns = [
|
|
|
|
RegExp(r'([0-9零一二三四五六七八九十百千万亿]+)[位人员名]'),
|
|
|
|
RegExp(r'([0-9]+)\s*个人'),
|
|
|
|
RegExp(r'([0-9]+)\s*人'),
|
|
|
|
RegExp(r'([0-9]+)\s*位'),
|
|
|
|
];
|
|
|
|
|
|
|
|
for (var pattern in patterns) {
|
|
|
|
final Iterable<Match> matches = pattern.allMatches(text);
|
|
|
|
for (var match in matches) {
|
|
|
|
String numberStr = match.group(1)!;
|
|
|
|
|
|
|
|
bool isNmuber = RegExp(r'^\d+$').hasMatch(numberStr);
|
|
|
|
String? number;
|
|
|
|
if (isNmuber) {
|
|
|
|
//提取出来的是阿拉伯数字
|
|
|
|
number = numberStr;
|
|
|
|
} else {
|
|
|
|
number = ChineseDateUtils.parseChineseNumber(numberStr).toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (number != null) {
|
|
|
|
numbers.add(number);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return numbers;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<String> extractTimes(String text) {
|
|
|
|
List<String> extractedTimes = [];
|
|
|
|
// final String text = '''
|
|
|
|
// 我今天十一点半要去开会,然后十二点吃午饭,
|
|
|
|
// 下午一点有约会,上午两点还有另一个会议。
|
|
|
|
// 记得三点钟打电话,晚上七点吃饭。
|
|
|
|
// ''';
|
|
|
|
// 正则表达式匹配中文时间格式
|
|
|
|
RegExp timeRegExp = RegExp(
|
|
|
|
r'([上下]午)?([零一二三四五六七八九十两]+)点(半|整|[零一二三四五六七八九十]+分?)?',
|
|
|
|
caseSensitive: false);
|
|
|
|
|
|
|
|
Iterable<Match> matches = timeRegExp.allMatches(text);
|
|
|
|
extractedTimes = matches.map((match) => match.group(0)!).toList();
|
|
|
|
|
|
|
|
// 提取并转换时间
|
|
|
|
List<String> convertedTimes = [];
|
|
|
|
for (var time in extractedTimes) {
|
|
|
|
String? standardTime = convertChineseTime(time);
|
|
|
|
if (standardTime != null) {
|
|
|
|
print(time); //提取出来的汉字时间 十一点半| 下午一点|十二点|上午两点|三点|七点
|
|
|
|
print(standardTime); // 转换后的 07:30 | 11:30
|
|
|
|
convertedTimes.add('$standardTime');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// setState(() {
|
|
|
|
extractedTimes = convertedTimes;
|
|
|
|
return extractedTimes;
|
|
|
|
// });
|
|
|
|
}
|
|
|
|
|
|
|
|
String? convertChineseTime(String chineseTime) {
|
|
|
|
// 移除空格
|
|
|
|
String time = chineseTime.trim();
|
|
|
|
|
|
|
|
// 处理上午/下午
|
|
|
|
bool isAfternoon = time.contains('下午');
|
|
|
|
bool isMorning = time.contains('上午');
|
|
|
|
|
|
|
|
// 移除上午/下午前缀
|
|
|
|
if (isAfternoon || isMorning) {
|
|
|
|
time = time.replaceAll(RegExp(r'[上下]午'), '');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 提取小时和分钟
|
|
|
|
RegExp hourMinuteReg =
|
|
|
|
RegExp(r'([零一二三四五六七八九十两]+)点(半|整|([零一二三四五六七八九十]+)分?)?');
|
|
|
|
Match? match = hourMinuteReg.firstMatch(time);
|
|
|
|
|
|
|
|
if (match == null) return null;
|
|
|
|
|
|
|
|
String hourChinese = match.group(1)!;
|
|
|
|
String minuteChinese = match.group(2) ?? '整';
|
|
|
|
|
|
|
|
// 转换中文数字
|
|
|
|
int hour = chineseToNumberTime(hourChinese);
|
|
|
|
|
|
|
|
// 处理12小时制转24小时制
|
|
|
|
if (isAfternoon && hour < 12) {
|
|
|
|
hour += 12;
|
|
|
|
} else if (isMorning && hour == 12) {
|
|
|
|
hour = 0; // 上午12点就是0点
|
|
|
|
}
|
|
|
|
|
|
|
|
// 转换分钟
|
|
|
|
int minute = 0;
|
|
|
|
if (minuteChinese.contains('半')) {
|
|
|
|
minute = 30;
|
|
|
|
} else if (minuteChinese.contains('分')) {
|
|
|
|
String minuteNum = minuteChinese.replaceAll('分', '');
|
|
|
|
minute = chineseToNumberTime(minuteNum);
|
|
|
|
}
|
|
|
|
|
|
|
|
return '${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}';
|
|
|
|
}
|
|
|
|
|
|
|
|
int chineseToNumberTime(String chinese) {
|
|
|
|
Map<String, int> numberMap = {
|
|
|
|
'零': 0,
|
|
|
|
'一': 1,
|
|
|
|
'二': 2,
|
|
|
|
'三': 3,
|
|
|
|
'四': 4,
|
|
|
|
'五': 5,
|
|
|
|
'六': 6,
|
|
|
|
'七': 7,
|
|
|
|
'八': 8,
|
|
|
|
'九': 9,
|
|
|
|
'十': 10,
|
|
|
|
'十一': 11,
|
|
|
|
'十二': 12,
|
|
|
|
'两': 2,
|
|
|
|
'廿': 20,
|
|
|
|
'卅': 30
|
|
|
|
};
|
|
|
|
|
|
|
|
// 处理复合数字
|
|
|
|
if (chinese.length > 1 && !numberMap.containsKey(chinese)) {
|
|
|
|
if (chinese.startsWith('十')) {
|
|
|
|
return 10 + numberMap[chinese.substring(1)]!;
|
|
|
|
} else if (chinese.endsWith('十')) {
|
|
|
|
return numberMap[chinese.substring(0, 1)]! * 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return numberMap[chinese] ?? int.tryParse(chinese) ?? 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DateTime parseChineseDate(String dateString) {
|
|
|
|
dateString = '十一月二十号';
|
|
|
|
// 定义中文月份到数字月份的映射
|
|
|
|
Map<String, int> monthMap = {
|
|
|
|
"一月": 1,
|
|
|
|
"二月": 2,
|
|
|
|
"三月": 3,
|
|
|
|
"四月": 4,
|
|
|
|
"五月": 5,
|
|
|
|
"六月": 6,
|
|
|
|
"七月": 7,
|
|
|
|
"八月": 8,
|
|
|
|
"九月": 9,
|
|
|
|
"十月": 10,
|
|
|
|
"十一月": 11,
|
|
|
|
"十二月": 12,
|
|
|
|
};
|
|
|
|
|
|
|
|
// 定义中文日到数字日的映射
|
|
|
|
Map<String, int> dayMap = {
|
|
|
|
"初一": 1, "初二": 2, "初三": 3,
|
|
|
|
"初四": 4, "初五": 5, "初六": 6,
|
|
|
|
"初七": 7, "初八": 8, "初九": 9,
|
|
|
|
"初十": 10, "十一": 11, "十二": 12,
|
|
|
|
"十三": 13, "十四": 14, "十五": 15,
|
|
|
|
"十六": 16, "十七": 17, "十八": 18,
|
|
|
|
"十九": 19, "二十": 20, "廿一": 21, // 注意这里的“廿”在某些情况下可能需要根据具体语境调整或省略
|
|
|
|
"廿二": 22, "廿三": 23, "廿四": 24,
|
|
|
|
"廿五": 25, "廿六": 26, "廿七": 27,
|
|
|
|
"廿八": 28, "廿九": 29, "三十": 30,
|
|
|
|
"三十一": 31 // 注意这里的“卅一”在某些情况下可能需要根据具体语境调整或省略
|
|
|
|
};
|
|
|
|
|
|
|
|
// 使用正则表达式提取月份和日期部分
|
|
|
|
RegExp regExp =
|
|
|
|
RegExp(r'([\u4e00-\u9fa5]+)(\d+)号'); // 注意这里的正则表达式可能需要根据你的具体格式调整
|
|
|
|
Iterable<RegExpMatch> matches = regExp.allMatches(dateString);
|
|
|
|
if (matches.isNotEmpty) {
|
|
|
|
RegExpMatch match = matches.first;
|
|
|
|
String? monthStr = match.group(1);
|
|
|
|
String? dayStr = match.group(2);
|
|
|
|
int month = monthMap[monthStr] ?? -1;
|
|
|
|
int day = dayMap[dayStr ?? '' + '号'] ?? -1; // 这里假设日期后面总是跟着“号”字,如果不是,请调整逻辑
|
|
|
|
|
|
|
|
if (month != -1 && day != -1) {
|
|
|
|
// 获取当前年份,你也可以根据需要传入特定的年份
|
|
|
|
int year = DateTime.now().year;
|
|
|
|
return DateTime(year, month, day);
|
|
|
|
} else {
|
|
|
|
throw Exception('无法解析日期:$dateString');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw Exception('无法找到匹配的日期格式:$dateString');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void extractChineseDate(String text) {
|
|
|
|
// 定义月份中文到数字的映射
|
|
|
|
final Map<String, int> monthMap = {
|
|
|
|
'一': 1,
|
|
|
|
'二': 2,
|
|
|
|
'三': 3,
|
|
|
|
'四': 4,
|
|
|
|
'五': 5,
|
|
|
|
'六': 6,
|
|
|
|
'七': 7,
|
|
|
|
'八': 8,
|
|
|
|
'九': 9,
|
|
|
|
'十': 10,
|
|
|
|
'十一': 11,
|
|
|
|
'十二': 12,
|
|
|
|
};
|
|
|
|
|
|
|
|
// 定义一个能匹配中文日期格式的正则
|
|
|
|
// (一..十二)月? (数字)号
|
|
|
|
RegExp dateRegExp = RegExp(r'((一|二|三|四|五|六|七|八|九|十|十一|十二)月)?(\d{1,2})号');
|
|
|
|
|
|
|
|
var matches = dateRegExp.allMatches(text);
|
|
|
|
|
|
|
|
for (var match in matches) {
|
|
|
|
String? monthStr = match.group(2); // 捕获月份名 (如 "十")
|
|
|
|
String? dayStr = match.group(3); // 捕获日期数字 (如 "20")
|
|
|
|
|
|
|
|
int month =
|
|
|
|
monthStr != null ? (monthMap[monthStr] ?? 0) : 0; // 获取月份数字,默认为0
|
|
|
|
int day = int.tryParse(dayStr ?? '0') ?? 0; // 转换日期数字
|
|
|
|
|
|
|
|
// 假设当前年份 (这里可以根据需要修改或从文本中提取)
|
|
|
|
int currentYear = DateTime.now().year;
|
|
|
|
|
|
|
|
if (month > 0 && day > 0) {
|
|
|
|
try {
|
|
|
|
// 使用 intl 包格式化日期
|
|
|
|
DateTime date = DateTime(currentYear, month, day);
|
|
|
|
String formattedDate = DateFormat('yyyy-MM-dd').format(date);
|
|
|
|
print('匹配到日期: ${match.group(0)} -> 格式化日期: $formattedDate');
|
|
|
|
} catch (e) {
|
|
|
|
print('日期转换错误: ${e.toString()}');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//带有“几月几号”日期解析
|
|
|
|
DateTime? _haoDate(String result) {
|
|
|
|
//
|
|
|
|
// RegExp regExp =
|
|
|
|
// RegExp(r'([零一二三四五六七八九十百千万亿]+)月([零一二三四五六七八九十百千万亿]+)号'); // 正则表达式匹配数字和“号”
|
|
|
|
RegExp regExp = RegExp(r'(\d+)月(\d+)号'); // 正则表达式匹配数字和“号”
|
|
|
|
Match? match = regExp.firstMatch(result);
|
|
|
|
if (match != null) {
|
|
|
|
int month = int.parse(match.group(1)!); // 获取月份并转换为整数
|
|
|
|
int day = int.parse(match.group(2)!); // 获取日期并转换为整数
|
|
|
|
|
|
|
|
//如果月份小于此月,则用year + 1,否则用year
|
|
|
|
|
|
|
|
DateTime now = DateTime.now();
|
|
|
|
int monthNow = now.month;
|
|
|
|
int dayNow = now.day;
|
|
|
|
int year = now.year;
|
|
|
|
if (month < monthNow) {
|
|
|
|
year = now.year + 1;
|
|
|
|
} else if (month == monthNow) {
|
|
|
|
if (day < dayNow) {
|
|
|
|
year = now.year + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DateTime date =
|
|
|
|
DateTime(2000, month, day); // 假设年份为2000,根据需要可以调整年份或使用完整日期信息。
|
|
|
|
DateTime(year, month, day); // 假设年份为2000,根据需要可以调整年份或使用完整日期信息。
|
|
|
|
return date;
|
|
|
|
// print("解析后的日期: ${date.toString()}"); // 输出解析后的日期
|
|
|
|
} else {
|
|
...
|
...
|
@@ -353,6 +955,243 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//把字符串中的汉字数字转换成阿拉伯数字 月份
|
|
|
|
String convertChineseNumberToArabicMonth(String chineseNumber) {
|
|
|
|
Map<String, String> chineseToArabic = {
|
|
|
|
'零': '0',
|
|
|
|
'一': '1',
|
|
|
|
'幺': '1',
|
|
|
|
'二': '2',
|
|
|
|
'三': '3',
|
|
|
|
'四': '4',
|
|
|
|
'五': '5',
|
|
|
|
'六': '6',
|
|
|
|
'七': '7',
|
|
|
|
'八': '8',
|
|
|
|
'九': '9',
|
|
|
|
'十': '10',
|
|
|
|
'十一': '11',
|
|
|
|
'十二': '12',
|
|
|
|
'十三': '13',
|
|
|
|
'十四': '14',
|
|
|
|
'十五': '15',
|
|
|
|
'十六': '16',
|
|
|
|
'十七': '17',
|
|
|
|
'十八': '18',
|
|
|
|
'十九': '19',
|
|
|
|
'二十': '20',
|
|
|
|
'二十一': '21',
|
|
|
|
'二十二': '22',
|
|
|
|
'二十三': '23',
|
|
|
|
'二十四': '24',
|
|
|
|
'二十五': '25',
|
|
|
|
'二十六': '26',
|
|
|
|
'二十七': '27',
|
|
|
|
'二十八': '28',
|
|
|
|
'二十九': '29',
|
|
|
|
'三十': '30',
|
|
|
|
'三十一': '31',
|
|
|
|
};
|
|
|
|
String result = '';
|
|
|
|
for (int i = 0; i < chineseNumber.length; i++) {
|
|
|
|
String char = chineseNumber[i];
|
|
|
|
if (chineseToArabic.containsKey(char)) {
|
|
|
|
result += chineseToArabic[char]!;
|
|
|
|
} else {
|
|
|
|
result += char; // 如果不是汉字数字,直接添加到结果中
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int chineseNumberToArabic(String text) {
|
|
|
|
/// 汉字数字到阿拉伯数字映射
|
|
|
|
const Map<String, int> chineseToDigit = {
|
|
|
|
'零': 0,
|
|
|
|
'一': 1,
|
|
|
|
'二': 2,
|
|
|
|
'三': 3,
|
|
|
|
'四': 4,
|
|
|
|
'五': 5,
|
|
|
|
'六': 6,
|
|
|
|
'七': 7,
|
|
|
|
'八': 8,
|
|
|
|
'九': 9,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// 单位映射
|
|
|
|
const Map<String, int> unitMap = {
|
|
|
|
'十': 10,
|
|
|
|
'百': 100,
|
|
|
|
'千': 1000,
|
|
|
|
'万': 10000,
|
|
|
|
'亿': 100000000,
|
|
|
|
};
|
|
|
|
int result = 0;
|
|
|
|
int currentNum = 0; // 当前数字(如“三”中的 3)
|
|
|
|
int currentUnitValue = 1; // 当前单位值 (如“十”是 10)
|
|
|
|
|
|
|
|
// 遍历字符串,优先处理“万”和“亿”
|
|
|
|
for (int i = 0; i < text.length; i++) {
|
|
|
|
String char = text[i];
|
|
|
|
|
|
|
|
if (chineseToDigit.containsKey(char)) {
|
|
|
|
currentNum = chineseToDigit[char]!;
|
|
|
|
// 如果是“一十”,则直接变成 10,而不是 1*10
|
|
|
|
if (currentNum == 1 &&
|
|
|
|
(i > 0 &&
|
|
|
|
(text[i - 1] == '十' ||
|
|
|
|
text[i - 1] == '百' ||
|
|
|
|
text[i - 1] == '千'))) {
|
|
|
|
currentNum = 0; // 避免“一十”变成 10
|
|
|
|
}
|
|
|
|
} else if (unitMap.containsKey(char)) {
|
|
|
|
int unit = unitMap[char]!;
|
|
|
|
if (unit >= 10000) {
|
|
|
|
// 处理万、亿,先结算前面部分
|
|
|
|
result += currentNum * currentUnitValue;
|
|
|
|
currentNum = 0; // 重置当前数字
|
|
|
|
currentUnitValue = 1; // 重置单位
|
|
|
|
result *= unit; // 乘以万或亿
|
|
|
|
} else {
|
|
|
|
result += currentNum * currentUnitValue;
|
|
|
|
currentNum = 0; // 重置当前数字
|
|
|
|
currentUnitValue = unit; // 更新单位
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 遇到非数字/单位字符,先结算之前的
|
|
|
|
result += currentNum * currentUnitValue;
|
|
|
|
currentNum = 0;
|
|
|
|
currentUnitValue = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 结算最后一部分
|
|
|
|
result += currentNum * currentUnitValue;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
String convertChineseToArabic(String text) {
|
|
|
|
// 中文数字到阿拉伯数字的映射
|
|
|
|
Map<String, int> chineseNumbers = {
|
|
|
|
'零': 0,
|
|
|
|
'一': 1,
|
|
|
|
'二': 2,
|
|
|
|
'三': 3,
|
|
|
|
'四': 4,
|
|
|
|
'五': 5,
|
|
|
|
'六': 6,
|
|
|
|
'七': 7,
|
|
|
|
'八': 8,
|
|
|
|
'九': 9,
|
|
|
|
'十': 10,
|
|
|
|
'百': 100,
|
|
|
|
'千': 1000,
|
|
|
|
'万': 10000,
|
|
|
|
'亿': 100000000
|
|
|
|
};
|
|
|
|
|
|
|
|
// 处理多位数字,例如“二十二”
|
|
|
|
RegExp regExp = RegExp(r'([零一二三四五六七八九]{1,4})(百|千|万|亿)?');
|
|
|
|
List<String?> matches =
|
|
|
|
regExp.allMatches(text).map((m) => m.group(0)).toList();
|
|
|
|
|
|
|
|
int convertChineseNum(String numStr) {
|
|
|
|
int result = 0;
|
|
|
|
int unit = 1; // 个位
|
|
|
|
for (int i = numStr.length - 1; i >= 0; i--) {
|
|
|
|
String char = numStr[i];
|
|
|
|
if (char == '十') {
|
|
|
|
unit = 10;
|
|
|
|
} else if (char == '百') {
|
|
|
|
unit = 100;
|
|
|
|
} else if (char == '千') {
|
|
|
|
unit = 1000;
|
|
|
|
} else if (char == '万') {
|
|
|
|
unit = 10000;
|
|
|
|
} else if (char == '亿') {
|
|
|
|
unit = 100000000;
|
|
|
|
} else {
|
|
|
|
result += chineseNumbers[char]! * unit;
|
|
|
|
unit = 1; // 重置单位为个位
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
String result = text;
|
|
|
|
for (String? match in matches.reversed) {
|
|
|
|
// 从后往前替换,避免索引错位
|
|
|
|
if (match != null) {
|
|
|
|
result = result.replaceAll(match, convertChineseNum(match).toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
String chineseToNumber(String chinese) {
|
|
|
|
Map<String, int> map = {
|
|
|
|
"零": 0,
|
|
|
|
"一": 1,
|
|
|
|
"二": 2,
|
|
|
|
"三": 3,
|
|
|
|
"四": 4,
|
|
|
|
"五": 5,
|
|
|
|
"六": 6,
|
|
|
|
"七": 7,
|
|
|
|
"八": 8,
|
|
|
|
"九": 9,
|
|
|
|
"十": 10,
|
|
|
|
"百": 100,
|
|
|
|
"千": 1000,
|
|
|
|
"万": 10000,
|
|
|
|
"亿": 100000000
|
|
|
|
};
|
|
|
|
List<String> parts = chinese.split(RegExp(r'(\d+)')); // 分割数字和单位
|
|
|
|
int result = 0;
|
|
|
|
int temp = 1; // 临时变量,用于记录当前位的权值
|
|
|
|
for (String part in parts) {
|
|
|
|
if (map.containsKey(part)) {
|
|
|
|
if (map[part]! >= 10) {
|
|
|
|
// 是单位
|
|
|
|
temp = map[part]!;
|
|
|
|
} else {
|
|
|
|
// 是数字
|
|
|
|
result += temp * map[part]!;
|
|
|
|
temp = 1; // 重置temp为1,准备下一个数字的乘法
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
//把字符串中的汉字数字转换成阿拉伯数字
|
|
|
|
String convertChineseNumberToArabic(String chineseNumber) {
|
|
|
|
Map<String, String> chineseToArabic = {
|
|
|
|
'零': '0',
|
|
|
|
'一': '1',
|
|
|
|
'幺': '1',
|
|
|
|
'二': '2',
|
|
|
|
'三': '3',
|
|
|
|
'四': '4',
|
|
|
|
'五': '5',
|
|
|
|
'六': '6',
|
|
|
|
'七': '7',
|
|
|
|
'八': '8',
|
|
|
|
'九': '9'
|
|
|
|
};
|
|
|
|
|
|
|
|
String result = '';
|
|
|
|
for (int i = 0; i < chineseNumber.length; i++) {
|
|
|
|
String char = chineseNumber[i];
|
|
|
|
if (chineseToArabic.containsKey(char)) {
|
|
|
|
result += chineseToArabic[char]!;
|
|
|
|
} else {
|
|
|
|
result += char; // 如果不是汉字数字,直接添加到结果中
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
_extractDatesFromText(String text) {
|
|
|
|
// 定义一个正则表达式来匹配日期
|
|
|
|
// 示例:匹配形如 "2023年3月15日" 或 "15/03/2023" 的日期
|
|
...
|
...
|
@@ -399,6 +1238,7 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
return Center(
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Container(
|
|
|
|
height: ScreenUtil().setWidth(48),
|
|
...
|
...
|
@@ -407,57 +1247,162 @@ class SpeechPageState extends State<SpeechPage> { |
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_ffffff)),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
// VEmptyView(20),
|
|
|
|
// WaveformWidget(volume: volume), // 在录音时传入音量值
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.center,
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text(result,
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_ffffff)),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.center,
|
|
|
|
child: Text('日期:${resultDate.join('、')}',
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text('预订日期:${resultDate.join('、')}',
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_ffffff)),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.center,
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text('姓名:${resultName.join('、')}',
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text('性别:${resultSex.join('、')}',
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text('电话:${resultPhone.join('、')}',
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text('人数:${resultPnum.join('、')}',
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_ffffff)),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text('餐段:${resultDsolt.join('、')}',
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(40),
|
|
|
|
RoundButton(
|
|
|
|
text: '结束识别',
|
|
|
|
width:
|
|
|
|
(ScreenUtil().screenWidth - ScreenUtil().setWidth(62) * 2) /
|
|
|
|
Container(
|
|
|
|
// height: ScreenUtil().setWidth(48),
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
child: Text('到店时间:${resultDdate.join('、')}',
|
|
|
|
style: TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
fontSize: ScreenUtil().setSp(32),
|
|
|
|
color: MyColours.color_515151)),
|
|
|
|
),
|
|
|
|
VEmptyView(50),
|
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
|
children: [
|
|
|
|
RoundButton(
|
|
|
|
text: '开始识别',
|
|
|
|
width: (ScreenUtil().screenWidth -
|
|
|
|
ScreenUtil().setWidth(82) * 2) /
|
|
|
|
2,
|
|
|
|
height: ScreenUtil().setWidth(95),
|
|
|
|
radius: 30,
|
|
|
|
gradientStart: isInit && !isListening
|
|
|
|
? MyColours.color_438E93
|
|
|
|
: MyColours.color_999999,
|
|
|
|
gradientEnd: isInit && !isListening
|
|
|
|
? MyColours.color_438E93
|
|
|
|
: MyColours.color_999999,
|
|
|
|
textColor: MyColours.color_ffffff,
|
|
|
|
isBold: false,
|
|
|
|
fontSize: 30,
|
|
|
|
onPressed: () async {
|
|
|
|
_clearResultData();
|
|
|
|
setState(() {
|
|
|
|
isListening = true;
|
|
|
|
});
|
|
|
|
await SpeechToTextAlibaba.startDialog();
|
|
|
|
// _getDate('5点');
|
|
|
|
}),
|
|
|
|
RoundButton(
|
|
|
|
text: '结束识别',
|
|
|
|
width: (ScreenUtil().screenWidth -
|
|
|
|
ScreenUtil().setWidth(82) * 2) /
|
|
|
|
2,
|
|
|
|
height: ScreenUtil().setWidth(112),
|
|
|
|
// radius: ScreenUtil().setWidth(10),
|
|
|
|
gradientStart: Colors.transparent,
|
|
|
|
gradientEnd: Colors.transparent,
|
|
|
|
textColor: MyColours.color_ffffff,
|
|
|
|
isBold: true,
|
|
|
|
fontSize: 34,
|
|
|
|
onPressed: () async {
|
|
|
|
await SpeechToTextAlibaba.stopDialog();
|
|
|
|
// if (Navigator.canPop(context)) {
|
|
|
|
// Navigator.pop(context);
|
|
|
|
// }
|
|
|
|
}),
|
|
|
|
height: ScreenUtil().setWidth(95),
|
|
|
|
radius: 30,
|
|
|
|
gradientStart: isListening
|
|
|
|
? MyColours.color_438E93
|
|
|
|
: MyColours.color_999999,
|
|
|
|
gradientEnd: isListening
|
|
|
|
? MyColours.color_438E93
|
|
|
|
: MyColours.color_999999,
|
|
|
|
textColor: MyColours.color_ffffff,
|
|
|
|
isBold: false,
|
|
|
|
fontSize: 30,
|
|
|
|
onPressed: () async {
|
|
|
|
setState(() {
|
|
|
|
isListening = false; //主动结束识别
|
|
|
|
});
|
|
|
|
await SpeechToTextAlibaba.stopDialog();
|
|
|
|
// _getDate('5点');
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
// Align(
|
|
|
|
// alignment: Alignment.center,
|
|
|
|
// child: RoundButton(
|
|
|
|
// text: '结束识别',
|
|
|
|
// width:
|
|
|
|
// (ScreenUtil().screenWidth - ScreenUtil().setWidth(62) * 2) /
|
|
|
|
// 2,
|
|
|
|
// height: ScreenUtil().setWidth(95),
|
|
|
|
// radius: 30,
|
|
|
|
// gradientStart: MyColours.color_438E93,
|
|
|
|
// gradientEnd: MyColours.color_438E93,
|
|
|
|
// textColor: MyColours.color_ffffff,
|
|
|
|
// isBold: false,
|
|
|
|
// fontSize: 30,
|
|
|
|
// onPressed: () async {
|
|
|
|
// await SpeechToTextAlibaba.stopDialog();
|
|
|
|
// // _getDate('5点');
|
|
|
|
// }),
|
|
|
|
// ),
|
|
|
|
// VEmptyView(300),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
));
|
...
|
...
|
|