Browse Source

[feat]添加定制人设历史页,增加提示弹窗

云天逵 8 months ago
parent
commit
741b0e18f7
42 changed files with 1936 additions and 281 deletions
  1. BIN
      assets/images/bg_character_custom_list.webp
  2. BIN
      assets/images/icon_character_custom_delete.webp
  3. 19 1
      assets/string/base/string.xml
  4. 5 3
      lib/base/base_response.dart
  5. 2 2
      lib/base/base_response.g.dart
  6. 32 2
      lib/data/api/atmob_api.dart
  7. 143 0
      lib/data/api/atmob_api.g.dart
  8. 23 0
      lib/data/api/request/character_custom_add_request.dart
  9. 74 0
      lib/data/api/request/character_custom_add_request.g.dart
  10. 17 0
      lib/data/api/request/character_custom_delete_request.dart
  11. 70 0
      lib/data/api/request/character_custom_delete_request.g.dart
  12. 26 0
      lib/data/api/request/character_custom_page_request.dart
  13. 76 0
      lib/data/api/request/character_custom_page_request.g.dart
  14. 42 0
      lib/data/api/request/character_custom_update_request.dart
  15. 90 0
      lib/data/api/request/character_custom_update_request.g.dart
  16. 16 0
      lib/data/api/response/character_custom_add_response.dart
  17. 19 0
      lib/data/api/response/character_custom_add_response.g.dart
  18. 2 2
      lib/data/api/response/character_custom_config_response.dart
  19. 2 2
      lib/data/api/response/character_custom_config_response.g.dart
  20. 18 0
      lib/data/api/response/character_custom_page_response.dart
  21. 24 0
      lib/data/api/response/character_custom_page_response.g.dart
  22. 18 0
      lib/data/api/response/character_custom_update_response.dart
  23. 19 0
      lib/data/api/response/character_custom_update_response.g.dart
  24. 97 12
      lib/data/repository/characters_repository.dart
  25. 18 4
      lib/data/repository/config_repository.dart
  26. 11 2
      lib/di/get_it.config.dart
  27. 148 0
      lib/dialog/tips_dialog.dart
  28. 2 2
      lib/module/character/character_controller.dart
  29. 22 22
      lib/module/character/content/character_group_content_controller.dart
  30. 40 37
      lib/module/character_custom/character_custom_controller.dart
  31. 23 14
      lib/module/character_custom/character_custom_page.dart
  32. 117 5
      lib/module/character_custom/detail/character_custom_detail_controller.dart
  33. 99 61
      lib/module/character_custom/detail/character_custom_detail_page.dart
  34. 138 0
      lib/module/character_custom/list/character_custom_list_controller.dart
  35. 307 0
      lib/module/character_custom/list/character_custom_list_page.dart
  36. 20 20
      lib/module/keyboard_manage/keyboard_manage_controller.dart
  37. 99 86
      lib/module/keyboard_manage/keyboard_manage_page.dart
  38. 10 0
      lib/resource/assets.gen.dart
  39. 18 0
      lib/resource/string.gen.dart
  40. 12 3
      lib/router/app_pages.dart
  41. 15 1
      lib/utils/styles.dart
  42. 3 0
      pubspec.yaml

BIN
assets/images/bg_character_custom_list.webp


BIN
assets/images/icon_character_custom_delete.webp


+ 19 - 1
assets/string/base/string.xml

@@ -107,7 +107,7 @@
     <string name="character_custom_name_hint">输入你的名字</string>
     <string name="character_custom_history">定制历史</string>
 
-        <!--    自定义标签-->
+    <!--    自定义标签-->
     <string name="custom_label">自定义标签</string>
     <string name="custom_label_hobbies_hint">输入你的爱好</string>
     <string name="custom_label_character_hint">输入你的特质</string>
@@ -118,4 +118,22 @@
     <!--    生成定制人设页面-->
     <string name="unlock_exclusive_character">解锁专属人设</string>
 
+    <!--    人设详情页面-->
+
+    <string name="gender">性别</string>
+    <string name="birthday">出生日期</string>
+    <string name="hobbies">兴趣爱好</string>
+    <string name="character_keywords">人设关键词</string>
+
+
+    <!--    人设列表页面-->
+    <string name="delete">删除</string>
+
+    <!--    提示弹窗-->
+    <string name="tips_dialog_title">温馨提示</string>
+    <string name="tips_dialog_confirm">保存</string>
+    <string name="tips_dialog_cancel">不保存</string>
+    <string name="tips_dialog_desc">是否保存键盘当前的修改?</string>
+
+
 </resources>

+ 5 - 3
lib/base/base_response.dart

@@ -1,17 +1,19 @@
-
 import 'package:json_annotation/json_annotation.dart';
 
 part 'base_response.g.dart';
 
 @JsonSerializable(genericArgumentFactories: true)
 class BaseResponse<T> {
+  @JsonKey(name: 'code')
   int? code;
+  @JsonKey(name: 'msg')
   String? message;
   T? data;
 
   BaseResponse(this.data, this.code, this.message);
 
   factory BaseResponse.fromJson(
-          Map<String, dynamic> json, T Function(Object? json) fromJsonT) =>
-      _$BaseResponseFromJson(json, fromJsonT);
+    Map<String, dynamic> json,
+    T Function(Object? json) fromJsonT,
+  ) => _$BaseResponseFromJson(json, fromJsonT);
 }

+ 2 - 2
lib/base/base_response.g.dart

@@ -12,7 +12,7 @@ BaseResponse<T> _$BaseResponseFromJson<T>(
 ) => BaseResponse<T>(
   _$nullableGenericFromJson(json['data'], fromJsonT),
   (json['code'] as num?)?.toInt(),
-  json['message'] as String?,
+  json['msg'] as String?,
 );
 
 Map<String, dynamic> _$BaseResponseToJson<T>(
@@ -20,7 +20,7 @@ Map<String, dynamic> _$BaseResponseToJson<T>(
   Object? Function(T value) toJsonT,
 ) => <String, dynamic>{
   'code': instance.code,
-  'message': instance.message,
+  'msg': instance.message,
   'data': _$nullableGenericToJson(instance.data, toJsonT),
 };
 

+ 32 - 2
lib/data/api/atmob_api.dart

@@ -1,7 +1,11 @@
 import 'package:dio/dio.dart';
 import 'package:keyboard/base/base_response.dart';
 import 'package:keyboard/data/api/request/character_add_request.dart';
+import 'package:keyboard/data/api/request/character_custom_add_request.dart';
+import 'package:keyboard/data/api/request/character_custom_delete_request.dart';
 import 'package:keyboard/data/api/request/character_custom_generate_request.dart';
+import 'package:keyboard/data/api/request/character_custom_page_request.dart';
+import 'package:keyboard/data/api/request/character_custom_update_request.dart';
 import 'package:keyboard/data/api/request/character_page_request.dart';
 import 'package:keyboard/data/api/request/character_unlock_request.dart';
 import 'package:keyboard/data/api/request/complaint_submit_request.dart';
@@ -16,6 +20,8 @@ import 'package:keyboard/data/api/request/user_info_setting_request.dart';
 import 'package:keyboard/data/api/response/character_add_response.dart';
 import 'package:keyboard/data/api/response/character_custom_config_response.dart';
 import 'package:keyboard/data/api/response/character_custom_generate_response.dart';
+import 'package:keyboard/data/api/response/character_custom_page_response.dart';
+import 'package:keyboard/data/api/response/character_custom_update_response.dart';
 import 'package:keyboard/data/api/response/character_group_response.dart';
 import 'package:keyboard/data/api/response/character_page_response.dart';
 import 'package:keyboard/data/api/response/character_unlock_response.dart';
@@ -107,6 +113,32 @@ abstract class AtmobApi {
     @Body() CharacterCustomGenerateRequest request,
   );
 
+  //更新定制人设信息
+  @POST("/project/keyboard/v1/character/custom/update")
+  Future<BaseResponse<CharacterCustomUpdateResponse>> updateCustomCharacter(
+    @Body() CharacterCustomUpdateRequest request,
+  );
+
+  // 添加定制人设到键盘
+  @POST("/project/keyboard/v1/character/custom/add")
+  Future<BaseResponse<CharacterCustomUpdateResponse>> addCustomCharacter(
+    @Body() CharacterCustomAddRequest request,
+  );
+
+  // 分页查询定制人设列表
+  @POST("/project/keyboard/v1/character/custom/page")
+  Future<BaseResponse<CharacterCustomPageResponse>> getCustomCharactersPage(
+    @Body() CharacterCustomPageRequest request,
+  );
+
+
+  // 删除定制人设
+  @POST("/project/keyboard/v1/character/custom/delete")
+  Future<BaseResponse> deleteCustomCharacter(
+      @Body() CharacterCustomDeleteRequest request,
+      );
+
+
   // 获取键盘人设列表
   @POST("/project/keyboard/v1/character/list")
   Future<BaseResponse<KeyboardCharacterListResponse>> getKeyboardCharacterList(
@@ -132,6 +164,4 @@ abstract class AtmobApi {
   //获取配置信息
   @POST("/project/keyboard/v1/confs")
   Future<BaseResponse<ConfigResponse>> confs(@Body() ConfigRequest request);
-
-
 }

+ 143 - 0
lib/data/api/atmob_api.g.dart

@@ -455,6 +455,149 @@ class _AtmobApi implements AtmobApi {
   }
 
   @override
+  Future<BaseResponse<CharacterCustomUpdateResponse>> updateCustomCharacter(
+    CharacterCustomUpdateRequest request,
+  ) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final _options =
+        _setStreamType<BaseResponse<CharacterCustomUpdateResponse>>(
+          Options(method: 'POST', headers: _headers, extra: _extra)
+              .compose(
+                _dio.options,
+                '/project/keyboard/v1/character/custom/update',
+                queryParameters: queryParameters,
+                data: _data,
+              )
+              .copyWith(
+                baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl),
+              ),
+        );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<CharacterCustomUpdateResponse> _value;
+    try {
+      _value = BaseResponse<CharacterCustomUpdateResponse>.fromJson(
+        _result.data!,
+        (json) => CharacterCustomUpdateResponse.fromJson(
+          json as Map<String, dynamic>,
+        ),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
+  Future<BaseResponse<CharacterCustomUpdateResponse>> addCustomCharacter(
+    CharacterCustomAddRequest request,
+  ) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final _options =
+        _setStreamType<BaseResponse<CharacterCustomUpdateResponse>>(
+          Options(method: 'POST', headers: _headers, extra: _extra)
+              .compose(
+                _dio.options,
+                '/project/keyboard/v1/character/custom/add',
+                queryParameters: queryParameters,
+                data: _data,
+              )
+              .copyWith(
+                baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl),
+              ),
+        );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<CharacterCustomUpdateResponse> _value;
+    try {
+      _value = BaseResponse<CharacterCustomUpdateResponse>.fromJson(
+        _result.data!,
+        (json) => CharacterCustomUpdateResponse.fromJson(
+          json as Map<String, dynamic>,
+        ),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
+  Future<BaseResponse<CharacterCustomPageResponse>> getCustomCharactersPage(
+    CharacterCustomPageRequest request,
+  ) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final _options = _setStreamType<BaseResponse<CharacterCustomPageResponse>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/character/custom/page',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<CharacterCustomPageResponse> _value;
+    try {
+      _value = BaseResponse<CharacterCustomPageResponse>.fromJson(
+        _result.data!,
+        (json) =>
+            CharacterCustomPageResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
+  Future<BaseResponse<dynamic>> deleteCustomCharacter(
+    CharacterCustomDeleteRequest request,
+  ) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final _options = _setStreamType<BaseResponse<dynamic>>(
+      Options(method: 'POST', headers: _headers, extra: _extra)
+          .compose(
+            _dio.options,
+            '/project/keyboard/v1/character/custom/delete',
+            queryParameters: queryParameters,
+            data: _data,
+          )
+          .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+    );
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<dynamic> _value;
+    try {
+      _value = BaseResponse<dynamic>.fromJson(
+        _result.data!,
+        (json) => json as dynamic,
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
+  @override
   Future<BaseResponse<KeyboardCharacterListResponse>> getKeyboardCharacterList(
     KeyboardCharacterListRequest request,
   ) async {

+ 23 - 0
lib/data/api/request/character_custom_add_request.dart

@@ -0,0 +1,23 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'character_custom_add_request.g.dart';
+
+@JsonSerializable()
+class CharacterCustomAddRequest extends AppBaseRequest {
+  @JsonKey(name: "characterId")
+  String characterId;
+
+  @JsonKey(name: "keyboardId")
+  String keyboardId;
+
+
+  CharacterCustomAddRequest({
+    required this.characterId,
+    required this.keyboardId,
+  });
+
+  @override
+  Map<String, dynamic> toJson() => _$CharacterCustomAddRequestToJson(this);
+}

+ 74 - 0
lib/data/api/request/character_custom_add_request.g.dart

@@ -0,0 +1,74 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_custom_add_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterCustomAddRequest _$CharacterCustomAddRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    CharacterCustomAddRequest(
+        characterId: json['characterId'] as String,
+        keyboardId: json['keyboardId'] as String,
+      )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$CharacterCustomAddRequestToJson(
+  CharacterCustomAddRequest instance,
+) => <String, dynamic>{
+  'appPlatform': instance.appPlatform,
+  'os': instance.os,
+  'osVersion': instance.osVersion,
+  'packageName': instance.packageName,
+  'appVersionName': instance.appVersionName,
+  'appVersionCode': instance.appVersionCode,
+  'channelName': instance.channelName,
+  'appId': instance.appId,
+  'tgPlatform': instance.tgPlatform,
+  'oaid': instance.oaid,
+  'aaid': instance.aaid,
+  'androidId': instance.androidId,
+  'imei': instance.imei,
+  'simImei0': instance.simImei0,
+  'simImei1': instance.simImei1,
+  'mac': instance.mac,
+  'idfa': instance.idfa,
+  'idfv': instance.idfv,
+  'machineId': instance.machineId,
+  'brand': instance.brand,
+  'model': instance.model,
+  'wifiName': instance.wifiName,
+  'region': instance.region,
+  'locLng': instance.locLng,
+  'locLat': instance.locLat,
+  'authToken': instance.authToken,
+  'characterId': instance.characterId,
+  'keyboardId': instance.keyboardId,
+};

+ 17 - 0
lib/data/api/request/character_custom_delete_request.dart

@@ -0,0 +1,17 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'character_custom_delete_request.g.dart';
+
+@JsonSerializable()
+class CharacterCustomDeleteRequest extends AppBaseRequest {
+  // 人设id
+  @JsonKey(name: "id")
+  String id;
+
+  CharacterCustomDeleteRequest({required this.id});
+
+  @override
+  Map<String, dynamic> toJson() => _$CharacterCustomDeleteRequestToJson(this);
+}

+ 70 - 0
lib/data/api/request/character_custom_delete_request.g.dart

@@ -0,0 +1,70 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_custom_delete_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterCustomDeleteRequest _$CharacterCustomDeleteRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    CharacterCustomDeleteRequest(id: json['id'] as String)
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$CharacterCustomDeleteRequestToJson(
+  CharacterCustomDeleteRequest instance,
+) => <String, dynamic>{
+  'appPlatform': instance.appPlatform,
+  'os': instance.os,
+  'osVersion': instance.osVersion,
+  'packageName': instance.packageName,
+  'appVersionName': instance.appVersionName,
+  'appVersionCode': instance.appVersionCode,
+  'channelName': instance.channelName,
+  'appId': instance.appId,
+  'tgPlatform': instance.tgPlatform,
+  'oaid': instance.oaid,
+  'aaid': instance.aaid,
+  'androidId': instance.androidId,
+  'imei': instance.imei,
+  'simImei0': instance.simImei0,
+  'simImei1': instance.simImei1,
+  'mac': instance.mac,
+  'idfa': instance.idfa,
+  'idfv': instance.idfv,
+  'machineId': instance.machineId,
+  'brand': instance.brand,
+  'model': instance.model,
+  'wifiName': instance.wifiName,
+  'region': instance.region,
+  'locLng': instance.locLng,
+  'locLat': instance.locLat,
+  'authToken': instance.authToken,
+  'id': instance.id,
+};

+ 26 - 0
lib/data/api/request/character_custom_page_request.dart

@@ -0,0 +1,26 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'character_custom_page_request.g.dart';
+
+@JsonSerializable()
+class CharacterCustomPageRequest extends AppBaseRequest {
+  //  键盘id,可返回是否添加到该键盘的状态,不传不返回状态
+  @JsonKey(name: "keyboardId")
+  String? keyboardId;
+
+  @JsonKey(name: "page")
+  int page;
+  @JsonKey(name: "pageSize")
+  int pageSize;
+
+  CharacterCustomPageRequest({
+    this.keyboardId,
+    required this.page,
+    required this.pageSize,
+  });
+
+  @override
+  Map<String, dynamic> toJson() => _$CharacterCustomPageRequestToJson(this);
+}

+ 76 - 0
lib/data/api/request/character_custom_page_request.g.dart

@@ -0,0 +1,76 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_custom_page_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterCustomPageRequest _$CharacterCustomPageRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    CharacterCustomPageRequest(
+        keyboardId: json['keyboardId'] as String?,
+        page: (json['page'] as num).toInt(),
+        pageSize: (json['pageSize'] as num).toInt(),
+      )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$CharacterCustomPageRequestToJson(
+  CharacterCustomPageRequest instance,
+) => <String, dynamic>{
+  'appPlatform': instance.appPlatform,
+  'os': instance.os,
+  'osVersion': instance.osVersion,
+  'packageName': instance.packageName,
+  'appVersionName': instance.appVersionName,
+  'appVersionCode': instance.appVersionCode,
+  'channelName': instance.channelName,
+  'appId': instance.appId,
+  'tgPlatform': instance.tgPlatform,
+  'oaid': instance.oaid,
+  'aaid': instance.aaid,
+  'androidId': instance.androidId,
+  'imei': instance.imei,
+  'simImei0': instance.simImei0,
+  'simImei1': instance.simImei1,
+  'mac': instance.mac,
+  'idfa': instance.idfa,
+  'idfv': instance.idfv,
+  'machineId': instance.machineId,
+  'brand': instance.brand,
+  'model': instance.model,
+  'wifiName': instance.wifiName,
+  'region': instance.region,
+  'locLng': instance.locLng,
+  'locLat': instance.locLat,
+  'authToken': instance.authToken,
+  'keyboardId': instance.keyboardId,
+  'page': instance.page,
+  'pageSize': instance.pageSize,
+};

+ 42 - 0
lib/data/api/request/character_custom_update_request.dart

@@ -0,0 +1,42 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../../base/app_base_request.dart';
+
+part 'character_custom_update_request.g.dart';
+
+@JsonSerializable()
+class CharacterCustomUpdateRequest extends AppBaseRequest {
+  @JsonKey(name: "id")
+  String id;
+
+  @JsonKey(name: "name")
+  String? name;
+
+  @JsonKey(name: "birthday")
+  String? birthday;
+
+  @JsonKey(name: "imageUrl")
+  String? imageUrl;
+
+  @JsonKey(name: "gender")
+  int? gender;
+
+  @JsonKey(name: "hobbies")
+  List<String>? hobbies;
+
+  @JsonKey(name: "characters")
+  List<String>? characters;
+
+  CharacterCustomUpdateRequest({
+    required this.id,
+    this.name,
+    this.birthday,
+    this.imageUrl,
+    this.gender,
+    this.hobbies,
+    this.characters,
+  });
+
+  @override
+  Map<String, dynamic> toJson() => _$CharacterCustomUpdateRequestToJson(this);
+}

+ 90 - 0
lib/data/api/request/character_custom_update_request.g.dart

@@ -0,0 +1,90 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_custom_update_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterCustomUpdateRequest _$CharacterCustomUpdateRequestFromJson(
+  Map<String, dynamic> json,
+) =>
+    CharacterCustomUpdateRequest(
+        id: json['id'] as String,
+        name: json['name'] as String?,
+        birthday: json['birthday'] as String?,
+        imageUrl: json['imageUrl'] as String?,
+        gender: (json['gender'] as num?)?.toInt(),
+        hobbies:
+            (json['hobbies'] as List<dynamic>?)
+                ?.map((e) => e as String)
+                .toList(),
+        characters:
+            (json['characters'] as List<dynamic>?)
+                ?.map((e) => e as String)
+                .toList(),
+      )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$CharacterCustomUpdateRequestToJson(
+  CharacterCustomUpdateRequest instance,
+) => <String, dynamic>{
+  'appPlatform': instance.appPlatform,
+  'os': instance.os,
+  'osVersion': instance.osVersion,
+  'packageName': instance.packageName,
+  'appVersionName': instance.appVersionName,
+  'appVersionCode': instance.appVersionCode,
+  'channelName': instance.channelName,
+  'appId': instance.appId,
+  'tgPlatform': instance.tgPlatform,
+  'oaid': instance.oaid,
+  'aaid': instance.aaid,
+  'androidId': instance.androidId,
+  'imei': instance.imei,
+  'simImei0': instance.simImei0,
+  'simImei1': instance.simImei1,
+  'mac': instance.mac,
+  'idfa': instance.idfa,
+  'idfv': instance.idfv,
+  'machineId': instance.machineId,
+  'brand': instance.brand,
+  'model': instance.model,
+  'wifiName': instance.wifiName,
+  'region': instance.region,
+  'locLng': instance.locLng,
+  'locLat': instance.locLat,
+  'authToken': instance.authToken,
+  'id': instance.id,
+  'name': instance.name,
+  'birthday': instance.birthday,
+  'imageUrl': instance.imageUrl,
+  'gender': instance.gender,
+  'hobbies': instance.hobbies,
+  'characters': instance.characters,
+};

+ 16 - 0
lib/data/api/response/character_custom_add_response.dart

@@ -0,0 +1,16 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/character_info.dart';
+
+part 'character_custom_add_response.g.dart';
+
+@JsonSerializable()
+class CharacterCustomAddResponse {
+  @JsonKey(name: "characterInfo")
+  late final CharacterInfo characterInfo;
+
+  CharacterCustomAddResponse({required this.characterInfo});
+
+  factory CharacterCustomAddResponse.fromJson(Map<String, dynamic> json) =>
+      _$CharacterCustomAddResponseFromJson(json);
+}

+ 19 - 0
lib/data/api/response/character_custom_add_response.g.dart

@@ -0,0 +1,19 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_custom_add_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterCustomAddResponse _$CharacterCustomAddResponseFromJson(
+  Map<String, dynamic> json,
+) => CharacterCustomAddResponse(
+  characterInfo: CharacterInfo.fromJson(
+    json['characterInfo'] as Map<String, dynamic>,
+  ),
+);
+
+Map<String, dynamic> _$CharacterCustomAddResponseToJson(
+  CharacterCustomAddResponse instance,
+) => <String, dynamic>{'characterInfo': instance.characterInfo};

+ 2 - 2
lib/data/api/response/character_custom_config_response.dart

@@ -8,9 +8,9 @@ part 'character_custom_config_response.g.dart';
 @JsonSerializable()
 class CharacterCustomConfigResponse {
   @JsonKey(name: "customConfig")
-  CustomConfigInfo? characterInfo;
+  CustomConfigInfo? customConfig;
 
-  CharacterCustomConfigResponse({this.characterInfo});
+  CharacterCustomConfigResponse({this.customConfig});
 
   factory CharacterCustomConfigResponse.fromJson(Map<String, dynamic> json) =>
       _$CharacterCustomConfigResponseFromJson(json);

+ 2 - 2
lib/data/api/response/character_custom_config_response.g.dart

@@ -9,7 +9,7 @@ part of 'character_custom_config_response.dart';
 CharacterCustomConfigResponse _$CharacterCustomConfigResponseFromJson(
   Map<String, dynamic> json,
 ) => CharacterCustomConfigResponse(
-  characterInfo:
+  customConfig:
       json['customConfig'] == null
           ? null
           : CustomConfigInfo.fromJson(
@@ -19,4 +19,4 @@ CharacterCustomConfigResponse _$CharacterCustomConfigResponseFromJson(
 
 Map<String, dynamic> _$CharacterCustomConfigResponseToJson(
   CharacterCustomConfigResponse instance,
-) => <String, dynamic>{'customConfig': instance.characterInfo};
+) => <String, dynamic>{'customConfig': instance.customConfig};

+ 18 - 0
lib/data/api/response/character_custom_page_response.dart

@@ -0,0 +1,18 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/character_info.dart';
+
+part 'character_custom_page_response.g.dart';
+
+@JsonSerializable()
+class CharacterCustomPageResponse {
+  @JsonKey(name: "count")
+  int? count;
+  @JsonKey(name: "list")
+  List<CharacterInfo>? characterInfos;
+
+  CharacterCustomPageResponse({this.count, this.characterInfos});
+
+  factory CharacterCustomPageResponse.fromJson(Map<String, dynamic> json) =>
+      _$CharacterCustomPageResponseFromJson(json);
+}

+ 24 - 0
lib/data/api/response/character_custom_page_response.g.dart

@@ -0,0 +1,24 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_custom_page_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterCustomPageResponse _$CharacterCustomPageResponseFromJson(
+  Map<String, dynamic> json,
+) => CharacterCustomPageResponse(
+  count: (json['count'] as num?)?.toInt(),
+  characterInfos:
+      (json['list'] as List<dynamic>?)
+          ?.map((e) => CharacterInfo.fromJson(e as Map<String, dynamic>))
+          .toList(),
+);
+
+Map<String, dynamic> _$CharacterCustomPageResponseToJson(
+  CharacterCustomPageResponse instance,
+) => <String, dynamic>{
+  'count': instance.count,
+  'list': instance.characterInfos,
+};

+ 18 - 0
lib/data/api/response/character_custom_update_response.dart

@@ -0,0 +1,18 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/character_info.dart';
+
+
+part 'character_custom_update_response.g.dart';
+
+@JsonSerializable()
+class CharacterCustomUpdateResponse {
+
+  @JsonKey(name: "characterInfo")
+  late final CharacterInfo characterInfo;
+
+  CharacterCustomUpdateResponse({ required this.characterInfo});
+
+  factory CharacterCustomUpdateResponse.fromJson(Map<String, dynamic> json) =>
+      _$CharacterCustomUpdateResponseFromJson(json);
+}

+ 19 - 0
lib/data/api/response/character_custom_update_response.g.dart

@@ -0,0 +1,19 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'character_custom_update_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+CharacterCustomUpdateResponse _$CharacterCustomUpdateResponseFromJson(
+  Map<String, dynamic> json,
+) => CharacterCustomUpdateResponse(
+  characterInfo: CharacterInfo.fromJson(
+    json['characterInfo'] as Map<String, dynamic>,
+  ),
+);
+
+Map<String, dynamic> _$CharacterCustomUpdateResponseToJson(
+  CharacterCustomUpdateResponse instance,
+) => <String, dynamic>{'characterInfo': instance.characterInfo};

+ 97 - 12
lib/data/repository/characters_repository.dart

@@ -1,7 +1,10 @@
 import 'package:injectable/injectable.dart';
 
 import 'package:get/get.dart';
+import 'package:keyboard/data/api/request/character_custom_update_request.dart';
 import 'package:keyboard/data/api/response/character_add_response.dart';
+import 'package:keyboard/data/api/response/character_custom_page_response.dart';
+import 'package:keyboard/data/api/response/character_custom_update_response.dart';
 import 'package:keyboard/data/api/response/character_unlock_response.dart';
 import 'package:keyboard/data/repository/keyboard_repository.dart';
 import '../../base/app_base_request.dart';
@@ -10,7 +13,10 @@ import '../../utils/atmob_log.dart';
 import '../../utils/http_handler.dart';
 import '../api/atmob_api.dart';
 import '../api/request/character_add_request.dart';
+import '../api/request/character_custom_add_request.dart';
+import '../api/request/character_custom_delete_request.dart';
 import '../api/request/character_custom_generate_request.dart';
+import '../api/request/character_custom_page_request.dart';
 import '../api/request/character_page_request.dart';
 import '../api/request/character_unlock_request.dart';
 import '../api/response/character_custom_generate_response.dart';
@@ -34,7 +40,6 @@ class CharactersRepository {
     getCharactersGroup();
   }
 
-
   // 获取主题
   Future<CharacterGroupResponse> getCharactersGroup() async {
     return atmobApi
@@ -51,7 +56,7 @@ class CharactersRepository {
     required String groupId,
     required String keyboardId,
     int page = 1,
-    int pageSize = 5,
+    int pageSize = 10,
   }) {
     return atmobApi
         .getCharactersPage(
@@ -89,26 +94,106 @@ class CharactersRepository {
             keyboardId: keyboardId,
           ),
         )
-        .then(HttpHandler.handle(false));
+        .then(HttpHandler.handle(true));
   }
 
-
   ///生成自定义人设
   Future<CharacterCustomGenerateResponse> generateCharacterCustom({
-    String?name,
-    String?birthday,
-    String?imageUrl,
-    int?gender,
-    List<String>?hobbies,
-    List<String>?characters,
+    String? name,
+    String? birthday,
+    String? imageUrl,
+    int? gender,
+    List<String>? hobbies,
+    List<String>? characters,
+  }) {
+    return atmobApi
+        .generateCharacterCustom(
+          CharacterCustomGenerateRequest(
+            name: name,
+            birthday: birthday,
+            imageUrl: imageUrl,
+            gender: gender,
+            hobbies: hobbies,
+            characters: characters,
+          ),
+        )
+        .then(HttpHandler.handle(true));
+  }
+
+  // 更新定制人设信息
+  Future<CharacterCustomUpdateResponse> characterCustomUpdate({
+    required String id,
+    String? name,
+    String? birthday,
+    String? imgUrl,
+    int? gender,
+    List<String>? hobbies,
+    List<String>? characters,
+  }) {
+    return atmobApi
+        .updateCustomCharacter(
+          CharacterCustomUpdateRequest(
+            id: id,
+            name: name,
+            birthday: birthday,
+            imageUrl: imgUrl,
+            gender: gender,
+            hobbies: hobbies,
+            characters: characters,
+          ),
+        )
+        .then(HttpHandler.handle(true));
+  }
+
+  // 添加定制人设到键盘
+  Future<CharacterCustomUpdateResponse> addCustomCharacter({
+    required String characterId,
+    required String keyboardId,
+  }) {
+    return atmobApi
+        .addCustomCharacter(
+          CharacterCustomAddRequest(
+            characterId: characterId,
+            keyboardId: keyboardId,
+          ),
+        )
+        .then(HttpHandler.handle(true));
   }
-      ) {
+
+  // 分页查询定制人设列表
+  Future<CharacterCustomPageResponse> getCustomCharactersPage({
+    String? keyboardId,
+    int page = 1,
+    int pageSize = 10,
+  }) {
     return atmobApi
-        .generateCharacterCustom(CharacterCustomGenerateRequest(name: name,birthday: birthday,imageUrl: imageUrl,gender: gender,hobbies: hobbies, characters: characters))
+        .getCustomCharactersPage(
+          CharacterCustomPageRequest(
+            keyboardId: keyboardId,
+            page: page,
+            pageSize: pageSize,
+          ),
+        )
         .then(HttpHandler.handle(true));
   }
 
+  // 删除定制人设
+  Future<void> deleteCustomCharacter({required String characterId}) {
+    return atmobApi
+        .deleteCustomCharacter(CharacterCustomDeleteRequest(id: characterId))
+        .then(HttpHandler.handle(false));
+  }
 
   static CharactersRepository getInstance() =>
       getIt.get<CharactersRepository>();
 }
+
+class CharacterException implements Exception {
+  final String msg;
+  final int? code;
+
+  CharacterException(this.msg, {this.code});
+
+  @override
+  String toString() => msg;
+}

+ 18 - 4
lib/data/repository/config_repository.dart

@@ -9,17 +9,24 @@ import '../api/atmob_api.dart';
 import '../api/request/config_request.dart';
 import '../api/response/config_response.dart';
 
+import 'package:get/get.dart';
+
+import '../bean/custom_config_info.dart';
+
 @lazySingleton
 class ConfigRepository {
   final tag = "ConfigRepository";
 
   final AtmobApi atmobApi;
 
+  final Rxn<CustomConfigInfo> _characterCustomConfig = Rxn<CustomConfigInfo>();
+
+  CustomConfigInfo? get characterCustomConfig => _characterCustomConfig.value;
+
   ConfigRepository(this.atmobApi) {
     AtmobLog.d(tag, '$tag....init');
     refreshConfig();
-
-
+    refreshCharacterCustomConfig();
   }
 
   // 更新配置的值
@@ -36,6 +43,15 @@ class ConfigRepository {
     });
   }
 
+  Future<void> refreshCharacterCustomConfig() async {
+    try {
+      final value = await getCharacterCustomConfig();
+      _characterCustomConfig.value = value.customConfig;
+    } catch (e) {
+      AtmobLog.e(tag, "获取定制人设配置失败: $e");
+    }
+  }
+
   /// 获取配置信息
   Future<ConfigResponse> requestConfigsData() {
     return atmobApi
@@ -49,6 +65,4 @@ class ConfigRepository {
         .getCharacterCustomConfig(AppBaseRequest())
         .then(HttpHandler.handle(true));
   }
-
-
 }

+ 11 - 2
lib/di/get_it.config.dart

@@ -32,6 +32,8 @@ import '../module/character/content/character_group_content_controller.dart'
 import '../module/character_custom/character_custom_controller.dart' as _i15;
 import '../module/character_custom/detail/character_custom_detail_controller.dart'
     as _i79;
+import '../module/character_custom/list/character_custom_list_controller.dart'
+    as _i1059;
 import '../module/feedback/feedback_controller.dart' as _i876;
 import '../module/keyboard_manage/keyboard_manage_controller.dart' as _i922;
 import '../module/login/login_controller.dart' as _i1008;
@@ -130,14 +132,21 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i274.KeyboardRepository>(),
       ),
     );
+    gh.factory<_i79.CharacterCustomDetailController>(
+      () => _i79.CharacterCustomDetailController(
+        gh<_i421.CharactersRepository>(),
+        gh<_i50.ConfigRepository>(),
+      ),
+    );
     gh.factory<_i970.CharacterGroupContentController>(
       () => _i970.CharacterGroupContentController(
         gh<_i421.CharactersRepository>(),
       ),
     );
-    gh.factory<_i79.CharacterCustomDetailController>(
-      () => _i79.CharacterCustomDetailController(
+    gh.factory<_i1059.CharacterCustomListController>(
+      () => _i1059.CharacterCustomListController(
         gh<_i421.CharactersRepository>(),
+        gh<_i274.KeyboardRepository>(),
       ),
     );
     return this;

+ 148 - 0
lib/dialog/tips_dialog.dart

@@ -0,0 +1,148 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:keyboard/resource/string.gen.dart';
+
+import '../resource/assets.gen.dart';
+import '../resource/colors.gen.dart';
+import '../utils/styles.dart';
+
+// 温馨提示弹窗
+class TipsDialog {
+  static const String tag = 'TipsDialog';
+
+  static void show({
+    String? title,
+    String? desc,
+    String? btnCancelText,
+    String? btnConfirmText,
+    Function? btnCancel,
+    Function? btnConfirm,
+  }) {
+    SmartDialog.show(
+      tag: tag,
+      backType: SmartBackType.block,
+      clickMaskDismiss: true,
+      maskColor: ColorName.black70,
+      builder: (_) {
+        return Scaffold(
+          backgroundColor: Colors.transparent,
+          body: Column(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            mainAxisAlignment: MainAxisAlignment.center,
+            children: [
+              Container(
+                width: double.infinity,
+                margin: EdgeInsets.symmetric(horizontal: 31.w),
+                decoration: ShapeDecoration(
+                  color: Colors.white,
+                  shape: RoundedRectangleBorder(
+                    borderRadius: BorderRadius.circular(20.r),
+                  ),
+                ),
+                child: Stack(
+                  children: [
+                    Container(
+                      padding: EdgeInsets.symmetric(
+                        horizontal: 16.w,
+                        vertical: 24.h,
+                      ),
+                      child: Column(
+                        mainAxisSize: MainAxisSize.min,
+                        crossAxisAlignment: CrossAxisAlignment.center,
+                        mainAxisAlignment: MainAxisAlignment.center,
+                        children: [
+                          Text(
+                            title ?? "",
+                            style: Styles.getTextStyleBlack204W500(16.sp),
+                          ),
+                          SizedBox(height: 16.h),
+                          Text(
+                            desc ?? "",
+                            style: Styles.getTextStyleBlack204W400(14.sp),
+                          ),
+                          SizedBox(height: 20.h),
+                          Row(
+                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                            children: [
+                              Expanded(
+                                child: GestureDetector(
+                                  onTap: () {
+                                    if (btnCancel != null) {
+                                      btnCancel();
+                                    }
+                                    SmartDialog.dismiss();
+                                  },
+                                  child: Container(
+                                    height: 48.h,
+                                    alignment: Alignment.center,
+                                    decoration: ShapeDecoration(
+                                      color: const Color(0xFFF5F4F9),
+                                      shape: RoundedRectangleBorder(
+                                        borderRadius: BorderRadius.circular(
+                                          31.r,
+                                        ),
+                                      ),
+                                    ),
+                                    child: Text(
+                                      btnCancelText ?? "",
+                                      style: Styles.getTextStyleBlack204W500(
+                                        16.sp,
+                                      ),
+                                    ),
+                                  ),
+                                ),
+                              ),
+                              SizedBox(width: 16.w),
+                              Expanded(
+                                child: GestureDetector(
+                                  onTap: () {
+                                    if (btnConfirm != null) {
+                                      btnConfirm();
+                                    }
+                                    SmartDialog.dismiss(tag: tag);
+                                  },
+                                  child: Container(
+                                    height: 48.h,
+                                    alignment: Alignment.center,
+                                    decoration:
+                                        Styles.getActivateButtonDecoration(
+                                          20.r,
+                                        ),
+                                    child: Text(
+                                      btnConfirmText ?? "",
+                                      style: Styles.getTextStyleWhiteW500(
+                                        16.sp,
+                                      ),
+                                    ),
+                                  ),
+                                ),
+                              ),
+                            ],
+                          ),
+                        ],
+                      ),
+                    ),
+                    Positioned(
+                      right: 14.w,
+                      top: 14.h,
+                      child: GestureDetector(
+                        onTap: () {
+                          SmartDialog.dismiss();
+                        },
+                        child: Assets.images.iconCustomDialogClose.image(
+                          width: 24.w,
+                          height: 24.h,
+                        ),
+                      ),
+                    ),
+                  ],
+                ),
+              ),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}

+ 2 - 2
lib/module/character/character_controller.dart

@@ -27,11 +27,11 @@ class CharacterController extends BaseController
   );
 
   // 人设主题
-  RxList<CharacterGroupInfo> get characterGroupList =>
+  List<CharacterGroupInfo> get characterGroupList =>
       charactersRepository.characterGroupList;
 
   // 键盘列表
-  RxList<KeyboardInfo> get keyboardInfoList =>
+  List<KeyboardInfo> get keyboardInfoList =>
       keyboardRepository.keyboardInfoList;
 
   late Rx<TabController> tabController;

+ 22 - 22
lib/module/character/content/character_group_content_controller.dart

@@ -113,28 +113,28 @@ class CharacterGroupContentController extends BaseController {
     );
   }
 
-  void addCharacter(CharacterInfo characterInfo) {
-    charactersRepository
-        .characterAdd(
-          characterId: characterInfo.id.toString(),
-          keyboardId: currentKeyboardInfo.value.id.toString(),
-        )
-        .then((characterAddResponse) {
-          int index = characterList.indexWhere(
-            (element) => element.id == characterAddResponse.characterInfo.id,
-          );
-          if (index != -1) {
-            characterList[index] = characterAddResponse.characterInfo;
-          }
-          ToastUtil.show('添加成功~');
-        })
-        .catchError((error) {
-          if (error is ServerErrorException && error.code == 1005) {
-            ToastUtil.show('请开通会员解锁权益~');
-          } else {
-            ErrorHandler.toastError(error);
-          }
-        });
+  Future<void> addCharacter(CharacterInfo characterInfo) async {
+    try {
+      final characterAddResponse = await charactersRepository.characterAdd(
+        characterId: characterInfo.id.toString(),
+        keyboardId: currentKeyboardInfo.value.id.toString(),
+      );
+
+      int index = characterList.indexWhere(
+        (element) => element.id == characterAddResponse.characterInfo.id,
+      );
+      if (index != -1) {
+        characterList[index] = characterAddResponse.characterInfo;
+      }
+
+      ToastUtil.show('添加成功~');
+    } catch (error) {
+      if (error is ServerErrorException && error.code == 1005) {
+        ToastUtil.show('请开通会员解锁权益~');
+      } else {
+        ErrorHandler.toastError(error);
+      }
+    }
   }
 
   void unlockCharacter(CharacterInfo characterInfo) {

+ 40 - 37
lib/module/character_custom/character_custom_controller.dart

@@ -5,6 +5,7 @@ import 'package:keyboard/base/base_controller.dart';
 import 'package:keyboard/data/bean/custom_config_info.dart';
 import 'package:keyboard/data/repository/config_repository.dart';
 import 'package:keyboard/module/character_custom/detail/character_custom_detail_page.dart';
+import 'package:keyboard/module/character_custom/list/character_custom_list_page.dart';
 import 'package:keyboard/resource/string.gen.dart';
 import 'package:keyboard/utils/atmob_log.dart';
 
@@ -25,16 +26,19 @@ enum StepType {
 @injectable
 class CharacterCustomController extends BaseController {
   final String tag = 'CharacterCustomController';
+
   final ConfigRepository configRepository;
 
-  final Rxn<CustomConfigInfo> currentCharacterCustomConfig =
-      Rxn<CustomConfigInfo>();
+  CustomConfigInfo? get currentCharacterCustomConfig =>
+      configRepository.characterCustomConfig;
 
   final RxList<Hobbies> hobbiesLabelsList = <Hobbies>[].obs;
-  final RxList<String> hobbiesSelectLabels = <String>[].obs;
+
+  final RxList<Hobbies> hobbiesSelectLabels = <Hobbies>[].obs;
 
   final RxList<CharactersList> characterLabelsList = <CharactersList>[].obs;
-  final RxList<String> characterSelectLabels = <String>[].obs;
+
+  final RxList<CharactersList> characterSelectLabels = <CharactersList>[].obs;
 
   final Rx<StepType> currentStep = StepType.home.obs;
 
@@ -42,23 +46,25 @@ class CharacterCustomController extends BaseController {
 
   CharacterCustomController(this.configRepository);
 
-
-
   @override
   void onInit() {
     super.onInit();
-
+    initData();
     AtmobLog.d(tag, "首次加载数据,触发 refreshCharacterCustomConfig()");
-    refreshCharacterCustomConfig();
   }
 
   //  初始化数据
+  void initData() {
+    AtmobLog.d(tag, "initData");
+    hobbiesLabelsList.value = currentCharacterCustomConfig?.hobbies ?? [];
+    characterLabelsList.value = currentCharacterCustomConfig?.characters ?? [];
+  }
 
   // 自定义兴趣爱好
   void clickHobbiesCustom() {
     AtmobLog.d(tag, "clickHobbiesCustom");
     CustomLabelDialog.show(
-      maxLength: currentCharacterCustomConfig.value?.maxHobbyWords ?? 10,
+      maxLength: currentCharacterCustomConfig?.maxHobbyWords ?? 10,
       hintText: StringName.customLabelHobbiesHint,
       clickCallback: (value) {
         hobbiesLabelsList.add(Hobbies(name: value));
@@ -70,7 +76,7 @@ class CharacterCustomController extends BaseController {
   void clickCharacterCustom() {
     AtmobLog.d(tag, "clickCharacterCustom");
     CustomLabelDialog.show(
-      maxLength: currentCharacterCustomConfig.value?.maxCharacterWords ?? 10,
+      maxLength: currentCharacterCustomConfig?.maxCharacterWords ?? 10,
       hintText: StringName.customLabelCharacterHint,
       clickCallback: (value) {
         characterLabelsList.add(CharactersList(name: value));
@@ -92,21 +98,10 @@ class CharacterCustomController extends BaseController {
     }
   }
 
-  Future<void> refreshCharacterCustomConfig() async {
-    try {
-      final value = await configRepository.getCharacterCustomConfig();
-      currentCharacterCustomConfig.value = value.characterInfo;
-      hobbiesLabelsList.value = value.characterInfo?.hobbies ?? [];
-      characterLabelsList.value = value.characterInfo?.characters ?? [];
-    } catch (e) {
-      AtmobLog.e(tag, "获取定制人设配置失败: $e");
-    }
-  }
-
   // 爱好页的下一步
   void clickHobbiesNext() {
-    int min = currentCharacterCustomConfig.value?.minHobbyNum ?? 1;
-    int max = currentCharacterCustomConfig.value?.maxHobbyNum ?? 3;
+    int min = currentCharacterCustomConfig?.minHobbyNum ?? 1;
+    int max = currentCharacterCustomConfig?.maxHobbyNum ?? 3;
 
     if (hobbiesSelectLabels.isEmpty) {
       ToastUtil.show("请选择爱好");
@@ -125,8 +120,8 @@ class CharacterCustomController extends BaseController {
 
   // 性格页的下一步
   void clickCharacterNext() {
-    int min = currentCharacterCustomConfig.value?.minCharacterNum ?? 1;
-    int max = currentCharacterCustomConfig.value?.maxCharacterNum ?? 3;
+    int min = currentCharacterCustomConfig?.minCharacterNum ?? 1;
+    int max = currentCharacterCustomConfig?.maxCharacterNum ?? 3;
 
     if (characterSelectLabels.isEmpty) {
       ToastUtil.show("请选择特质");
@@ -154,12 +149,16 @@ class CharacterCustomController extends BaseController {
       return;
     }
 
-    CharacterCustomDetailPage.start();
+    CharacterCustomDetailPage.start(
+      hobbiesSelectLabels: hobbiesSelectLabels,
+      characterSelectLabels: characterSelectLabels,
+      characterCustomName: currentNameValue.value,
+    );
   }
 
   // 处理下一步
   void clickNextButton(StepType stepType) {
-    if (currentCharacterCustomConfig.value == null) {
+    if (currentCharacterCustomConfig == null) {
       AtmobLog.e(tag, "clickStartCustom - 当前配置为空");
       return;
     }
@@ -175,31 +174,35 @@ class CharacterCustomController extends BaseController {
   }
 
   /// 选择爱好标签
-  void selectHobby(String name) {
+  void selectHobby(Hobbies hobby) {
     handleSelection(
-      name: name,
+      name: hobby,
       selectedList: hobbiesSelectLabels,
-      max: currentCharacterCustomConfig.value?.maxHobbyNum ?? 3,
-      errorMessage:
-          "最多选择${currentCharacterCustomConfig.value?.maxHobbyNum ?? 3}个爱好",
+      max: currentCharacterCustomConfig?.maxHobbyNum ?? 3,
+      errorMessage: "最多选择${currentCharacterCustomConfig?.maxHobbyNum ?? 3}个爱好",
     );
   }
 
   /// 选择性格标签
-  void selectCharacter(String name) {
+  void selectCharacter(CharactersList name) {
     handleSelection(
       name: name,
       selectedList: characterSelectLabels,
-      max: currentCharacterCustomConfig.value?.maxCharacterNum ?? 3,
+      max: currentCharacterCustomConfig?.maxCharacterNum ?? 3,
       errorMessage:
-          "最多选择${currentCharacterCustomConfig.value?.maxCharacterNum ?? 3}个特质",
+          "最多选择${currentCharacterCustomConfig?.maxCharacterNum ?? 3}个特质",
     );
   }
 
+  void clickHistory() {
+    AtmobLog.d(tag, "clickHistory");
+    CharacterCustomListPage.start();
+  }
+
   ///标签选择处理
   void handleSelection({
-    required String name,
-    required RxList<String> selectedList,
+    required dynamic name,
+    required RxList<dynamic> selectedList,
     required int max,
     required String errorMessage,
   }) {

+ 23 - 14
lib/module/character_custom/character_custom_page.dart

@@ -63,13 +63,22 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
                   ),
                 ),
               ),
-              Opacity(
-                opacity: 0.80,
+              GestureDetector(
+                onTap: () {
+                  controller.clickHistory();
+                },
                 child: Container(
                   width: 76.r,
                   height: 32.h,
                   decoration: ShapeDecoration(
-                    color: const Color(0xFF400164),
+                    gradient: LinearGradient(
+                      colors: [
+                        const Color(0xFF702E96),
+                        const Color(0XFF400264),
+                      ],
+                      begin: Alignment.centerLeft,
+                      end: Alignment.centerRight,
+                    ),
                     shape: RoundedRectangleBorder(
                       borderRadius: BorderRadius.only(
                         topLeft: Radius.circular(16.r),
@@ -258,9 +267,9 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
   Widget _buildSelectionPage({
     required String title,
     required String subtitle,
-    required RxList<dynamic> items,
-    required RxList<String> selectedLabels,
-    required Function(String) onSelected,
+    required List<dynamic> items,
+    required RxList<dynamic> selectedLabels,
+    required Function(dynamic) onSelected,
     required bool isCustomEnabled,
     required VoidCallback onCustomClick,
     required VoidCallback nextClick,
@@ -300,7 +309,7 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
                   final emoji = item.emoji ?? "";
                   final name = item.name ?? "";
                   return Obx(() {
-                    bool isSelected = selectedLabels.contains(name);
+                    bool isSelected = selectedLabels.contains(item);
                     return Stack(
                       children: [
                         ChoiceChip(
@@ -332,7 +341,7 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
                             borderRadius: BorderRadius.circular(31.r),
                           ),
                           onSelected: (selected) {
-                            onSelected(name);
+                            onSelected(item);
                           },
                         ),
                       ],
@@ -424,7 +433,7 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
     return _buildSelectionPage(
       title: StringName.characterCustomHobbiesTitle,
       subtitle:
-          "(最多选择${controller.currentCharacterCustomConfig.value?.maxHobbyNum ?? 3}个)",
+          "(最多选择${controller.currentCharacterCustomConfig?.maxHobbyNum ?? 3}个)",
       items: controller.hobbiesLabelsList,
       selectedLabels: controller.hobbiesSelectLabels,
       isShowEmoji: true,
@@ -432,7 +441,7 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
         controller.selectHobby(name);
       },
       isCustomEnabled:
-          controller.currentCharacterCustomConfig.value?.customHobby == true,
+          controller.currentCharacterCustomConfig?.customHobby == true,
       onCustomClick: () {
         controller.clickHobbiesCustom();
       },
@@ -447,15 +456,15 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
     return _buildSelectionPage(
       title: StringName.characterCustomcharacterTitle,
       subtitle:
-          "(最多选择${controller.currentCharacterCustomConfig.value?.maxCharacterNum ?? 3}个)",
+          "(最多选择${controller.currentCharacterCustomConfig?.maxCharacterNum ?? 3}个)",
       items: controller.characterLabelsList,
       selectedLabels: controller.characterSelectLabels,
       isShowEmoji: false,
-      onSelected: (name) {
-        controller.selectCharacter(name);
+      onSelected: (character) {
+        controller.selectCharacter(character);
       },
       isCustomEnabled:
-          controller.currentCharacterCustomConfig.value?.customCharacter ==
+          controller.currentCharacterCustomConfig?.customCharacter ==
           true,
       onCustomClick: () {
         controller.clickCharacterCustom();

+ 117 - 5
lib/module/character_custom/detail/character_custom_detail_controller.dart

@@ -1,17 +1,110 @@
 import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
+import 'package:keyboard/data/bean/custom_config_info.dart';
 import 'package:keyboard/data/repository/characters_repository.dart';
 import 'package:get/get.dart';
+import 'package:keyboard/data/repository/config_repository.dart';
+import 'package:keyboard/utils/atmob_log.dart';
 
+// 定制人设详情页
 @injectable
 class CharacterCustomDetailController extends BaseController {
+  final String tag = 'CharacterCustomDetailController';
+
   final CharactersRepository charactersRepository;
 
-  CharacterCustomDetailController(this.charactersRepository);
+  final ConfigRepository configRepository;
+
+  CustomConfigInfo? get currentCharacterCustomConfig =>
+      configRepository.characterCustomConfig;
+
+  final RxList<Hobbies> _hobbiesSelectLabels = <Hobbies>[].obs;
+
+  List<Hobbies> get hobbiesSelectLabels => _hobbiesSelectLabels.toList();
+
+  final RxList<CharactersList> _characterSelectLabels = <CharactersList>[].obs;
+
+  List<CharactersList> get characterSelectLabels =>
+      _characterSelectLabels.toList();
+
+  final RxString _characterCustomName = "".obs;
+
+  String get characterCustomName => _characterCustomName.value;
+
+  final RxInt _gender = 1.obs;
+
+  final RxString _birthday = "".obs;
+
+  final RxString _avatarUrl = "".obs;
+
+  String get avatarUrl => _avatarUrl.value;
+
+  final List<String> _boyAvatars = [];
+
+  final List<String> _girlAvatars = [];
+
+  CharacterCustomDetailController(
+    this.charactersRepository,
+    this.configRepository,
+  );
 
   @override
   void onInit() {
+    _getArgs();
+
     super.onInit();
+    initData();
+  }
+
+  void initData() {
+    AtmobLog.d(tag, "initData");
+    _boyAvatars.addAll(currentCharacterCustomConfig?.boyAvatars ?? []);
+    _girlAvatars.addAll(currentCharacterCustomConfig?.girlAvatars ?? []);
+    if (_gender.value == 1) {
+      _avatarUrl.value = _boyAvatars[0];
+    } else {
+      _avatarUrl.value = _girlAvatars[0];
+    }
+  }
+
+  void nextAvatar() {
+    AtmobLog.d(tag, "nextAvatar");
+
+    if (_gender.value == 1) {
+      int currentIndex = _boyAvatars.indexOf(_avatarUrl.value);
+      _avatarUrl.value = _boyAvatars[(currentIndex + 1) % _boyAvatars.length];
+    } else {
+      int currentIndex = _girlAvatars.indexOf(_avatarUrl.value);
+      _avatarUrl.value = _girlAvatars[(currentIndex + 1) % _girlAvatars.length];
+    }
+
+  }
+
+  void _getArgs() {
+    final arguments = Get.arguments as Map<String, dynamic>?;
+
+    if (arguments?['hobbiesSelectLabels'] == null) {
+      AtmobLog.i(tag, '没有传递 hobbiesSelectLabels 参数');
+    } else {
+      _hobbiesSelectLabels.assignAll(arguments?['hobbiesSelectLabels'] ?? []);
+      AtmobLog.i(tag, "hobbiesSelectLabels: $hobbiesSelectLabels");
+    }
+
+    if (arguments?['characterSelectLabels'] == null) {
+      AtmobLog.i(tag, '没有传递 characterSelectLabels 参数');
+    } else {
+      _characterSelectLabels.assignAll(
+        arguments?['characterSelectLabels'] ?? [],
+      );
+      AtmobLog.i(tag, "characterSelectLabels: $characterSelectLabels");
+    }
+
+    if (arguments?['characterCustomName'] == null) {
+      AtmobLog.i(tag, '警告: 没有传递 characterCustomName 参数');
+    } else {
+      _characterCustomName(arguments?['characterCustomName'] ?? '');
+      AtmobLog.i(tag, "characterCustomName: $characterCustomName");
+    }
   }
 
   @override
@@ -29,12 +122,31 @@ class CharacterCustomDetailController extends BaseController {
   }
 
   void clickUnlockButton() {
-
+    AtmobLog.d(tag, "点击解锁按钮,生成专属人设");
+    generateCharacterCustom();
   }
 
   // 生成定制人设
-  Future<void>generateCharacterCustom() async {
-
-
+  Future<void> generateCharacterCustom() async {
+    try {
+      await charactersRepository.generateCharacterCustom(
+        name: _characterCustomName.value,
+        gender: 1,
+        hobbies:
+            _hobbiesSelectLabels
+                .map((hobby) => hobby.name)
+                .whereType<String>()
+                .toList(),
+        characters:
+            _characterSelectLabels
+                .map((character) => character.name)
+                .whereType<String>()
+                .toList(),
+        birthday: _birthday.value,
+        imageUrl: _avatarUrl.value,
+      );
+    } catch (e) {
+      AtmobLog.e(tag, "生成专属人设失败: $e");
+    }
   }
 }

+ 99 - 61
lib/module/character_custom/detail/character_custom_detail_page.dart

@@ -1,6 +1,8 @@
+import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:keyboard/base/base_page.dart';
 import 'package:flutter/material.dart';
+import '../../../data/bean/custom_config_info.dart';
 import '../../../resource/assets.gen.dart';
 import '../../../resource/string.gen.dart';
 import '../../../router/app_pages.dart';
@@ -12,8 +14,19 @@ class CharacterCustomDetailPage
     extends BasePage<CharacterCustomDetailController> {
   const CharacterCustomDetailPage({super.key});
 
-  static void start() {
-    Get.toNamed(RoutePath.characterCustomDetail, arguments: {});
+  static void start({
+    List<Hobbies>? hobbiesSelectLabels,
+    List<CharactersList>? characterSelectLabels,
+    String? characterCustomName,
+  }) {
+    Get.toNamed(
+      RoutePath.characterCustomDetail,
+      arguments: {
+        'hobbiesSelectLabels': hobbiesSelectLabels,
+        'characterSelectLabels': characterSelectLabels,
+        'characterCustomName': characterCustomName,
+      },
+    );
   }
 
   @override
@@ -103,7 +116,7 @@ class CharacterCustomDetailPage
         crossAxisAlignment: CrossAxisAlignment.end,
         children: [
           Text(
-            '自定义人设',
+            controller.characterCustomName,
             textAlign: TextAlign.center,
             style: TextStyle(
               color: Colors.black.withAlpha(204),
@@ -123,22 +136,28 @@ class CharacterCustomDetailPage
   }
 
   _buildAvatar() {
-    return Container(
+    return GestureDetector(
+        onTap: controller.nextAvatar,
+        child: Container(
       width: 72.r,
       height: 72.r,
       decoration: ShapeDecoration(
         shape: OvalBorder(side: BorderSide(width: 2, color: Colors.white)),
       ),
-    );
+      child:CachedNetworkImage(imageUrl: controller.avatarUrl,width: 72.r,height: 72.r,),
+    ));
   }
 
   _buildAvatarSwitch() {
-    return SizedBox(
-      width: 22.r,
-      height: 22.r,
-      child: Assets.images.iconCharacterCustomDetailSwitch.image(
+    return GestureDetector(
+      onTap: controller.nextAvatar,
+      child: SizedBox(
         width: 22.r,
         height: 22.r,
+        child: Assets.images.iconCharacterCustomDetailSwitch.image(
+          width: 22.r,
+          height: 22.r,
+        ),
       ),
     );
   }
@@ -184,63 +203,26 @@ class CharacterCustomDetailPage
   }
 
   Widget _hobbiesCard() {
-    return _buildListItem(
-      onTap: () {
-        debugPrint('点击了爱好');
+
+    return _buildTagCard(
+      title: '兴趣爱好',
+      labels: controller.hobbiesSelectLabels,
+      isShowEmoji: true,
+      onTap:  () {
+        debugPrint('点击了兴趣爱好');
       },
-      firstWidget: Text('兴趣爱好', style: Styles.getTextStyleBlack204W400(14.sp)),
-      bottomWidget: Row(
-        children: [
-          Expanded(
-            child: Wrap(
-              spacing: 8.w,
-              runSpacing: 8.h,
-              children: [
-                _buildColorTag(
-                  color: Color(0xFFFEECE0),
-                  name: '45sssssssssssssssssss摄影',
-                ),
-                _buildColorTag(
-                  color: Color(0xFFE8E4FC),
-                  name: '摄影摄影摄影摄影摄影摄摄影摄影摄影',
-                ),
-                _buildColorTag(
-                  color: Color(0xFFE8E4FC),
-                  name: '摄影摄影摄影摄影摄影摄摄影摄影摄影',
-                ),
-              ],
-            ),
-          ),
-          SizedBox(width: 8.w),
-          Assets.images.iconArrowRight.image(width: 24.w, height: 24.w),
-        ],
-      ),
     );
   }
 
   Widget _characterCard() {
-    return _buildListItem(
-      onTap: () {
+
+    return _buildTagCard(
+      title: '性格',
+      labels: controller.characterSelectLabels,
+      isShowEmoji: false,
+      onTap:  () {
         debugPrint('点击了性格');
       },
-      firstWidget: Text('性格', style: Styles.getTextStyleBlack204W400(14.sp)),
-      bottomWidget: Row(
-        children: [
-          Expanded(
-            child: Wrap(
-              spacing: 8.w,
-              runSpacing: 8.h,
-              children: [
-                _buildColorTag(color: Color(0xFFFEECE0), name: '45摄影'),
-                _buildColorTag(color: Color(0xFFE8E4FC), name: '46摄影'),
-                _buildColorTag(color: Color(0xFFE0EDFD), name: '测试一共有多长才可以'),
-              ],
-            ),
-          ),
-          SizedBox(width: 8.w),
-          Assets.images.iconArrowRight.image(width: 24.w, height: 24.w),
-        ],
-      ),
     );
   }
 
@@ -332,7 +314,63 @@ class CharacterCustomDetailPage
     );
   }
 
-  Widget _buildColorTag({required Color color, String? name, String? emoji}) {
+
+  Widget _buildTagCard({
+    required String title,
+    required List<dynamic> labels,
+    required bool isShowEmoji,
+     VoidCallback? onTap,
+  }) {
+    return _buildListItem(
+      onTap: onTap,
+      firstWidget: Text(title, style: Styles.getTextStyleBlack204W400(14.sp)),
+      bottomWidget: Row(
+        children: [
+          Expanded(
+            child: Wrap(
+              spacing: 8.w,
+              runSpacing: 8.h,
+              children: labels.asMap().entries.map((entry) {
+                int index = entry.key;
+                var label = entry.value;
+
+                // 根据索引设置不同的颜色
+                Color color;
+                switch (index) {
+                  case 0:
+                    color = Color(0xFFFEECE0);
+                    break;
+                  case 1:
+                    color = Color(0xFFE8E4FC);
+                    break;
+                  case 2:
+                    color = Color(0xFFE8F8E0);
+                    break;
+                  default:
+                    color = Color(0xFFFEECE0);
+                }
+                return _buildColorTag(
+                  color: color,
+                  name: label.name,
+                  emoji: label.emoji,
+                  isShowEmoji: isShowEmoji,
+                );
+              }).toList(),
+            ),
+          ),
+          SizedBox(width: 8.w),
+          Assets.images.iconArrowRight.image(width: 24.w, height: 24.w),
+        ],
+      ),
+    );
+  }
+
+  Widget _buildColorTag({
+    required Color color,
+    String? name,
+    String? emoji,
+    bool isShowEmoji = true,
+  }) {
     return Container(
       padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 3.h),
       decoration: ShapeDecoration(
@@ -346,7 +384,7 @@ class CharacterCustomDetailPage
         mainAxisAlignment: MainAxisAlignment.center,
         crossAxisAlignment: CrossAxisAlignment.center,
         children: [
-          if (emoji != null)
+          if (emoji != null&&isShowEmoji)
             Text(emoji, style: Styles.getTextStyleBlack204W400(14.sp)),
           if (emoji != null && name != null) SizedBox(width: 4.w),
           if (name != null)

+ 138 - 0
lib/module/character_custom/list/character_custom_list_controller.dart

@@ -0,0 +1,138 @@
+import 'package:easy_refresh/easy_refresh.dart';
+import 'package:injectable/injectable.dart';
+import 'package:keyboard/base/base_controller.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/data/repository/characters_repository.dart';
+import 'package:keyboard/data/repository/keyboard_repository.dart';
+import 'package:keyboard/utils/toast_util.dart';
+
+import '../../../data/bean/character_info.dart';
+import '../../../data/bean/keyboard_info.dart';
+import '../../../data/consts/error_code.dart';
+import '../../../dialog/character_details_dialog.dart';
+import '../../../resource/string.gen.dart';
+import '../../../utils/atmob_log.dart';
+import '../../../utils/http_handler.dart';
+
+@injectable
+class CharacterCustomListController extends BaseController {
+  final tag = "CharacterCustomListController";
+
+  final RxInt _currentPage = 1.obs;
+
+  final RxInt _currentListCount = 0.obs;
+
+  final RxList<CharacterInfo> _characterList = <CharacterInfo>[].obs;
+
+  List<CharacterInfo> get characterList => _characterList;
+
+  final Rx<KeyboardInfo> _currentKeyboardInfo = KeyboardInfo().obs;
+
+  final CharactersRepository charactersRepository;
+
+  final KeyboardRepository keyboardRepository;
+
+  late EasyRefreshController refreshController;
+
+  CharacterCustomListController(
+    this.charactersRepository,
+    this.keyboardRepository,
+  );
+
+  @override
+  void onInit() {
+    super.onInit();
+    refreshController = EasyRefreshController(
+      controlFinishLoad: true,
+      controlFinishRefresh: true,
+    );
+    // 等待页面渲染完成后再加载数据
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      refreshData();
+    });
+  }
+
+  // 下拉刷新
+  Future<void> refreshData() async {
+    _currentPage.value = 1;
+    await getCurrentCharacterListInfo(isRefresh: true);
+    refreshController.finishRefresh();
+    refreshController.resetFooter(); // 允许加载更多
+  }
+
+  // 上拉加载更多
+  Future<void> loadMoreData() async {
+    if (characterList.length >= _currentListCount.value) {
+      refreshController.finishLoad(IndicatorResult.noMore);
+      return;
+    }
+    _currentPage.value++;
+    await getCurrentCharacterListInfo(isRefresh: false);
+    refreshController.finishLoad(IndicatorResult.success);
+  }
+
+  // 获取角色列表
+  Future<void> getCurrentCharacterListInfo({bool isRefresh = false}) async {
+    var response = await charactersRepository.getCustomCharactersPage(
+      pageSize: 10,
+      page: _currentPage.value,
+      keyboardId: _currentKeyboardInfo.value.id.toString(),
+    );
+    if (response.characterInfos != null) {
+      if (isRefresh) {
+        _characterList.value = response.characterInfos!;
+      } else {
+        _characterList.addAll(response.characterInfos!);
+      }
+      if (response.count != null) {
+        _currentListCount.value = response.count!;
+      }
+    }
+  }
+
+  void itemButtonClick(CharacterInfo characterInfo) async {
+    AtmobLog.d(tag, 'characterInfo ${characterInfo.toJson()} ');
+    try {
+      if (characterInfo.id != null) {
+        await charactersRepository.addCustomCharacter(
+          characterId: characterInfo.id!,
+          keyboardId: _currentKeyboardInfo.value.id.toString(),
+        );
+      }
+
+      ToastUtil.show("使用成功");
+      Get.back(result: characterInfo);
+    } catch (error) {
+      if (error is ServerErrorException) {
+        ToastUtil.show(error.message);
+      }
+    }
+  }
+
+  // 删除定制人设
+  void itemDeleteClick(CharacterInfo characterInfo) async {
+    AtmobLog.d(tag, 'characterInfo ${characterInfo.toJson()} ');
+    if (characterInfo.isAdd == true) {
+      ToastUtil.show("使用中,不可删除");
+    }
+    String? characterId = characterInfo.id;
+    if (characterId != null) {
+      try {
+        await charactersRepository.deleteCustomCharacter(
+          characterId: characterId,
+        );
+        _characterList.remove(characterInfo);
+        ToastUtil.show("删除成功");
+      } catch (error) {
+        if (error is ServerErrorException) {
+          ToastUtil.show(error.message);
+        }
+      }
+    }
+  }
+
+  void clickBack() {
+    Get.back();
+  }
+}

+ 307 - 0
lib/module/character_custom/list/character_custom_list_page.dart

@@ -0,0 +1,307 @@
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:easy_refresh/easy_refresh.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:flutter_slidable/flutter_slidable.dart';
+import 'package:get/get.dart';
+import 'package:keyboard/base/base_page.dart';
+import 'package:keyboard/module/character_custom/list/character_custom_list_controller.dart';
+import 'package:keyboard/resource/string.gen.dart';
+import 'package:keyboard/utils/styles.dart';
+
+import '../../../data/bean/character_info.dart';
+import '../../../resource/assets.gen.dart';
+import '../../../router/app_pages.dart';
+
+class CharacterCustomListPage extends BasePage<CharacterCustomListController> {
+  const CharacterCustomListPage({super.key});
+
+  static start() {
+    Get.toNamed(RoutePath.characterCustomList);
+  }
+
+  @override
+  immersive() {
+    return true;
+  }
+
+  @override
+  backgroundColor() {
+    return Color(0xFFF6F5FA);
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Stack(
+      children: [
+        Container(
+          child: Assets.images.bgCharacterCustomList.image(
+            width: double.infinity,
+            fit: BoxFit.fill,
+          ),
+        ),
+        SafeArea(
+          child: Container(
+            color: Colors.transparent,
+            alignment: Alignment.topCenter,
+            child: Stack(
+              children: [
+                Column(
+                  children: [
+                    _buildTitle(),
+                    SizedBox(height: 26.h),
+                    Expanded(
+                      child: Obx(() {
+                        return EasyRefresh(
+                          controller: controller.refreshController,
+                          header: const ClassicHeader(),
+                          footer: ClassicFooter(
+                            noMoreText: StringName.noMoreData,
+                            failedText: StringName.loadFailed,
+                            processedText: StringName.loadCompleted,
+                            processingText: StringName.loading,
+                          ),
+
+                          // onRefresh: controller.refreshData,
+                          onLoad: controller.loadMoreData,
+                          child: ListView.separated(
+                            itemCount: controller.characterList.length,
+                            itemBuilder: (context, index) {
+                              return _buildListItem(
+                                characterInfo: controller.characterList[index],
+                              );
+                            },
+                            separatorBuilder: (
+                              BuildContext context,
+                              int index,
+                            ) {
+                              return SizedBox(
+                                width: double.infinity,
+                                height: 10.h,
+                                child: Container(
+                                  color: const Color(0xFFF4F2FB),
+                                ),
+                              );
+                            },
+                          ),
+                        );
+                      }),
+                    ),
+                  ],
+                ),
+              ],
+            ),
+          ),
+        ),
+      ],
+    );
+  }
+
+  _buildTitle() {
+    return Container(
+      alignment: Alignment.centerLeft,
+      padding: EdgeInsets.only(top: 12.h, left: 16.w, right: 16.w),
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.spaceBetween,
+        children: [
+          GestureDetector(
+            onTap: controller.clickBack,
+            child: Assets.images.iconMineBackArrow.image(
+              width: 24.w,
+              height: 24.w,
+            ),
+          ),
+          Text(
+            StringName.characterCustomHistory,
+            style: Styles.getTextStyleBlack204W500(17.sp),
+          ),
+          SizedBox(),
+        ],
+      ),
+    );
+  }
+
+  // 构建列表项
+  Widget _buildListItem({required CharacterInfo characterInfo}) {
+    return GestureDetector(
+      onTap: () {
+        // controller.itemButtonClick(characterInfo);
+      },
+      child: Container(
+        margin: EdgeInsets.symmetric(horizontal: 16.w),
+        decoration: ShapeDecoration(
+          color: Colors.white,
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(12.r),
+          ),
+        ),
+        height: 88.h,
+        child: ClipRect(
+          child: Slidable(
+            key: ValueKey(characterInfo.id),
+            endActionPane: ActionPane(
+              extentRatio: 0.18,
+              motion: StretchMotion(),
+
+              children: [
+                CustomSlidableAction(
+                  padding: EdgeInsets.zero,
+                  borderRadius: BorderRadius.only(
+                    topRight: Radius.circular(12.r),
+                    bottomRight: Radius.circular(12.r),
+                  ),
+                  backgroundColor: Colors.red,
+                  onPressed: (buildContext) {
+                    controller.itemDeleteClick(characterInfo);
+                  },
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.center,
+                    mainAxisAlignment: MainAxisAlignment.center,
+                    children: [
+                      Assets.images.iconCharacterCustomDelete.image(
+                        width: 20.r,
+                        height: 20.r,
+                      ),
+                      Text(
+                        StringName.delete,
+                        softWrap: false,
+                        style: Styles.getTextStyleWhiteW400(13.sp),
+                      ),
+                    ],
+                  ),
+                ),
+              ],
+            ),
+            child: Container(
+              padding: EdgeInsets.symmetric(horizontal: 16.w),
+              child: Row(
+                children: [
+                  _buildAvatar(imageUrl: characterInfo.imageUrl),
+                  SizedBox(width: 8.w),
+                  _buildCharacterInfo(characterInfo),
+                  _buildActionButton(characterInfo),
+                ],
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  /// 角色头像
+  Widget _buildAvatar({required String? imageUrl}) {
+    return Container(
+      width: 60.r,
+      height: 60.r,
+      decoration: BoxDecoration(
+        borderRadius: BorderRadius.circular(8),
+        gradient: LinearGradient(
+          begin: Alignment.topCenter,
+          end: Alignment.bottomCenter,
+          colors: [Color(0xffebe6ff), Color(0xffffe6fe)],
+        ),
+      ),
+      child: CachedNetworkImage(
+        imageUrl: imageUrl ?? "",
+        width: 60.r,
+        height: 60.r,
+        fit: BoxFit.cover,
+      ),
+    );
+  }
+
+  /// 构建角色信息,包括名称、VIP标识和描述
+  Widget _buildCharacterInfo(CharacterInfo characterInfo) {
+    return Expanded(
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            children: [
+              Text(
+                characterInfo.name ?? "",
+                style: TextStyle(
+                  color: Colors.black.withAlpha(204),
+                  fontSize: 15.sp,
+                  fontWeight: FontWeight.w500,
+                ),
+              ),
+              SizedBox(width: 4.w),
+              characterInfo.isVip == true
+                  ? Assets.images.iconCharacterVip.image(
+                    width: 38.w,
+                    height: 16.h,
+                  )
+                  : Container(),
+            ],
+          ),
+          Text(
+            characterInfo.description ?? "",
+            softWrap: true,
+            style: TextStyle(
+              color: Colors.black.withAlpha(153),
+              fontSize: 12.sp,
+              fontWeight: FontWeight.w400,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  ///  按钮
+  Widget _buildActionButton(CharacterInfo characterInfo) {
+    return InkWell(
+      onTap: () {
+        controller.itemButtonClick(characterInfo);
+      },
+      child: Container(
+        width: 72.w,
+        height: 28.h,
+        margin: EdgeInsets.only(left: 8.w),
+        decoration: BoxDecoration(
+          borderRadius: BorderRadius.circular(50.r),
+          gradient:
+              characterInfo.isAdd == true
+                  ? null
+                  : const LinearGradient(
+                    colors: [Color(0xFF7D46FC), Color(0xFFBC87FF)],
+                    begin: Alignment.topLeft,
+                    end: Alignment.bottomRight,
+                  ),
+          color: characterInfo.isAdd == true ? const Color(0xFFEDE8FF) : null,
+        ),
+        child: Row(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            if (characterInfo.isLock == true && characterInfo.isVip == true)
+              Padding(
+                padding: EdgeInsets.only(right: 2.w),
+                child: Assets.images.iconCharacterLock.image(
+                  width: 18.r,
+                  height: 18.r,
+                ), // 锁定图标
+              ),
+            Text(
+              characterInfo.isLock == true && characterInfo.isVip == true
+                  ? StringName.characterUnlock
+                  : characterInfo.isAdd == true
+                  ? StringName.characterAdded
+                  : StringName.characterAdd,
+              style: TextStyle(
+                color:
+                    characterInfo.isAdd == true
+                        ? const Color(0xFF7D46FC)
+                        : Colors.white,
+                fontSize: 14.sp,
+                fontWeight: FontWeight.w500,
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 20 - 20
lib/module/keyboard_manage/keyboard_manage_controller.dart

@@ -26,12 +26,12 @@ class KeyboardManageController extends BaseController
   // 自定义键盘列表
   final RxList<KeyboardInfo> _customKeyboardInfoList = RxList();
 
-  RxList<KeyboardInfo> get customKeyboardInfoList => _customKeyboardInfoList;
+  List<KeyboardInfo> get customKeyboardInfoList => _customKeyboardInfoList;
 
   // 当前自定义键盘
   final Rx<KeyboardInfo> _currentCustomKeyboardInfo = KeyboardInfo().obs;
 
-  Rx<KeyboardInfo> get currentCustomKeyboardInfo => _currentCustomKeyboardInfo;
+  KeyboardInfo get currentCustomKeyboardInfo => _currentCustomKeyboardInfo.value;
 
   //当前自定义键盘人设列表
   final RxList<CharacterInfo> _currentCustomKeyboardCharacterList = RxList();
@@ -42,17 +42,17 @@ class KeyboardManageController extends BaseController
   // 当前自定义键盘亲密度
   final RxInt _currentCustomIntimacy = 0.obs;
 
-  RxInt get currentCustomIntimacy => _currentCustomIntimacy;
+  int get currentCustomIntimacy => _currentCustomIntimacy.value;
 
   // 当前定制亲密度是否有变化
   final RxBool _customIntimacyChanged = false.obs;
 
-  RxBool get customIntimacyChanged => _customIntimacyChanged;
+  bool get customIntimacyChanged => _customIntimacyChanged.value;
 
   final RxBool _customKeyboardCharacterListChanged = false.obs;
 
-  RxBool get customKeyboardCharacterListChanged =>
-      _customKeyboardCharacterListChanged;
+  bool get customKeyboardCharacterListChanged =>
+      _customKeyboardCharacterListChanged.value;
 
   // 存储排序前的定制人设列表,用于比较是否有变化
   late List<CharacterInfo> _oldCustomCharacterList;
@@ -60,24 +60,24 @@ class KeyboardManageController extends BaseController
   // 通用键盘列表
   final RxList<KeyboardInfo> _generalKeyboardInfoList = RxList();
 
-  RxList<KeyboardInfo> get generalKeyboardInfoList => _generalKeyboardInfoList;
+  List<KeyboardInfo> get generalKeyboardInfoList => _generalKeyboardInfoList;
 
   // 当前通用键盘
   final Rx<KeyboardInfo> _currentGeneralKeyboardInfo = KeyboardInfo().obs;
 
-  Rx<KeyboardInfo> get currentGeneralKeyboardInfo =>
-      _currentGeneralKeyboardInfo;
+  KeyboardInfo get currentGeneralKeyboardInfo =>
+      _currentGeneralKeyboardInfo.value;
 
   // 当前通用键盘人设列表
   final RxList<CharacterInfo> _currentGeneralKeyboardCharacterList = RxList();
 
-  RxList<CharacterInfo> get currentGeneralKeyboardCharacterList =>
+  List<CharacterInfo> get currentGeneralKeyboardCharacterList =>
       _currentGeneralKeyboardCharacterList;
 
   // 当前通用键盘亲密度
   final RxInt _currentGeneralIntimacy = 0.obs;
 
-  RxInt get currentGeneralIntimacy => _currentGeneralIntimacy;
+  int get currentGeneralIntimacy => _currentGeneralIntimacy.value;
 
   // 当前通用亲密度是否有变化
   final RxBool _generalIntimacyChanged = false.obs;
@@ -86,8 +86,8 @@ class KeyboardManageController extends BaseController
 
   final RxBool _generalKeyboardCharacterListChanged = false.obs;
 
-  RxBool get generalKeyboardCharacterListChanged =>
-      _generalKeyboardCharacterListChanged;
+  bool get generalKeyboardCharacterListChanged =>
+      _generalKeyboardCharacterListChanged.value;
 
   // 存储排序前的通用人设列表,用于比较是否有变化
   late List<CharacterInfo> _oldGeneralCharacterList;
@@ -214,7 +214,7 @@ class KeyboardManageController extends BaseController
           tag,
           'keyboardCharacterListResponse: ${keyboardCharacterListResponse.characterInfos.toString()}',
         );
-        _currentCustomKeyboardCharacterList.addAll(
+        _currentCustomKeyboardCharacterList.assignAll(
           keyboardCharacterListResponse.characterInfos,
         );
         _oldCustomCharacterList = List<CharacterInfo>.from(
@@ -357,13 +357,13 @@ class KeyboardManageController extends BaseController
   void saveCustomKeyboardCharacterList() {
     if (_customIntimacyChanged.value) {
       AtmobLog.i(tag, 'clickSave intimacyChanged');
-      _currentCustomKeyboardInfo.value.intimacy = currentCustomIntimacy.value;
+      _currentCustomKeyboardInfo.value.intimacy = currentCustomIntimacy;
       String? keyboardId = _currentCustomKeyboardInfo.value.id;
       if (keyboardId != null) {
         keyboardRepository
             .updateKeyboardInfo(
               keyboardId: keyboardId,
-              intimacy: currentCustomIntimacy.value,
+              intimacy: currentCustomIntimacy,
             )
             .then((value) {
               ToastUtil.show(StringName.keyboardSaveSuccess);
@@ -413,13 +413,13 @@ class KeyboardManageController extends BaseController
   void saveGeneralKeyboardCharacterList() {
     if (_generalIntimacyChanged.value) {
       AtmobLog.i(tag, 'clickSave intimacyChanged');
-      _currentGeneralKeyboardInfo.value.intimacy = currentGeneralIntimacy.value;
+      _currentGeneralKeyboardInfo.value.intimacy = currentGeneralIntimacy;
       String? keyboardId = _currentGeneralKeyboardInfo.value.id;
       if (keyboardId != null) {
         keyboardRepository
             .updateKeyboardInfo(
               keyboardId: keyboardId,
-              intimacy: currentGeneralIntimacy.value,
+              intimacy: currentGeneralIntimacy,
             )
             .then((value) {
               ToastUtil.show(StringName.keyboardSaveSuccess);
@@ -480,7 +480,7 @@ class KeyboardManageController extends BaseController
   clickAddCharacter({required bool isCustom}) {
     if (isCustom) {
       CharacterAddDialog.show(
-        currentKeyboardInfo: currentCustomKeyboardInfo.value,
+        currentKeyboardInfo: currentCustomKeyboardInfo,
         clickCallback: () {
           getKeyboardCharacterList(
             keyboardId: _currentCustomKeyboardInfo.value.id ?? "",
@@ -491,7 +491,7 @@ class KeyboardManageController extends BaseController
       );
     } else {
       CharacterAddDialog.show(
-        currentKeyboardInfo: currentGeneralKeyboardInfo.value,
+        currentKeyboardInfo: currentGeneralKeyboardInfo,
         clickCallback: () {
           getGeneralKeyboard();
         },

+ 99 - 86
lib/module/keyboard_manage/keyboard_manage_page.dart

@@ -156,59 +156,67 @@ class KeyboardManagePage extends BasePage<KeyboardManageController> {
     return Obx(() {
       return Padding(
         padding: EdgeInsets.only(left: 16.w, top: 24.h, right: 16.w),
-        child: Row(children: [
-          Text("自己&",
+        child: Row(
+          children: [
+            Text(
+              "自己&",
               textAlign: TextAlign.center,
               style: TextStyle(
-            color: Colors.black.withAlpha(204),
-            fontSize: 16.sp,
-            fontWeight: FontWeight.w500,
-          )),
-          DropdownButton<String>(
-            underline: Container(height: 0),
-            style: TextStyle(
-              color: Colors.black.withAlpha(204),
-              fontSize: 16.sp,
-              fontWeight: FontWeight.w500,
-            ),
-            icon: Assets.images.iconCharacterArrowDown.image(
-              width: 20.r,
-              height: 20.r,
+                color: Colors.black.withAlpha(204),
+                fontSize: 16.sp,
+                fontWeight: FontWeight.w500,
+              ),
             ),
-            value: controller.currentCustomKeyboardInfo.value.name,
-            onChanged: (String? newValue) {
-              controller.switchCustomKeyboard(newValue);
-            },
+            DropdownButton<String>(
+              underline: Container(height: 0),
+              style: TextStyle(
+                color: Colors.black.withAlpha(204),
+                fontSize: 16.sp,
+                fontWeight: FontWeight.w500,
+              ),
+              icon: Assets.images.iconCharacterArrowDown.image(
+                width: 20.r,
+                height: 20.r,
+              ),
+              value: controller.currentCustomKeyboardInfo.name,
+              onChanged: (String? newValue) {
+                controller.switchCustomKeyboard(newValue);
+              },
 
-            items: List.generate(controller.customKeyboardInfoList.length, (
+              items: List.generate(controller.customKeyboardInfoList.length, (
                 index,
-                ) {
-              String? value = controller.customKeyboardInfoList[index].name;
-              return DropdownMenuItem<String>(
-                value: value,
-                child: Column(
-                  crossAxisAlignment: CrossAxisAlignment.start,
-                  mainAxisSize: MainAxisSize.min,
-                  children: [
-                    Padding(
-                      padding: EdgeInsets.symmetric(vertical: 8),
-                      child: Text(
-                        value ?? "",
-                        style: TextStyle(
-                          color: Colors.black.withAlpha(204),
-                          fontSize: 16.sp,
-                          fontWeight: FontWeight.w500,
+              ) {
+                String? value = controller.customKeyboardInfoList[index].name;
+                return DropdownMenuItem<String>(
+                  value: value,
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    mainAxisSize: MainAxisSize.min,
+                    children: [
+                      Padding(
+                        padding: EdgeInsets.symmetric(vertical: 8),
+                        child: Text(
+                          value ?? "",
+                          style: TextStyle(
+                            color: Colors.black.withAlpha(204),
+                            fontSize: 16.sp,
+                            fontWeight: FontWeight.w500,
+                          ),
                         ),
                       ),
-                    ),
-                    if (index != controller.customKeyboardInfoList.length - 1)
-                      Divider(color: Color(0xFFF6F6F6), thickness: 1, height: 1),
-                  ],
-                ),
-              );
-            }),
-          ),
-        ],)
+                      if (index != controller.customKeyboardInfoList.length - 1)
+                        Divider(
+                          color: Color(0xFFF6F6F6),
+                          thickness: 1,
+                          height: 1,
+                        ),
+                    ],
+                  ),
+                );
+              }),
+            ),
+          ],
+        ),
       );
     });
   }
@@ -258,8 +266,8 @@ class KeyboardManagePage extends BasePage<KeyboardManageController> {
                 child: Obx(() {
                   return Text(
                     isCustom
-                        ? '${StringName.intimacy}${controller.currentCustomIntimacy.value}%'
-                        : '${StringName.intimacy}${controller.currentGeneralIntimacy.value}%',
+                        ? '${StringName.intimacy}${controller.currentCustomIntimacy}%'
+                        : '${StringName.intimacy}${controller.currentGeneralIntimacy}%',
                     textAlign: TextAlign.right,
                     style: TextStyle(
                       color: Colors.black.withAlpha(204),
@@ -288,9 +296,8 @@ class KeyboardManagePage extends BasePage<KeyboardManageController> {
                   return Slider(
                     value:
                         isCustom
-                            ? controller.currentCustomIntimacy.value.toDouble()
-                            : controller.currentGeneralIntimacy.value
-                                .toDouble(),
+                            ? controller.currentCustomIntimacy.toDouble()
+                            : controller.currentGeneralIntimacy.toDouble(),
 
                     divisions: 100,
                     min: 0,
@@ -348,7 +355,7 @@ class KeyboardManagePage extends BasePage<KeyboardManageController> {
                 ),
                 name: StringName.addCharacter,
                 onTap: () {
-                  controller.clickAddCharacter( isCustom: isCustom);
+                  controller.clickAddCharacter(isCustom: isCustom);
                 },
               ),
               _buildFooterItem(
@@ -445,41 +452,47 @@ class KeyboardManagePage extends BasePage<KeyboardManageController> {
   }
 
   // 添加人设按钮
-  Widget _buildFooterItem({required Widget image, required String name,void Function()? onTap}) {
-    return GestureDetector(onTap: onTap,
-        child: Container(
-      margin: EdgeInsets.only(top: 4.h),
-      child: DottedBorder(
-        color: const Color(0xFFC9C2DB),
-        // 虚线颜色
-        strokeWidth: 1.0.w,
-        // 线条宽度
-        borderType: BorderType.Rect,
-        // 圆角矩形
-        radius: Radius.circular(8.r),
-        // 圆角半径
-        child: Container(
-          width: 102.w,
-          height: 38.h,
-          alignment: Alignment.center,
-          child: Row(
-            mainAxisAlignment: MainAxisAlignment.center,
-            crossAxisAlignment: CrossAxisAlignment.center,
-            children: [
-              image,
-              Text(
-                name,
-                style: TextStyle(
-                  color: const Color(0xFFC9C2DB),
-                  fontSize: 14.sp,
-                  fontWeight: FontWeight.w500,
+  Widget _buildFooterItem({
+    required Widget image,
+    required String name,
+    void Function()? onTap,
+  }) {
+    return GestureDetector(
+      onTap: onTap,
+      child: Container(
+        margin: EdgeInsets.only(top: 4.h),
+        child: DottedBorder(
+          color: const Color(0xFFC9C2DB),
+          // 虚线颜色
+          strokeWidth: 1.0.w,
+          // 线条宽度
+          borderType: BorderType.Rect,
+          // 圆角矩形
+          radius: Radius.circular(8.r),
+          // 圆角半径
+          child: Container(
+            width: 102.w,
+            height: 38.h,
+            alignment: Alignment.center,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                image,
+                Text(
+                  name,
+                  style: TextStyle(
+                    color: const Color(0xFFC9C2DB),
+                    fontSize: 14.sp,
+                    fontWeight: FontWeight.w500,
+                  ),
                 ),
-              ),
-            ],
+              ],
+            ),
           ),
         ),
       ),
-    ));
+    );
   }
 
   //   保存按钮
@@ -487,9 +500,9 @@ class KeyboardManagePage extends BasePage<KeyboardManageController> {
     return Obx(() {
       bool hasChanges =
           isCustom
-              ? (controller.customKeyboardCharacterListChanged.value ||
-                  controller.customIntimacyChanged.value)
-              : (controller.generalKeyboardCharacterListChanged.value ||
+              ? (controller.customKeyboardCharacterListChanged ||
+                  controller.customIntimacyChanged)
+              : (controller.generalKeyboardCharacterListChanged ||
                   controller.generalIntimacyChanged.value);
       return GestureDetector(
         onTap: () {

+ 10 - 0
lib/resource/assets.gen.dart

@@ -24,6 +24,10 @@ class $AssetsImagesGen {
   AssetGenImage get bgCharacterCustomHuman =>
       const AssetGenImage('assets/images/bg_character_custom_human.webp');
 
+  /// File path: assets/images/bg_character_custom_list.webp
+  AssetGenImage get bgCharacterCustomList =>
+      const AssetGenImage('assets/images/bg_character_custom_list.webp');
+
   /// File path: assets/images/bg_character_custom_steps.webp
   AssetGenImage get bgCharacterCustomSteps =>
       const AssetGenImage('assets/images/bg_character_custom_steps.webp');
@@ -87,6 +91,10 @@ class $AssetsImagesGen {
   AssetGenImage get iconCharacterCustomClose =>
       const AssetGenImage('assets/images/icon_character_custom_close.webp');
 
+  /// File path: assets/images/icon_character_custom_delete.webp
+  AssetGenImage get iconCharacterCustomDelete =>
+      const AssetGenImage('assets/images/icon_character_custom_delete.webp');
+
   /// File path: assets/images/icon_character_custom_detail_edit.webp
   AssetGenImage get iconCharacterCustomDetailEdit => const AssetGenImage(
     'assets/images/icon_character_custom_detail_edit.webp',
@@ -273,6 +281,7 @@ class $AssetsImagesGen {
     bgCharacterBoyBanner,
     bgCharacterCustomDetail,
     bgCharacterCustomHuman,
+    bgCharacterCustomList,
     bgCharacterCustomSteps,
     bgCharacterCustomStepsDesc,
     bgCharacterDialog,
@@ -289,6 +298,7 @@ class $AssetsImagesGen {
     iconCharacterArrowRight,
     iconCharacterCustomButton,
     iconCharacterCustomClose,
+    iconCharacterCustomDelete,
     iconCharacterCustomDetailEdit,
     iconCharacterCustomDetailLock,
     iconCharacterCustomDetailMale,

+ 18 - 0
lib/resource/string.gen.dart

@@ -85,6 +85,15 @@ class StringName {
   static final String customLabelNameHint = 'custom_label_name_hint'.tr; // 输入你的名字
   static final String customLabelSave = 'custom_label_save'.tr; // 保存
   static final String unlockExclusiveCharacter = 'unlock_exclusive_character'.tr; // 解锁专属人设
+  static final String gender = 'gender'.tr; // 性别
+  static final String birthday = 'birthday'.tr; // 出生日期
+  static final String hobbies = 'hobbies'.tr; // 兴趣爱好
+  static final String characterKeywords = 'character_keywords'.tr; // 人设关键词
+  static final String delete = 'delete'.tr; // 删除
+  static final String tipsDialogTitle = 'tips_dialog_title'.tr; // 温馨提示
+  static final String tipsDialogConfirm = 'tips_dialog_confirm'.tr; // 保存
+  static final String tipsDialogCancel = 'tips_dialog_cancel'.tr; // 不保存
+  static final String tipsDialogDesc = 'tips_dialog_desc'.tr; // 是否保存键盘当前的修改?
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -173,6 +182,15 @@ class StringMultiSource {
       'custom_label_name_hint': '输入你的名字',
       'custom_label_save': '保存',
       'unlock_exclusive_character': '解锁专属人设',
+      'gender': '性别',
+      'birthday': '出生日期',
+      'hobbies': '兴趣爱好',
+      'character_keywords': '人设关键词',
+      'delete': '删除',
+      'tips_dialog_title': '温馨提示',
+      'tips_dialog_confirm': '保存',
+      'tips_dialog_cancel': '不保存',
+      'tips_dialog_desc': '是否保存键盘当前的修改?',
     },
   };
 }

+ 12 - 3
lib/router/app_pages.dart

@@ -4,6 +4,7 @@ import 'package:keyboard/module/browser/browser_controller.dart';
 import 'package:keyboard/module/character/content/character_group_content_controller.dart';
 import 'package:keyboard/module/character_custom/character_custom_controller.dart';
 import 'package:keyboard/module/character_custom/detail/character_custom_detail_page.dart';
+import 'package:keyboard/module/character_custom/list/character_custom_list_controller.dart';
 import 'package:keyboard/module/feedback/feedback_controller.dart';
 import 'package:keyboard/module/keyboard/keyboard_controller.dart';
 import 'package:keyboard/module/keyboard_manage/keyboard_manage_controller.dart';
@@ -16,6 +17,7 @@ import '../module/browser/browser_page.dart';
 import '../module/character/character_controller.dart';
 import '../module/character_custom/character_custom_page.dart';
 import '../module/character_custom/detail/character_custom_detail_controller.dart';
+import '../module/character_custom/list/character_custom_list_page.dart';
 import '../module/feedback/feedback_page.dart';
 import '../module/keyboard_manage/keyboard_manage_page.dart';
 import '../module/login/login_page.dart';
@@ -37,6 +39,7 @@ abstract class RoutePath {
   static const keyboardManage = '/keyboardManage';
   static const characterCustom = '/characterCustom';
   static const characterCustomDetail = '/characterCustomDetail';
+  static const characterCustomList = '/characterCustomList';
 }
 
 class AppBinding extends Bindings {
@@ -54,8 +57,7 @@ class AppBinding extends Bindings {
     lazyPut(() => getIt.get<KeyboardManageController>());
     lazyPut(() => getIt.get<CharacterCustomController>());
     lazyPut(() => getIt.get<CharacterCustomDetailController>());
-
-
+    lazyPut(() => getIt.get<CharacterCustomListController>());
   }
 
   void lazyPut<S>(InstanceBuilderCallback<S> builder) {
@@ -71,5 +73,12 @@ final generalPages = [
   GetPage(name: RoutePath.browser, page: () => BrowserPage()),
   GetPage(name: RoutePath.keyboardManage, page: () => KeyboardManagePage()),
   GetPage(name: RoutePath.characterCustom, page: () => CharacterCustomPage()),
-  GetPage(name: RoutePath.characterCustomDetail, page: () => CharacterCustomDetailPage()),
+  GetPage(
+    name: RoutePath.characterCustomDetail,
+    page: () => CharacterCustomDetailPage(),
+  ),
+  GetPage(
+    name: RoutePath.characterCustomList,
+    page: () => CharacterCustomListPage(),
+  ),
 ];

+ 15 - 1
lib/utils/styles.dart

@@ -31,11 +31,18 @@ class Styles {
     return TextStyle(
       color: Colors.black.withAlpha(204),
       fontSize: sp,
-
       fontWeight: FontWeight.w400,
     );
   }
 
+  static TextStyle getTextStyleBlack204W500(double? sp) {
+    return TextStyle(
+      color: Colors.black.withAlpha(204),
+      fontSize: sp,
+      fontWeight: FontWeight.w500,
+    );
+  }
+
   static TextStyle getTextStyleWhiteW500(double? sp) {
     return TextStyle(
       color: Colors.white,
@@ -44,5 +51,12 @@ class Styles {
     );
   }
 
+  static TextStyle getTextStyleWhiteW400(double? sp) {
+    return TextStyle(
+      color: Colors.white,
+      fontSize: sp,
+      fontWeight: FontWeight.w400,
+    );
+  }
 
 }

+ 3 - 0
pubspec.yaml

@@ -64,6 +64,9 @@ dependencies:
   #网络图片
   cached_network_image: ^3.4.1
 
+  #左滑
+  flutter_slidable: ^4.0.0
+
   #android日志打印
   atmob_logging:
     version: ^0.0.5