12 changed files with 401 additions and 76 deletions
@ -0,0 +1,49 @@ |
|||||
|
import 'dart:async'; |
||||
|
import 'package:path/path.dart'; |
||||
|
import 'package:sqflite/sqflite.dart'; |
||||
|
|
||||
|
class DatabaseHelper { |
||||
|
static final DatabaseHelper _instance = DatabaseHelper._internal(); |
||||
|
factory DatabaseHelper() => _instance; |
||||
|
static Database? _database; |
||||
|
|
||||
|
DatabaseHelper._internal(); |
||||
|
|
||||
|
Future<Database> get database async { |
||||
|
if (_database != null) return _database!; |
||||
|
_database = await _initDatabase(); |
||||
|
return _database!; |
||||
|
} |
||||
|
|
||||
|
Future<Database> _initDatabase() async { |
||||
|
String path = join( |
||||
|
await getDatabasesPath(), |
||||
|
'database.db', |
||||
|
); |
||||
|
return await openDatabase(path, version: 1, onCreate: _createDB); |
||||
|
} |
||||
|
|
||||
|
Future<void> _createDB(Database db, int version) async { |
||||
|
final batch = db.batch(); |
||||
|
// 商务会议助手列表 |
||||
|
batch.execute(''' |
||||
|
CREATE TABLE IF NOT EXISTS meeting ( |
||||
|
id INTEGER PRIMARY KEY, |
||||
|
title TEXT, |
||||
|
content TEXT, |
||||
|
seconds INTEGER, |
||||
|
creationtime INTEGER |
||||
|
) |
||||
|
'''); |
||||
|
// 商务会议助手详情内容 |
||||
|
batch.execute(''' |
||||
|
CREATE TABLE IF NOT EXISTS meetingdetails ( |
||||
|
id INTEGER PRIMARY KEY, |
||||
|
meetingid INTEGER, |
||||
|
audio BLOB, |
||||
|
content TEXT |
||||
|
) |
||||
|
'''); |
||||
|
await batch.commit(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
|
import 'database_helper.dart'; |
||||
|
|
||||
|
class SqfliteApi { |
||||
|
// 获取商务会议助手列表 |
||||
|
static Future getMeetingList(int page) async { |
||||
|
final db = await DatabaseHelper().database; |
||||
|
return await db.query( |
||||
|
'meeting', |
||||
|
limit: 10, |
||||
|
offset: (page - 1) * 10, |
||||
|
orderBy: 'creationtime DESC', |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// 获取商务会议助手详情 |
||||
|
static Future getMeetingDetails(id) async { |
||||
|
final db = await DatabaseHelper().database; |
||||
|
List meetingList = |
||||
|
await db.query('meeting', where: 'id = ?', whereArgs: [id]); |
||||
|
List detailsList = await db.query( |
||||
|
'meetingdetails', |
||||
|
where: 'meetingid = ?', |
||||
|
whereArgs: [id], |
||||
|
); |
||||
|
return <String, dynamic>{ |
||||
|
...meetingList.first, |
||||
|
'details': detailsList.first |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// 插入商务会议助手 |
||||
|
static Future insertMeeting(Map<String, Object?> meeting) async { |
||||
|
final db = await DatabaseHelper().database; |
||||
|
try { |
||||
|
int id = 0; |
||||
|
await db.transaction((txn) async { |
||||
|
id = await txn.insert('meeting', { |
||||
|
'title': meeting['title'], |
||||
|
'content': meeting['firstContent'], |
||||
|
'seconds': meeting['seconds'], |
||||
|
'creationtime': meeting['creationTime'], |
||||
|
}); |
||||
|
await txn.insert('meetingdetails', { |
||||
|
'meetingid': id, |
||||
|
'audio': meeting['audio'], |
||||
|
'content': meeting['content'], |
||||
|
}); |
||||
|
}); |
||||
|
return id; |
||||
|
} catch (e) { |
||||
|
Get.snackbar( |
||||
|
'error'.tr, // 错误 |
||||
|
'unknownError'.tr, |
||||
|
snackPosition: SnackPosition.TOP, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 修改商务会议助手 |
||||
|
static Future editMeeting(id, Map<String, Object?> meeting) async { |
||||
|
final db = await DatabaseHelper().database; |
||||
|
try { |
||||
|
await db.transaction((txn) async { |
||||
|
await txn.update( |
||||
|
'meeting', |
||||
|
{ |
||||
|
'title': meeting['title'], |
||||
|
'content': meeting['firstContent'], |
||||
|
'seconds': meeting['seconds'], |
||||
|
}, |
||||
|
where: 'id = ?', |
||||
|
whereArgs: [id], |
||||
|
); |
||||
|
await txn.update( |
||||
|
'meetingdetails', |
||||
|
{'content': meeting['content']}, |
||||
|
where: 'meetingid = ?', |
||||
|
whereArgs: [id], |
||||
|
); |
||||
|
}); |
||||
|
return true; |
||||
|
} catch (e) { |
||||
|
Get.snackbar( |
||||
|
'error'.tr, // 错误 |
||||
|
'unknownError'.tr, |
||||
|
snackPosition: SnackPosition.TOP, |
||||
|
); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 修改商务会议助手标题 |
||||
|
static Future editMeetingTitle(id, title) async { |
||||
|
final db = await DatabaseHelper().database; |
||||
|
await db.update('meeting', {'title': title}, |
||||
|
where: 'id = ?', whereArgs: [id]); |
||||
|
} |
||||
|
|
||||
|
// 删除商务会议助手 |
||||
|
static Future deleteMeeting(id) async { |
||||
|
final db = await DatabaseHelper().database; |
||||
|
await db.transaction((txn) async { |
||||
|
await txn.delete('meeting', where: 'id = ?', whereArgs: [id]); |
||||
|
await txn |
||||
|
.delete('meetingdetails', where: 'meetingid = ?', whereArgs: [id]); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
import 'package:get/get.dart'; |
||||
|
import '../controllers/timbre_controller.dart'; |
||||
|
|
||||
|
class TimbreBinding extends Bindings { |
||||
|
@override |
||||
|
void dependencies() { |
||||
|
Get.put<TimbreController>(TimbreController()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
|
class TimbreController extends GetxController { |
||||
|
RxList dataList = [ |
||||
|
{'type': '1', 'name': '默认音色', 'description': '微软云端音色服务'}, |
||||
|
{'type': '2', 'name': '声音复刻', 'description': '复刻本人音色服务'} |
||||
|
].obs; |
||||
|
RxString type = '1'.obs; |
||||
|
|
||||
|
@override |
||||
|
void onInit() { |
||||
|
super.onInit(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,168 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import '../../../core/theme/app_colors.dart'; |
||||
|
import '../controllers/timbre_controller.dart'; |
||||
|
|
||||
|
class TimbreView extends GetView<TimbreController> { |
||||
|
const TimbreView({Key? key}) : super(key: key); |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
final isDarkMode = Theme.of(context).brightness == Brightness.dark; |
||||
|
|
||||
|
return Scaffold( |
||||
|
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], |
||||
|
appBar: _buildAppBar(isDarkMode), |
||||
|
body: _buildBody(isDarkMode), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// 构建应用栏 |
||||
|
PreferredSizeWidget _buildAppBar(bool isDarkMode) { |
||||
|
return AppBar( |
||||
|
backgroundColor: isDarkMode ? Colors.black : Colors.white, |
||||
|
elevation: 0.5, |
||||
|
leading: IconButton( |
||||
|
icon: Icon( |
||||
|
Icons.arrow_back_ios, |
||||
|
color: isDarkMode ? Colors.white : Colors.black, |
||||
|
size: 20.sp, |
||||
|
), |
||||
|
onPressed: () => Get.back(), |
||||
|
), |
||||
|
title: Text( |
||||
|
'选择音色', |
||||
|
style: TextStyle( |
||||
|
fontSize: 17.sp, |
||||
|
fontWeight: FontWeight.w600, |
||||
|
color: isDarkMode ? Colors.white : Colors.black, |
||||
|
), |
||||
|
), |
||||
|
centerTitle: true, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// 构建主体内容 |
||||
|
Widget _buildBody(bool isDarkMode) { |
||||
|
return Column( |
||||
|
children: [ |
||||
|
Expanded( |
||||
|
child: Obx( |
||||
|
() => ListView.builder( |
||||
|
itemCount: controller.dataList.length, |
||||
|
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w), |
||||
|
itemBuilder: (BuildContext context, int index) { |
||||
|
Map data = controller.dataList[index]; |
||||
|
return _item(isDarkMode, data); |
||||
|
}, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
GestureDetector( |
||||
|
onTap: () {}, |
||||
|
child: Container( |
||||
|
width: 130.w, |
||||
|
height: 48.w, |
||||
|
margin: EdgeInsets.symmetric(vertical: 20.w), |
||||
|
decoration: BoxDecoration( |
||||
|
color: isDarkMode ? AppColors.primary : Colors.blue, |
||||
|
borderRadius: BorderRadius.circular(12.w), |
||||
|
), |
||||
|
child: Row( |
||||
|
mainAxisAlignment: MainAxisAlignment.center, |
||||
|
children: [ |
||||
|
const Icon(Icons.keyboard_voice, color: Colors.white), |
||||
|
5.horizontalSpace, |
||||
|
Text( |
||||
|
'声音复刻', |
||||
|
style: TextStyle(color: Colors.white, fontSize: 16.sp), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Widget _item(bool isDarkMode, Map data) { |
||||
|
return InkWell( |
||||
|
onTap: () { |
||||
|
controller.type.value = data['type']; |
||||
|
}, |
||||
|
child: Obx( |
||||
|
() => Container( |
||||
|
margin: EdgeInsets.symmetric(vertical: 6.h), |
||||
|
padding: EdgeInsets.all(12.w), |
||||
|
decoration: BoxDecoration( |
||||
|
color: controller.type.value == data['type'] |
||||
|
? (isDarkMode |
||||
|
? Colors.teal[900]!.withOpacity(0.3) |
||||
|
: Colors.teal[50]!) |
||||
|
: (isDarkMode |
||||
|
? Colors.white.withOpacity(0.05) |
||||
|
: Colors.grey[100]!), |
||||
|
borderRadius: BorderRadius.circular(12.r), |
||||
|
border: Border.all( |
||||
|
color: controller.type.value == data['type'] |
||||
|
? (isDarkMode ? Colors.teal[300]! : Colors.teal[600]!) |
||||
|
: Colors.transparent, |
||||
|
width: 1, |
||||
|
), |
||||
|
), |
||||
|
child: Row( |
||||
|
children: [ |
||||
|
Expanded( |
||||
|
child: Column( |
||||
|
crossAxisAlignment: CrossAxisAlignment.start, |
||||
|
children: [ |
||||
|
Text( |
||||
|
data['name'], |
||||
|
style: TextStyle( |
||||
|
fontSize: 16.sp, |
||||
|
fontWeight: FontWeight.w500, |
||||
|
color: isDarkMode ? Colors.white : Colors.black87, |
||||
|
), |
||||
|
overflow: TextOverflow.ellipsis, |
||||
|
), |
||||
|
SizedBox(height: 4.h), |
||||
|
Text( |
||||
|
data['description'], |
||||
|
style: TextStyle( |
||||
|
fontSize: 12.sp, |
||||
|
color: isDarkMode ? Colors.white70 : Colors.black54, |
||||
|
), |
||||
|
overflow: TextOverflow.ellipsis, |
||||
|
maxLines: 2, |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
Container( |
||||
|
width: 24.w, |
||||
|
height: 24.w, |
||||
|
margin: EdgeInsets.only(left: 12.w), |
||||
|
decoration: BoxDecoration( |
||||
|
color: controller.type.value == data['type'] |
||||
|
? (isDarkMode ? Colors.teal[300] : Colors.teal[600]) |
||||
|
: (isDarkMode |
||||
|
? Colors.white.withOpacity(0.1) |
||||
|
: Colors.grey[300]), |
||||
|
shape: BoxShape.circle, |
||||
|
), |
||||
|
child: controller.type.value == data['type'] |
||||
|
? Icon( |
||||
|
Icons.check, |
||||
|
size: 16.w, |
||||
|
color: Colors.white, |
||||
|
) |
||||
|
: null, |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue