feat: 增加接口封装、获取验证码接口
This commit is contained in:
parent
4e8e860810
commit
1adeb429cc
@ -1,3 +1,3 @@
|
|||||||
class ApiPath {
|
class ApiPath {
|
||||||
static const String login = "/auth/login";
|
static const String sendValidationCode = "/v1/common/sendValidationCode";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,51 +1,70 @@
|
|||||||
class ApiResponse<T> {
|
class ApiResponse<T> {
|
||||||
final bool success;
|
final String? description;
|
||||||
final T? data;
|
final T? data;
|
||||||
final String? message;
|
final String? errorMsg;
|
||||||
final int? statusCode;
|
final int? errorCode;
|
||||||
|
|
||||||
// 构造函数
|
|
||||||
const ApiResponse({
|
const ApiResponse({
|
||||||
required this.success,
|
this.description,
|
||||||
this.data,
|
this.data,
|
||||||
this.message,
|
this.errorMsg,
|
||||||
this.statusCode,
|
this.errorCode,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 成功响应
|
// ✅ 新增:从 JSON 创建 ApiResponse
|
||||||
factory ApiResponse.success(T data,
|
factory ApiResponse.fromJson(
|
||||||
{String message = 'Success', int? statusCode}) {
|
Map<String, dynamic> json,
|
||||||
|
T Function(dynamic)? dataFromJson, // 可为空,有些接口 data 是 null
|
||||||
|
) {
|
||||||
|
final dataJson = json['data'];
|
||||||
|
final T? parsedData = dataJson != null && dataFromJson != null
|
||||||
|
? dataFromJson(dataJson)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return ApiResponse<T>(
|
||||||
|
errorCode: json['errorCode'],
|
||||||
|
errorMsg: json['errorMsg'],
|
||||||
|
description: json['description'],
|
||||||
|
data: parsedData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功工厂构造
|
||||||
|
factory ApiResponse.success(T data, {
|
||||||
|
String? errorMsg,
|
||||||
|
int? errorCode,
|
||||||
|
String? description,
|
||||||
|
}) {
|
||||||
return ApiResponse<T>(
|
return ApiResponse<T>(
|
||||||
success: true,
|
|
||||||
data: data,
|
data: data,
|
||||||
message: message,
|
errorMsg: errorMsg ?? 'success',
|
||||||
statusCode: statusCode,
|
errorCode: errorCode,
|
||||||
|
description: description ?? 'success',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 失败响应
|
// 失败工厂构造
|
||||||
factory ApiResponse.error(String message, {int? statusCode, T? data}) {
|
factory ApiResponse.error(
|
||||||
|
String errorMsg, {
|
||||||
|
int? errorCode,
|
||||||
|
T? data,
|
||||||
|
String? description,
|
||||||
|
}) {
|
||||||
return ApiResponse<T>(
|
return ApiResponse<T>(
|
||||||
success: false,
|
description: description,
|
||||||
message: message,
|
errorMsg: errorMsg,
|
||||||
statusCode: statusCode,
|
errorCode: errorCode,
|
||||||
data: data, // 可选:返回部分数据(如错误时的缓存数据)
|
data: data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载中(可选)
|
bool get isSuccess {
|
||||||
factory ApiResponse.loading() {
|
return errorCode == 0;
|
||||||
return ApiResponse<T>(
|
|
||||||
success: false,
|
|
||||||
message: 'Loading...',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [success, data, message, statusCode];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ApiResponse(success: $success, message: $message, data: $data, statusCode: $statusCode)';
|
return 'ApiResponse(description: $description, errorMsg: $errorMsg, data: $data, errorCode: $errorCode)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
// api/service/user_api_service.dart
|
|
||||||
import 'package:dio/dio.dart' as dioAlias;
|
import 'package:dio/dio.dart' as dioAlias;
|
||||||
|
|
||||||
import 'package:starwork_flutter/api/api_response.dart';
|
import 'package:starwork_flutter/api/api_response.dart';
|
||||||
|
import 'package:starwork_flutter/common/constant/http_constant.dart';
|
||||||
import 'package:starwork_flutter/flavors.dart';
|
import 'package:starwork_flutter/flavors.dart';
|
||||||
|
import 'package:flutter/foundation.dart'; // 用于 debugPrint
|
||||||
|
|
||||||
class BaseApiService {
|
class BaseApiService {
|
||||||
final dioAlias.Dio dio = dioAlias.Dio();
|
final dioAlias.Dio dio = dioAlias.Dio();
|
||||||
@ -10,37 +12,62 @@ class BaseApiService {
|
|||||||
dio.options.baseUrl = F.apiHost;
|
dio.options.baseUrl = F.apiHost;
|
||||||
dio.options.connectTimeout = const Duration(seconds: 30);
|
dio.options.connectTimeout = const Duration(seconds: 30);
|
||||||
dio.options.receiveTimeout = const Duration(seconds: 30);
|
dio.options.receiveTimeout = const Duration(seconds: 30);
|
||||||
// 可添加拦截器、token 等
|
|
||||||
|
// 🔥 添加日志拦截器
|
||||||
|
if (kDebugMode) {
|
||||||
|
dio.interceptors.add(dioAlias.LogInterceptor(
|
||||||
|
request: true,
|
||||||
|
requestHeader: true,
|
||||||
|
requestBody: true,
|
||||||
|
responseHeader: true,
|
||||||
|
responseBody: true,
|
||||||
|
error: true,
|
||||||
|
logPrint: (obj) {
|
||||||
|
debugPrint('[DIO] $obj');
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 统一请求方法(私有,但子类可用)
|
/// 统一请求方法
|
||||||
Future<ApiResponse<T>> makeRequest<T>({
|
Future<ApiResponse<T>> makeRequest<T>({
|
||||||
required String path,
|
required String path,
|
||||||
String method = 'GET',
|
String method = HttpConstant.post,
|
||||||
dynamic data,
|
dynamic data,
|
||||||
Map<String, dynamic>? queryParameters,
|
Map<String, dynamic>? queryParameters,
|
||||||
required T Function(dynamic) fromJson,
|
required T Function(dynamic) fromJson,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
|
// 🔍 打印请求信息(更详细控制)
|
||||||
|
if (kDebugMode) {
|
||||||
|
final uri = Uri.parse('${dio.options.baseUrl}$path');
|
||||||
|
final queryString =
|
||||||
|
queryParameters != null ? '?${uri.queryParameters.toString()}' : '';
|
||||||
|
debugPrint('🟦 API Request: $method ${uri.toString()}$queryString');
|
||||||
|
if (data != null) {
|
||||||
|
debugPrint('🟦 Request Body: $data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dioAlias.Response response;
|
dioAlias.Response response;
|
||||||
|
|
||||||
switch (method.toUpperCase()) {
|
switch (method.toUpperCase()) {
|
||||||
case 'GET':
|
case HttpConstant.get:
|
||||||
response = await dio.get(path, queryParameters: queryParameters);
|
response = await dio.get(path, queryParameters: queryParameters);
|
||||||
break;
|
break;
|
||||||
case 'POST':
|
case HttpConstant.post:
|
||||||
response = await dio.post(path,
|
response = await dio.post(path,
|
||||||
data: data, queryParameters: queryParameters);
|
data: data, queryParameters: queryParameters);
|
||||||
break;
|
break;
|
||||||
case 'PUT':
|
case HttpConstant.put:
|
||||||
response =
|
response =
|
||||||
await dio.put(path, data: data, queryParameters: queryParameters);
|
await dio.put(path, data: data, queryParameters: queryParameters);
|
||||||
break;
|
break;
|
||||||
case 'DELETE':
|
case HttpConstant.delete:
|
||||||
response = await dio.delete(path,
|
response = await dio.delete(path,
|
||||||
data: data, queryParameters: queryParameters);
|
data: data, queryParameters: queryParameters);
|
||||||
break;
|
break;
|
||||||
case 'PATCH':
|
case HttpConstant.patch:
|
||||||
response = await dio.patch(path,
|
response = await dio.patch(path,
|
||||||
data: data, queryParameters: queryParameters);
|
data: data, queryParameters: queryParameters);
|
||||||
break;
|
break;
|
||||||
@ -48,20 +75,46 @@ class BaseApiService {
|
|||||||
return ApiResponse.error('Unsupported method: $method');
|
return ApiResponse.error('Unsupported method: $method');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ 打印响应
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint(
|
||||||
|
'🟩 API Response [${response.statusCode}] ${response.requestOptions.path}');
|
||||||
|
debugPrint('🟩 Response Data: ${response.data}');
|
||||||
|
}
|
||||||
|
|
||||||
if (response.statusCode! >= 200 && response.statusCode! < 300) {
|
if (response.statusCode! >= 200 && response.statusCode! < 300) {
|
||||||
final parsedData = fromJson(response.data);
|
final jsonMap = response.data as Map<String, dynamic>;
|
||||||
return ApiResponse.success(
|
|
||||||
parsedData,
|
// ✅ 直接用 ApiResponse.fromJson 解析整个响应
|
||||||
statusCode: response.statusCode,
|
final apiResponse = ApiResponse<T>.fromJson(jsonMap, fromJson);
|
||||||
message: 'Success',
|
|
||||||
);
|
// ✅ 判断业务是否成功(根据 errorCode)
|
||||||
|
if (apiResponse.errorCode == 0) {
|
||||||
|
return apiResponse; // 直接返回,data 已解析
|
||||||
|
} else {
|
||||||
|
// 业务失败,但 data 可能有部分信息
|
||||||
|
return ApiResponse.error(
|
||||||
|
apiResponse.errorMsg ?? 'Request failed',
|
||||||
|
errorCode: apiResponse.errorCode,
|
||||||
|
data: apiResponse.data,
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return ApiResponse.error(
|
return ApiResponse.error(
|
||||||
response.statusMessage ?? 'Request failed',
|
response.statusMessage ?? 'Request failed',
|
||||||
statusCode: response.statusCode,
|
errorCode: response.statusCode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} on dioAlias.DioException catch (e) {
|
} on dioAlias.DioException catch (e) {
|
||||||
|
// ❌ 打印错误
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint('🟥 API Error: ${e.type} - ${e.message}');
|
||||||
|
if (e.response != null) {
|
||||||
|
debugPrint('🟥 Error Response: ${e.response?.data}');
|
||||||
|
debugPrint('🟥 Status Code: ${e.response?.statusCode}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String message = 'Unknown error';
|
String message = 'Unknown error';
|
||||||
int? statusCode = e.response?.statusCode;
|
int? statusCode = e.response?.statusCode;
|
||||||
|
|
||||||
@ -77,8 +130,11 @@ class BaseApiService {
|
|||||||
message = e.message ?? 'Something went wrong';
|
message = e.message ?? 'Something went wrong';
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApiResponse.error(message, statusCode: statusCode);
|
return ApiResponse.error(message, errorCode: statusCode);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint('🟥 Unexpected Error: $e');
|
||||||
|
}
|
||||||
return ApiResponse.error('Unexpected error: $e');
|
return ApiResponse.error('Unexpected error: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
// 验证码接口请求参数
|
||||||
|
// 登陆后可使用 sendValidationCodeAuth 接口,免图片滑动验证,只需要 channel,codeType 两个参数即可
|
||||||
|
import 'package:starwork_flutter/common/constant/validation_code_type.dart';
|
||||||
|
|
||||||
|
class SendValidationCodeRequest {
|
||||||
|
final String countryCode;
|
||||||
|
final String account;
|
||||||
|
final String channel; //1 短信,2 邮箱
|
||||||
|
final ValidationCodeType codeType;
|
||||||
|
|
||||||
|
SendValidationCodeRequest({
|
||||||
|
required this.countryCode,
|
||||||
|
required this.account,
|
||||||
|
required this.channel,
|
||||||
|
required this.codeType,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'countryCode': countryCode,
|
||||||
|
'account': account,
|
||||||
|
'channel': channel,
|
||||||
|
'codeType': codeType.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
18
lib/api/model/string_response.dart
Normal file
18
lib/api/model/string_response.dart
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// models/string_response.dart
|
||||||
|
class StringResponse {
|
||||||
|
final String value;
|
||||||
|
|
||||||
|
StringResponse(this.value);
|
||||||
|
|
||||||
|
factory StringResponse.fromJson(dynamic data) {
|
||||||
|
if (data is String) {
|
||||||
|
return StringResponse(data);
|
||||||
|
} else if (data is Map && data['message'] != null) {
|
||||||
|
return StringResponse(data['message'].toString());
|
||||||
|
} else if (data is Map && data['data'] != null) {
|
||||||
|
return StringResponse(data['data'].toString());
|
||||||
|
} else {
|
||||||
|
return StringResponse(data.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
lib/api/model/user/request/validation_code_login.dart
Normal file
19
lib/api/model/user/request/validation_code_login.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
class ValidationCodeLoginRequest {
|
||||||
|
final int platId; //平台 1web 2app 3小程序 4pc
|
||||||
|
final String phone; // 登录账号(手机号)
|
||||||
|
final String verificationCode; //验证码
|
||||||
|
|
||||||
|
ValidationCodeLoginRequest({
|
||||||
|
required this.platId,
|
||||||
|
required this.phone,
|
||||||
|
required this.verificationCode,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'platId': platId,
|
||||||
|
'phone': phone,
|
||||||
|
'verificationCode': verificationCode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
25
lib/api/service/common_api_service.dart
Normal file
25
lib/api/service/common_api_service.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import 'package:starwork_flutter/api/api_path.dart';
|
||||||
|
import 'package:starwork_flutter/api/api_response.dart';
|
||||||
|
import 'package:starwork_flutter/api/base_api_service.dart';
|
||||||
|
import 'package:starwork_flutter/api/model/common/request/send_validation_code_request.dart';
|
||||||
|
import 'package:starwork_flutter/api/model/string_response.dart';
|
||||||
|
import 'package:starwork_flutter/api/model/user/user_model.dart';
|
||||||
|
import 'package:starwork_flutter/common/constant/http_constant.dart';
|
||||||
|
|
||||||
|
class CommonApiService {
|
||||||
|
final BaseApiService _api;
|
||||||
|
|
||||||
|
CommonApiService(this._api); // 通过构造函数注入
|
||||||
|
// 发送验证码
|
||||||
|
Future<ApiResponse<void>> sendValidationCode({
|
||||||
|
required SendValidationCodeRequest request,
|
||||||
|
}) {
|
||||||
|
return _api.makeRequest(
|
||||||
|
// 通过实例调用
|
||||||
|
path: ApiPath.sendValidationCode,
|
||||||
|
method: HttpConstant.post,
|
||||||
|
data: request.toJson(),
|
||||||
|
fromJson: (data) {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,9 @@ import 'package:starwork_flutter/api/base_api_service.dart';
|
|||||||
import 'package:starwork_flutter/api/model/user/user_model.dart';
|
import 'package:starwork_flutter/api/model/user/user_model.dart';
|
||||||
|
|
||||||
class UserApiService {
|
class UserApiService {
|
||||||
final BaseApiService _api = BaseApiService();
|
final BaseApiService _api;
|
||||||
|
|
||||||
|
UserApiService(this._api); // 通过构造函数注入
|
||||||
|
|
||||||
Future<ApiResponse<UserModel>> login(Map<String, dynamic> userData) {
|
Future<ApiResponse<UserModel>> login(Map<String, dynamic> userData) {
|
||||||
return _api.makeRequest(
|
return _api.makeRequest(
|
||||||
|
|||||||
13
lib/app.dart
13
lib/app.dart
@ -19,12 +19,11 @@ class App extends StatelessWidget {
|
|||||||
builder: (_, child) {
|
builder: (_, child) {
|
||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
textSelectionTheme: TextSelectionThemeData(
|
textSelectionTheme: TextSelectionThemeData(
|
||||||
cursorColor: Colors.blue.shade200,
|
cursorColor: Colors.blue.shade200,
|
||||||
selectionColor: Colors.blue.shade100,
|
selectionColor: Colors.blue.shade100,
|
||||||
selectionHandleColor: Colors.blue.shade300,
|
selectionHandleColor: Colors.blue.shade300,
|
||||||
)
|
)),
|
||||||
),
|
|
||||||
// 必须配置以下三个本地化代理
|
// 必须配置以下三个本地化代理
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
GlobalMaterialLocalizations.delegate, // 提供Material组件本地化字符串
|
GlobalMaterialLocalizations.delegate, // 提供Material组件本地化字符串
|
||||||
@ -42,7 +41,7 @@ class App extends StatelessWidget {
|
|||||||
fallbackLocale: const Locale('zh', 'CN'),
|
fallbackLocale: const Locale('zh', 'CN'),
|
||||||
// 必须设置 fallback 没有该语言时使用
|
// 必须设置 fallback 没有该语言时使用
|
||||||
getPages: AppPages.pages,
|
getPages: AppPages.pages,
|
||||||
initialRoute: AppRoutes.main,
|
initialRoute: AppRoutes.login,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,20 +5,31 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:starwork_flutter/api/base_api_service.dart';
|
import 'package:starwork_flutter/api/base_api_service.dart';
|
||||||
|
import 'package:starwork_flutter/api/service/common_api_service.dart';
|
||||||
import 'package:starwork_flutter/api/service/user_api_service.dart';
|
import 'package:starwork_flutter/api/service/user_api_service.dart';
|
||||||
import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart';
|
import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart';
|
||||||
import 'package:starwork_flutter/i18n/app_i18n.dart';
|
import 'package:starwork_flutter/i18n/app_i18n.dart';
|
||||||
|
|
||||||
class AppInitialization {
|
class AppInitialization {
|
||||||
static Future<void> initializeApp() async {
|
static Future<void> initializeApp() async {
|
||||||
// 确保绑定已初始化,能够安全使用插件
|
try {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
// 设置系统状态栏
|
setSystemStatusBar();
|
||||||
setSystemStatusBar();
|
await SharedPreferencesUtils.init();
|
||||||
// 初始化 SharedPreferences
|
|
||||||
await SharedPreferencesUtils.init();
|
// 日志:方便调试
|
||||||
// 初始化api服务(单例)
|
print('✅ SharedPreferences initialized');
|
||||||
Get.put(BaseApiService(),permanent: true);
|
|
||||||
|
Get.lazyPut(() => BaseApiService());
|
||||||
|
Get.lazyPut(() => CommonApiService(Get.find<BaseApiService>()));
|
||||||
|
|
||||||
|
print('✅ API services registered');
|
||||||
|
} catch (e, stack) {
|
||||||
|
print('❌ Initialization failed: $e');
|
||||||
|
print(stack);
|
||||||
|
// 可以上报错误(Sentry 等)
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setSystemStatusBar() {
|
static void setSystemStatusBar() {
|
||||||
|
|||||||
8
lib/common/constant/http_constant.dart
Normal file
8
lib/common/constant/http_constant.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
class HttpConstant {
|
||||||
|
static const String post="POST";
|
||||||
|
static const String get="GET";
|
||||||
|
static const String put="PUT";
|
||||||
|
static const String delete="DELETE";
|
||||||
|
static const String patch="PATCH";
|
||||||
|
|
||||||
|
}
|
||||||
34
lib/common/constant/login_type.dart
Normal file
34
lib/common/constant/login_type.dart
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
class LoginType {
|
||||||
|
static const phoneCodeLogin = LoginType('1', '手机验证码');
|
||||||
|
static const emailCodeLogin = LoginType('2', '邮箱验证码');
|
||||||
|
static const emailPasswordLogin = LoginType('3', '邮箱验证码');
|
||||||
|
static const phonePassword = LoginType('4', '手机号密码');
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
const LoginType(this.value, this.label);
|
||||||
|
|
||||||
|
// 支持通过字符串值查找枚举实例
|
||||||
|
static LoginType? fromValue(String? value) {
|
||||||
|
return {
|
||||||
|
'1': phoneCodeLogin,
|
||||||
|
'2': phonePassword,
|
||||||
|
}[value];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持 toString() 直接输出 value
|
||||||
|
@override
|
||||||
|
String toString() => value;
|
||||||
|
|
||||||
|
// 可选:支持 == 比较
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is LoginType &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
value == other.value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => value.hashCode;
|
||||||
|
}
|
||||||
33
lib/common/constant/validation_code_channel.dart
Normal file
33
lib/common/constant/validation_code_channel.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Description: 验证码渠道枚举类
|
||||||
|
class ValidationCodeChannel {
|
||||||
|
static const sms = ValidationCodeChannel('1', '短信');
|
||||||
|
static const email = ValidationCodeChannel('2', '邮箱');
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
const ValidationCodeChannel(this.value, this.label);
|
||||||
|
|
||||||
|
// 支持通过字符串值查找枚举实例
|
||||||
|
static ValidationCodeChannel? fromValue(String? value) {
|
||||||
|
return {
|
||||||
|
'1': sms,
|
||||||
|
'2': email,
|
||||||
|
}[value];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持 toString() 直接输出 value
|
||||||
|
@override
|
||||||
|
String toString() => value;
|
||||||
|
|
||||||
|
// 可选:支持 == 比较
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is ValidationCodeChannel &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
value == other.value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => value.hashCode;
|
||||||
|
}
|
||||||
46
lib/common/constant/validation_code_type.dart
Normal file
46
lib/common/constant/validation_code_type.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
class ValidationCodeType {
|
||||||
|
static const login = ValidationCodeType('1', '登录');
|
||||||
|
static const reset = ValidationCodeType('2', '重置密码');
|
||||||
|
static const bindPhone = ValidationCodeType('3', '绑定手机号');
|
||||||
|
static const unbindPhone = ValidationCodeType('4', '解绑手机号');
|
||||||
|
static const deleteAccount = ValidationCodeType('5', '删除账号');
|
||||||
|
static const bindEmail = ValidationCodeType('6', '绑定邮箱');
|
||||||
|
static const unbindEmail = ValidationCodeType('7', '解绑邮箱');
|
||||||
|
static const deleteLock = ValidationCodeType('8', '删除门锁');
|
||||||
|
static const updatePassword = ValidationCodeType('9', '修改密码');
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
const ValidationCodeType(this.value, this.label);
|
||||||
|
|
||||||
|
// 支持通过字符串值查找枚举实例
|
||||||
|
static ValidationCodeType? fromValue(String? value) {
|
||||||
|
return {
|
||||||
|
'1': login,
|
||||||
|
'2': reset,
|
||||||
|
'3': bindPhone,
|
||||||
|
'4': unbindPhone,
|
||||||
|
'5': deleteAccount,
|
||||||
|
'6': bindEmail,
|
||||||
|
'7': unbindEmail,
|
||||||
|
'8': deleteLock,
|
||||||
|
'9': updatePassword,
|
||||||
|
}[value];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持 toString() 直接输出 value
|
||||||
|
@override
|
||||||
|
String toString() => value;
|
||||||
|
|
||||||
|
// 可选:支持 == 比较
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is ValidationCodeType &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
value == other.value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => value.hashCode;
|
||||||
|
}
|
||||||
@ -1,5 +0,0 @@
|
|||||||
enum LoginType {
|
|
||||||
phoneCode,
|
|
||||||
phonePassword;
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -32,7 +32,7 @@ class F {
|
|||||||
static String get apiHost {
|
static String get apiHost {
|
||||||
switch (appFlavor) {
|
switch (appFlavor) {
|
||||||
case Flavor.skyDev:
|
case Flavor.skyDev:
|
||||||
return 'https://loacl.work.star-lock.cn/api';
|
return 'http://192.168.1.136/api';
|
||||||
case Flavor.skyPre:
|
case Flavor.skyPre:
|
||||||
return 'https://loacl.work.star-lock.cn/api';
|
return 'https://loacl.work.star-lock.cn/api';
|
||||||
case Flavor.skyRelease:
|
case Flavor.skyRelease:
|
||||||
|
|||||||
@ -4,12 +4,19 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:starwork_flutter/api/model/common/request/send_validation_code_request.dart';
|
||||||
|
import 'package:starwork_flutter/api/service/common_api_service.dart';
|
||||||
import 'package:starwork_flutter/base/base_controller.dart';
|
import 'package:starwork_flutter/base/base_controller.dart';
|
||||||
import 'package:starwork_flutter/common/enums/login_type.dart';
|
import 'package:starwork_flutter/common/constant/login_type.dart';
|
||||||
|
import 'package:starwork_flutter/common/constant/validation_code_channel.dart';
|
||||||
|
import 'package:starwork_flutter/common/constant/validation_code_type.dart';
|
||||||
|
|
||||||
import 'package:starwork_flutter/routes/app_pages.dart';
|
import 'package:starwork_flutter/routes/app_pages.dart';
|
||||||
import 'package:starwork_flutter/routes/app_routes.dart';
|
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||||
|
|
||||||
class LoginController extends BaseController {
|
class LoginController extends BaseController {
|
||||||
|
final commonApi = Get.find<CommonApiService>();
|
||||||
|
|
||||||
int phoneNumberSize = 11;
|
int phoneNumberSize = 11;
|
||||||
TextEditingController phoneController = TextEditingController();
|
TextEditingController phoneController = TextEditingController();
|
||||||
TextEditingController passwordController = TextEditingController();
|
TextEditingController passwordController = TextEditingController();
|
||||||
@ -17,7 +24,7 @@ class LoginController extends BaseController {
|
|||||||
final isFormValid = false.obs;
|
final isFormValid = false.obs;
|
||||||
final isPasswordVisible = true.obs;
|
final isPasswordVisible = true.obs;
|
||||||
final isPrivacyAgreementValid = 0.obs;
|
final isPrivacyAgreementValid = 0.obs;
|
||||||
final loginType = LoginType.phoneCode.obs;
|
final loginType = LoginType.phoneCodeLogin.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -41,28 +48,34 @@ class LoginController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取手机验证码
|
// 获取手机验证码
|
||||||
void requestPhoneCode() {
|
void requestPhoneCode() async {
|
||||||
if (isPrivacyAgreementValid.value != 1) {
|
if (isPrivacyAgreementValid.value != 1) {
|
||||||
_showCustomDialog(
|
_showCustomDialog(
|
||||||
title: '欢迎使用星勤'.tr,
|
title: '欢迎使用星勤'.tr,
|
||||||
content: '1',
|
content: '1',
|
||||||
onConfirm: () {
|
onConfirm: () async {
|
||||||
isPrivacyAgreementValid.value = 1;
|
isPrivacyAgreementValid.value = 1;
|
||||||
Get.toNamed(
|
|
||||||
AppRoutes.inputVerificationCode,
|
|
||||||
parameters: {
|
|
||||||
'phone': phoneController.text.trim(),
|
|
||||||
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
_handleRequestPhoneCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleRequestPhoneCode() async {
|
||||||
|
var sendValidationCodeResult = await commonApi.sendValidationCode(
|
||||||
|
request: SendValidationCodeRequest(
|
||||||
|
countryCode: '+86',
|
||||||
|
account: phoneController.text.trim(),
|
||||||
|
channel: ValidationCodeChannel.sms.value,
|
||||||
|
codeType: ValidationCodeType.login,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sendValidationCodeResult.isSuccess) {
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
AppRoutes.inputVerificationCode,
|
AppRoutes.inputVerificationCode,
|
||||||
parameters: {
|
parameters: {
|
||||||
'phone': phoneController.text.trim(),
|
'phone': phoneController.text.trim(),
|
||||||
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:starwork_flutter/common/enums/login_type.dart';
|
import 'package:starwork_flutter/common/constant/login_type.dart';
|
||||||
|
|
||||||
import 'package:starwork_flutter/routes/app_routes.dart';
|
import 'package:starwork_flutter/routes/app_routes.dart';
|
||||||
|
|
||||||
import 'login_controller.dart';
|
import 'login_controller.dart';
|
||||||
@ -173,7 +174,7 @@ class LoginView extends GetView<LoginController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
controller.loginType.value == LoginType.phoneCode
|
controller.loginType.value == LoginType.phoneCodeLogin
|
||||||
? '获取验证码'.tr
|
? '获取验证码'.tr
|
||||||
: '登录'.tr,
|
: '登录'.tr,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -188,7 +189,7 @@ class LoginView extends GetView<LoginController> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: LoginType.phoneCode == controller.loginType.value,
|
visible: LoginType.phoneCodeLogin == controller.loginType.value,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller.loginType.value = LoginType.phonePassword;
|
controller.loginType.value = LoginType.phonePassword;
|
||||||
@ -208,7 +209,7 @@ class LoginView extends GetView<LoginController> {
|
|||||||
visible: LoginType.phonePassword == controller.loginType.value,
|
visible: LoginType.phonePassword == controller.loginType.value,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller.loginType.value = LoginType.phoneCode;
|
controller.loginType.value = LoginType.phoneCodeLogin;
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'验证码登录'.tr,
|
'验证码登录'.tr,
|
||||||
@ -236,7 +237,8 @@ class LoginView extends GetView<LoginController> {
|
|||||||
visible: LoginType.phonePassword == controller.loginType.value,
|
visible: LoginType.phonePassword == controller.loginType.value,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
overlayColor: MaterialStateProperty.all(Colors.transparent), // 墨水覆盖
|
overlayColor:
|
||||||
|
MaterialStateProperty.all(Colors.transparent), // 墨水覆盖
|
||||||
side: MaterialStateProperty.all(null), // 取消边框
|
side: MaterialStateProperty.all(null), // 取消边框
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -266,7 +268,7 @@ class LoginView extends GetView<LoginController> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleLoginButtonText() {
|
_handleLoginButtonText() {
|
||||||
if (controller.loginType.value == LoginType.phoneCode) {
|
if (controller.loginType.value == LoginType.phoneCodeLogin) {
|
||||||
return;
|
return;
|
||||||
} else if (controller.loginType.value == LoginType.phonePassword) {
|
} else if (controller.loginType.value == LoginType.phonePassword) {
|
||||||
return '验证码登录'.tr + ' | '.tr + '忘记密码'.tr;
|
return '验证码登录'.tr + ' | '.tr + '忘记密码'.tr;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user