merge: integrate sccsbc release preparation safely

This commit is contained in:
MingNian
2026-07-16 00:41:52 +08:00
38 changed files with 2974 additions and 45 deletions

View File

@@ -1,12 +1,15 @@
import 'dart:developer';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart' show kReleaseMode;
import 'local_database.dart';
/// API 基础地址可通过 --dart-define=API_BASE_URL=http://host:5000 覆盖。
/// API 基础地址(生产模式)。本地开发可通过 --dart-define=API_BASE_URL=http://host:5000 覆盖。
const String baseUrl = String.fromEnvironment(
'API_BASE_URL',
defaultValue: 'http://10.4.237.12:5000',
defaultValue: kReleaseMode
? 'https://erpapi.datalumina.cn/xiaomai'
: 'http://10.4.237.12:5000',
);
class ApiException implements Exception {

View File

@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import '../../core/app_colors.dart';
import '../../core/app_design_tokens.dart';
import '../../core/navigation_provider.dart';
@@ -73,6 +76,66 @@ class _LoginPageState extends ConsumerState<LoginPage> {
}
}
// Apple 登录
Future<void> _appleSignIn() async {
if (_loading) return;
if (!_agreed) {
final accepted = await _showAgreementDialog();
if (!mounted || accepted != true) return;
setState(() => _agreed = true);
}
setState(() {
_loading = true;
_error = null;
_successMsg = null;
});
try {
final credential = await SignInWithApple.getAppleIDCredential(
scopes: [AppleIDAuthorizationScopes.fullName],
);
final identityToken = credential.identityToken;
if (identityToken == null) {
if (mounted) {
setState(() {
_loading = false;
_error = 'Apple 登录未返回身份信息,请重试';
});
}
return;
}
final name = '${credential.familyName ?? ''}${credential.givenName ?? ''}'
.trim();
final err = await ref
.read(authProvider.notifier)
.appleLogin(
identityToken,
credential.authorizationCode,
name.isEmpty ? null : name,
);
if (mounted) {
setState(() => _loading = false);
if (err != null) {
setState(() => _error = err);
}
}
} catch (e) {
if (mounted) {
setState(() {
_loading = false;
if (e is SignInWithAppleAuthorizationException) {
_error = 'Apple 登录已取消';
} else {
_error = 'Apple 登录失败,请稍后重试';
}
});
}
}
}
Future<void> _submit() async {
if (_phoneCtrl.text.trim().isEmpty || _codeCtrl.text.trim().isEmpty) {
setState(() => _error = '请输入手机号和验证码');
@@ -500,6 +563,20 @@ class _LoginPageState extends ConsumerState<LoginPage> {
label: _isLogin ? '登录' : '注册',
onTap: _loading ? null : _submit,
),
if (_isLogin &&
(Platform.isIOS ||
Platform.isMacOS)) ...[
const SizedBox(height: 20),
SignInWithAppleButton(
onPressed: _loading
? () {}
: _appleSignIn,
style: SignInWithAppleButtonStyle
.whiteOutlined,
height: 48,
text: '通过 Apple 登录',
),
],
const SizedBox(height: 16),
GestureDetector(
onTap: () => setState(() {

View File

@@ -213,6 +213,44 @@ class AuthNotifier extends Notifier<AuthState> {
}
}
/// Apple 登录
Future<String?> appleLogin(
String identityToken,
String? authorizationCode,
String? name,
) async {
try {
final api = ref.read(apiClientProvider);
final response = await api.post(
'/api/auth/apple-login',
data: {
'identityToken': identityToken,
'authorizationCode': authorizationCode,
'name': name,
},
);
final data = response.data['data'];
if (data == null) return response.data['message'] ?? 'Apple 登录失败';
await api.saveTokens(data['accessToken'], data['refreshToken']);
final user = data['user'];
state = AuthState(
isLoggedIn: true,
isLoading: false,
user: UserInfo(
id: user['id'] ?? '',
phone: user['phone'] ?? '',
role: user['role'] ?? 'User',
name: user['name'],
avatarUrl: user['avatarUrl'],
),
);
return null;
} catch (_) {
return 'Apple 登录失败,请稍后重试';
}
}
/// 登出
Future<void> logout() async {
final api = ref.read(apiClientProvider);