master
parent
5baa58d3f6
commit
54fdafd329
@ -0,0 +1 @@
|
|||||||
|
/Users/zhangmeng/fvm/versions/3.0.0
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"flutterSdkVersion": "3.0.0",
|
||||||
|
"flavors": {}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import '../environment/environment.dart';
|
||||||
|
|
||||||
|
|
||||||
|
/// * user 用户接口
|
||||||
|
/// * data 基础数据接口
|
||||||
|
/// * product 产品接口
|
||||||
|
class API {
|
||||||
|
static const tokenKey = 'Broker-Token';
|
||||||
|
|
||||||
|
///HOST
|
||||||
|
static String get host {
|
||||||
|
if (DevEV.instance.dev) {
|
||||||
|
return 'https://apiwenche.oa00.com';
|
||||||
|
} else {
|
||||||
|
return 'https://api.yunyunwenche.com';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String get imageHost {
|
||||||
|
if (DevEV.instance.dev) {
|
||||||
|
return 'https://static.oa00.com/wenche';
|
||||||
|
} else {
|
||||||
|
return 'https://static.yunyunwenche.com';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String get baseURL => '$host/app/broker';
|
||||||
|
//根分类
|
||||||
|
///文件
|
||||||
|
static _File file = _File();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _File {
|
||||||
|
///图片上传
|
||||||
|
String get uploadImage => '/file/upload/image';
|
||||||
|
|
||||||
|
///文件上传
|
||||||
|
String get uploadFile => '/file/upload/file';
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
|
||||||
|
class AppTheme {
|
||||||
|
static ThemeData get theme {
|
||||||
|
return ThemeData(primarySwatch: Colors.blue).copyWith(
|
||||||
|
primaryColor: const Color(0xFF027AFF),
|
||||||
|
extensions: <ThemeExtension<dynamic>>[
|
||||||
|
MyAppStyle(
|
||||||
|
mainColor: Colors.blue,
|
||||||
|
bodyText3: TextStyle(
|
||||||
|
fontSize: 30.sp,
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
textTheme: ThemeData.light().textTheme.copyWith(
|
||||||
|
headline3: TextStyle(
|
||||||
|
fontSize: 40.sp,
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
headline4: TextStyle(
|
||||||
|
fontSize: 36.sp,
|
||||||
|
color: const Color(0xFF111111),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
subtitle1: TextStyle(
|
||||||
|
fontSize: 32.sp,
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
),
|
||||||
|
subtitle2: TextStyle(
|
||||||
|
fontSize: 28.sp,
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
),
|
||||||
|
bodyText1: TextStyle(
|
||||||
|
fontSize: 24.sp,
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
),
|
||||||
|
bodyText2: TextStyle(
|
||||||
|
fontSize: 28.sp,
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButtonTheme: const FloatingActionButtonThemeData().copyWith(
|
||||||
|
backgroundColor: const Color(0xFFFFD000),
|
||||||
|
),
|
||||||
|
appBarTheme: AppBarTheme(
|
||||||
|
elevation: 0,
|
||||||
|
centerTitle: true,
|
||||||
|
iconTheme: const IconThemeData(
|
||||||
|
color: Color(0xFF333333),
|
||||||
|
),
|
||||||
|
systemOverlayStyle: SystemUiOverlayStyle.dark,
|
||||||
|
toolbarTextStyle: TextTheme(
|
||||||
|
headline6: TextStyle(
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
fontSize: 36.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
).bodyText2,
|
||||||
|
titleTextStyle: TextTheme(
|
||||||
|
headline6: TextStyle(
|
||||||
|
color: const Color(0xFF333333),
|
||||||
|
fontSize: 36.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
).headline6,
|
||||||
|
),
|
||||||
|
tabBarTheme: TabBarTheme(
|
||||||
|
labelColor: const Color(0xFF333333),
|
||||||
|
labelStyle: TextStyle(
|
||||||
|
fontSize: 28.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
unselectedLabelStyle: TextStyle(
|
||||||
|
fontSize: 28.sp,
|
||||||
|
),
|
||||||
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
|
),
|
||||||
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||||
|
selectedItemColor: Color(0xFF333333),
|
||||||
|
selectedLabelStyle: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
type: BottomNavigationBarType.fixed,
|
||||||
|
unselectedLabelStyle: TextStyle(),
|
||||||
|
),
|
||||||
|
radioTheme: RadioThemeData(
|
||||||
|
fillColor: MaterialStateProperty.resolveWith<Color?>((states) {
|
||||||
|
if (states.contains(MaterialState.selected)) {
|
||||||
|
return const Color(0xFFFFD000);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return const Color(0xFFFFF4D7);
|
||||||
|
}
|
||||||
|
return const Color(0xFFFFD000);
|
||||||
|
}),
|
||||||
|
elevation: MaterialStateProperty.all(0),
|
||||||
|
foregroundColor: MaterialStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return const Color(0xFF666666);
|
||||||
|
}
|
||||||
|
return const Color(0xFF333333);
|
||||||
|
}),
|
||||||
|
textStyle: MaterialStateProperty.all(TextStyle(
|
||||||
|
fontSize: 32.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
)),
|
||||||
|
padding: MaterialStateProperty.all(
|
||||||
|
EdgeInsets.symmetric(horizontal: 76.w, vertical: 22.w),
|
||||||
|
),
|
||||||
|
enableFeedback: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: ButtonStyle(
|
||||||
|
overlayColor: MaterialStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return const Color(0xFFFFF4D7);
|
||||||
|
}
|
||||||
|
return const Color(0xFFFFD000).withOpacity(0.2);
|
||||||
|
}),
|
||||||
|
foregroundColor: MaterialStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return const Color(0xFF666666);
|
||||||
|
}
|
||||||
|
return const Color(0xFF333333);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
dividerColor: const Color(0xFFE8E8E8),
|
||||||
|
colorScheme: ColorScheme.fromSwatch()
|
||||||
|
.copyWith(secondary: const Color(0xFF027AFF))
|
||||||
|
.copyWith(secondary: const Color(0xFF027AFF)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SystemStyle {
|
||||||
|
static const initial = SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
systemNavigationBarColor: Colors.white,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const yellowBottomBar = SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
systemNavigationBarColor: Color(0xFFFFD000),
|
||||||
|
);
|
||||||
|
|
||||||
|
static genStyle({required Color bottom}) {
|
||||||
|
return SystemUiOverlayStyle(
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
systemNavigationBarColor: bottom,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class MyAppStyle extends ThemeExtension<MyAppStyle> {
|
||||||
|
final TextStyle? bodyText3;
|
||||||
|
final Color? mainColor;
|
||||||
|
@override
|
||||||
|
MyAppStyle copyWith({Color? mainColor, TextStyle? bodyText3}) {
|
||||||
|
return MyAppStyle(
|
||||||
|
mainColor: mainColor ?? this.mainColor,
|
||||||
|
bodyText3: bodyText3 ?? this.bodyText3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ThemeExtension<MyAppStyle> lerp(ThemeExtension<MyAppStyle>? other, double t) {
|
||||||
|
if (other is! MyAppStyle) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return MyAppStyle(
|
||||||
|
mainColor: Color.lerp(mainColor, other.mainColor, t),
|
||||||
|
bodyText3: TextStyle.lerp(bodyText3, other.bodyText3, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MyAppStyle({
|
||||||
|
this.bodyText3,
|
||||||
|
this.mainColor,
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
class DevEV {
|
||||||
|
static final DevEV _instance = DevEV._();
|
||||||
|
|
||||||
|
DevEV._();
|
||||||
|
|
||||||
|
static DevEV get instance => _instance;
|
||||||
|
|
||||||
|
bool dev = false;
|
||||||
|
|
||||||
|
void setEnvironment(context, {required bool environment}) {
|
||||||
|
dev = environment;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'base_list_model.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class BaseListModel<T> {
|
||||||
|
final int code;
|
||||||
|
final String msg;
|
||||||
|
final ListInnerModel? data;
|
||||||
|
|
||||||
|
BaseListModel({
|
||||||
|
required this.code,
|
||||||
|
required this.msg,
|
||||||
|
required this.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory BaseListModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$BaseListModelFromJson(json);
|
||||||
|
|
||||||
|
List get nullSafetyList {
|
||||||
|
if (data == null) {
|
||||||
|
return [];
|
||||||
|
} else if (data!.list == null) {
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
return data!.list!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get nullSafetyTotal {
|
||||||
|
if (data == null) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return data!.total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class ListInnerModel {
|
||||||
|
final List<dynamic>? list;
|
||||||
|
final int total;
|
||||||
|
|
||||||
|
ListInnerModel({
|
||||||
|
this.list,
|
||||||
|
required this.total,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ListInnerModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ListInnerModelFromJson(json);
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'base_model.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class BaseModel<T> {
|
||||||
|
int code;
|
||||||
|
String msg;
|
||||||
|
dynamic data;
|
||||||
|
BaseModel({
|
||||||
|
required this.code,
|
||||||
|
required this.msg,
|
||||||
|
this.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
List<T> map(T Function(dynamic json) f) {
|
||||||
|
if (data == null) return [];
|
||||||
|
return (data! as List<T>).map(f).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
factory BaseModel.unknown() => BaseModel(
|
||||||
|
code: -1,
|
||||||
|
msg: 'UNKNOW FAIL',
|
||||||
|
);
|
||||||
|
|
||||||
|
factory BaseModel.dioErr(DioError err) => BaseModel(
|
||||||
|
code: err.response?.statusCode ?? -1,
|
||||||
|
msg: err.message,
|
||||||
|
);
|
||||||
|
|
||||||
|
factory BaseModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$BaseModelFromJson(json) as BaseModel<T>;
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
part 'user_info_model.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class UserInfoModel extends Equatable {
|
||||||
|
final String inviteCode;
|
||||||
|
final String nickname;
|
||||||
|
final String headImg;
|
||||||
|
final int gender;
|
||||||
|
final String phone;
|
||||||
|
final int level;
|
||||||
|
|
||||||
|
factory UserInfoModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$UserInfoModelFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$UserInfoModelToJson(this);
|
||||||
|
|
||||||
|
const UserInfoModel({
|
||||||
|
required this.inviteCode,
|
||||||
|
required this.nickname,
|
||||||
|
required this.headImg,
|
||||||
|
required this.gender,
|
||||||
|
required this.phone,
|
||||||
|
required this.level,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
inviteCode,
|
||||||
|
nickname,
|
||||||
|
headImg,
|
||||||
|
gender,
|
||||||
|
phone,
|
||||||
|
level,
|
||||||
|
];
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'user_info_model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
UserInfoModel _$UserInfoModelFromJson(Map<String, dynamic> json) =>
|
||||||
|
UserInfoModel(
|
||||||
|
inviteCode: json['inviteCode'] as String,
|
||||||
|
nickname: json['nickname'] as String,
|
||||||
|
headImg: json['headImg'] as String,
|
||||||
|
gender: json['gender'] as int,
|
||||||
|
phone: json['phone'] as String,
|
||||||
|
level: json['level'] as int,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$UserInfoModelToJson(UserInfoModel instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'inviteCode': instance.inviteCode,
|
||||||
|
'nickname': instance.nickname,
|
||||||
|
'headImg': instance.headImg,
|
||||||
|
'gender': instance.gender,
|
||||||
|
'phone': instance.phone,
|
||||||
|
'level': instance.level,
|
||||||
|
};
|
@ -0,0 +1,59 @@
|
|||||||
|
import 'package:cloud_car_internal/models/user/user_info_model.dart';
|
||||||
|
import 'package:cloud_car_internal/utils/api_client.dart';
|
||||||
|
import 'package:cloud_car_internal/utils/hive_util.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
|
import '../constants/api/api.dart';
|
||||||
|
import '../utils/toast/cloud_toast.dart';
|
||||||
|
|
||||||
|
class UserProvider extends ChangeNotifier {
|
||||||
|
bool _isLogin = false;
|
||||||
|
|
||||||
|
bool get isLogin => _isLogin;
|
||||||
|
late UserInfoModel _userInfo;
|
||||||
|
|
||||||
|
UserInfoModel get userInfo => _userInfo;
|
||||||
|
|
||||||
|
Future<bool> init() async {
|
||||||
|
var appBox = await HiveUtil().openAppBox;
|
||||||
|
if (appBox.containsKey('token')) {
|
||||||
|
final token = appBox.get('token') as String;
|
||||||
|
_isLogin = true;
|
||||||
|
apiClient.setToken(token);
|
||||||
|
await updateUserInfo();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
_isLogin = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future setToken(String token, ) async {
|
||||||
|
var appBox = await HiveUtil().openAppBox;
|
||||||
|
apiClient.setToken(token);
|
||||||
|
await appBox.put('token', token);
|
||||||
|
_isLogin = true;
|
||||||
|
//每次打开app更新用户信息
|
||||||
|
await updateUserInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future logout() async {
|
||||||
|
var appBox = await HiveUtil().openAppBox;
|
||||||
|
apiClient.clearToken();
|
||||||
|
_isLogin = false;
|
||||||
|
await appBox.delete('token');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future updateUserInfo() async {
|
||||||
|
// var base = await apiClient.request(API.user.userInfo);
|
||||||
|
// if (base.code == 0) {
|
||||||
|
// _userInfo = UserInfoModel.fromJson(base.data);
|
||||||
|
// } else {
|
||||||
|
// CloudToast.show(base.msg);
|
||||||
|
// _userInfo = UserInfoModel.fail;
|
||||||
|
// }
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cloud_car_internal/constants/api/api.dart';
|
||||||
|
import 'package:cloud_car_internal/models/inner_models/base_llist_model.dart';
|
||||||
|
import 'package:cloud_car_internal/models/inner_models/base_modell.dart';
|
||||||
|
import 'package:cloud_car_internal/utils/toast/cloud_toast.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:platform/platform.dart';
|
||||||
|
import 'net_interceptor.dart';
|
||||||
|
|
||||||
|
final ApiClient apiClient = ApiClient();
|
||||||
|
|
||||||
|
class ApiClient {
|
||||||
|
late Dio _dio;
|
||||||
|
|
||||||
|
Dio get dio => _dio;
|
||||||
|
|
||||||
|
ApiClient() {
|
||||||
|
var headers = <String, dynamic>{};
|
||||||
|
if (kIsWeb) {
|
||||||
|
headers = {'device-type': 'web'};
|
||||||
|
} else {
|
||||||
|
headers = {
|
||||||
|
'device-type': const LocalPlatform().operatingSystem.toLowerCase()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_dio = Dio()
|
||||||
|
..options.baseUrl = API.baseURL
|
||||||
|
..options.headers = headers
|
||||||
|
..interceptors.add(NetworkInterceptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
///设置token
|
||||||
|
Dio setToken(String token) {
|
||||||
|
return _dio..options.headers[API.tokenKey] = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
///清除token
|
||||||
|
Dio clearToken() {
|
||||||
|
return _dio
|
||||||
|
..options.headers.removeWhere((key, value) => key == API.tokenKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
///发送请求
|
||||||
|
Future<BaseModel> request(
|
||||||
|
String path, {
|
||||||
|
dynamic data,
|
||||||
|
bool showMessage = false,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
var response = await _dio.post(path, data: data);
|
||||||
|
var baseModel = BaseModel.fromJson(response.data);
|
||||||
|
if (showMessage) CloudToast.show(baseModel.msg);
|
||||||
|
return baseModel;
|
||||||
|
} catch (e) {
|
||||||
|
if (e is DioError) {
|
||||||
|
return BaseModel.dioErr(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BaseModel.unknown();
|
||||||
|
}
|
||||||
|
|
||||||
|
///请求列表
|
||||||
|
Future<BaseListModel> requestList(
|
||||||
|
String path, {
|
||||||
|
dynamic data,
|
||||||
|
bool showMessage = false,
|
||||||
|
}) async {
|
||||||
|
var response = await _dio.post(path, data: data);
|
||||||
|
var baseModel = BaseListModel.fromJson(response.data);
|
||||||
|
if (showMessage) CloudToast.show(baseModel.msg);
|
||||||
|
return baseModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
///上传图片
|
||||||
|
Future<String> uploadImage(File file) async {
|
||||||
|
var response = await _dio.post(
|
||||||
|
API.file.uploadImage,
|
||||||
|
data: FormData.fromMap(
|
||||||
|
{'image': await MultipartFile.fromFile(file.path)},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
var model = BaseModel.fromJson(response.data);
|
||||||
|
return model.data['path'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<String>> uploadFiles(
|
||||||
|
List<File> files,
|
||||||
|
) async {
|
||||||
|
List<String> urls = [];
|
||||||
|
if (files.isEmpty) {
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
for (var item in files) {
|
||||||
|
String path = await uploadImage(item);
|
||||||
|
if (path.isNotEmpty) {
|
||||||
|
urls.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
///上传文件
|
||||||
|
Future<String> uploadFile(File file) async {
|
||||||
|
var response = await _dio.post(
|
||||||
|
API.file.uploadFile,
|
||||||
|
data: FormData.fromMap(
|
||||||
|
{'file': await MultipartFile.fromFile(file.path)},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
var model = BaseModel.fromJson(response.data);
|
||||||
|
return model.data['path'];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
class HiveUtil {
|
||||||
|
static late final HiveUtil _instance = HiveUtil._();
|
||||||
|
|
||||||
|
factory HiveUtil() => _instance;
|
||||||
|
|
||||||
|
HiveUtil._();
|
||||||
|
|
||||||
|
static init() async {
|
||||||
|
if (!kIsWeb) {
|
||||||
|
var dir = await getApplicationDocumentsDirectory();
|
||||||
|
Hive.init(dir.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Box> get openAppBox async {
|
||||||
|
return await Hive.openBox('app');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Box> get openUserBox async {
|
||||||
|
return await Hive.openBox('user');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
import 'package:cloud_car_internal/providers/user_provider.dart';
|
||||||
|
import 'package:cloud_car_internal/utils/toast/cloud_toast.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:get/get.dart' hide Response;
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class NetworkInterceptor extends Interceptor {
|
||||||
|
@override
|
||||||
|
void onResponse(Response response, ResponseInterceptorHandler handler) async {
|
||||||
|
final userProvider = Provider.of<UserProvider>(Get.context!, listen: false);
|
||||||
|
// LoggerData.addData(response);
|
||||||
|
int code = response.data['code'] ?? 0;
|
||||||
|
switch (code) {
|
||||||
|
//登录超时
|
||||||
|
case 100000:
|
||||||
|
await userProvider.logout();
|
||||||
|
CloudToast.show(response.data['msg']);
|
||||||
|
// await Get.offAll(() => const LoginPage());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
super.onResponse(response, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onError(DioError err, ErrorInterceptorHandler handler) async {
|
||||||
|
switch (err.type) {
|
||||||
|
case DioErrorType.connectTimeout:
|
||||||
|
case DioErrorType.sendTimeout:
|
||||||
|
case DioErrorType.receiveTimeout:
|
||||||
|
CloudToast.show('连接超时');
|
||||||
|
break;
|
||||||
|
case DioErrorType.response:
|
||||||
|
CloudToast.show('Server Err');
|
||||||
|
break;
|
||||||
|
case DioErrorType.cancel:
|
||||||
|
break;
|
||||||
|
case DioErrorType.other:
|
||||||
|
CloudToast.show('网络出现问题');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
super.onError(err, handler);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CloudToast {
|
||||||
|
static show(String text,{AlignmentGeometry? align}) {
|
||||||
|
BotToast.showText(
|
||||||
|
text: text,
|
||||||
|
align: align?? const Alignment(0, 0.8),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Function get loading {
|
||||||
|
return BotToast.showCustomLoading(
|
||||||
|
toastBuilder: (cancel) {
|
||||||
|
return const Center(
|
||||||
|
child: Material(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
shape: StadiumBorder(),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
part of './grind.dart';
|
||||||
|
|
||||||
|
@Task('打包Android项目')
|
||||||
|
buildApk() async {
|
||||||
|
await runAsync(
|
||||||
|
'fvm',
|
||||||
|
arguments: [
|
||||||
|
'flutter',
|
||||||
|
'build',
|
||||||
|
'apk',
|
||||||
|
'--target-platform=android-arm64',
|
||||||
|
'--dart-define',
|
||||||
|
'ENV=release',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
String date = DateUtil.formatDate(DateTime.now(), format: 'yy_MM_dd_HH_mm');
|
||||||
|
String version = await getVersion();
|
||||||
|
await runAsync('rm', arguments: ['-rf', Config.apkDir]);
|
||||||
|
await runAsync('mkdir', arguments: ['-p', Config.apkDir]);
|
||||||
|
await runAsync('mv', arguments: [
|
||||||
|
Config.buildPath,
|
||||||
|
'${Config.apkDir}/${Config.packageName}_${version}_release_$date.apk'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('打包Android项目')
|
||||||
|
buildApkDev() async {
|
||||||
|
await runAsync(
|
||||||
|
'fvm',
|
||||||
|
arguments: [
|
||||||
|
'flutter',
|
||||||
|
'build',
|
||||||
|
'apk',
|
||||||
|
'--target-platform=android-arm64',
|
||||||
|
'--dart-define',
|
||||||
|
'ENV=dev',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
String date = DateUtil.formatDate(DateTime.now(), format: 'yy_MM_dd_HH_mm');
|
||||||
|
String version = await getVersion();
|
||||||
|
await runAsync('rm', arguments: ['-rf', Config.apkDevDir]);
|
||||||
|
await runAsync('mkdir', arguments: ['-p', Config.apkDevDir]);
|
||||||
|
await runAsync('mv', arguments: [
|
||||||
|
Config.buildPath,
|
||||||
|
'${Config.apkDevDir}/${Config.packageName}_${version}_beta_$date.apk'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('打包iOS项目')
|
||||||
|
buildIos() async {
|
||||||
|
await runAsync(
|
||||||
|
'fvm',
|
||||||
|
arguments: [
|
||||||
|
'flutter',
|
||||||
|
'build',
|
||||||
|
'ios',
|
||||||
|
'--dart-define',
|
||||||
|
'ENV=release',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('打包iOS项目')
|
||||||
|
buildIosDev() async {
|
||||||
|
await runAsync(
|
||||||
|
'fvm',
|
||||||
|
arguments: [
|
||||||
|
'flutter',
|
||||||
|
'build',
|
||||||
|
'ios',
|
||||||
|
'--dart-define',
|
||||||
|
'ENV=dev',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
part of 'grind.dart';
|
||||||
|
|
||||||
|
@Task('import 排序')
|
||||||
|
void sort() {
|
||||||
|
Pub.run('import_sorter:main');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('格式化dart代码')
|
||||||
|
void format() {
|
||||||
|
DartFmt.format(libDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('自动提交修改')
|
||||||
|
@Depends(sort, format, gitPush)
|
||||||
|
void git() {
|
||||||
|
log(' commit to git');
|
||||||
|
run(
|
||||||
|
'git',
|
||||||
|
arguments: [
|
||||||
|
'commit',
|
||||||
|
'-a',
|
||||||
|
'-m',
|
||||||
|
'[auto task] sort & format',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('推送代码')
|
||||||
|
void gitPush() {
|
||||||
|
log(' push to git');
|
||||||
|
run(
|
||||||
|
'git',
|
||||||
|
arguments: ['push'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('build runner')
|
||||||
|
void gen() async {
|
||||||
|
await runAsync(
|
||||||
|
'fvm',
|
||||||
|
arguments: [
|
||||||
|
'flutter',
|
||||||
|
'pub',
|
||||||
|
'run',
|
||||||
|
'build_runner',
|
||||||
|
'build',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('build runner clean')
|
||||||
|
void genClean() async {
|
||||||
|
await runAsync(
|
||||||
|
'fvm',
|
||||||
|
arguments: [
|
||||||
|
'flutter',
|
||||||
|
'pub',
|
||||||
|
'run',
|
||||||
|
'build_runner',
|
||||||
|
'build',
|
||||||
|
'--delete-conflicting-outputs'
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
class Config {
|
||||||
|
///包名
|
||||||
|
static const String packageName = 'cloud_car';
|
||||||
|
|
||||||
|
///打包目录
|
||||||
|
static String get buildPath =>
|
||||||
|
'./build/app/outputs/flutter-apk/app-release.apk';
|
||||||
|
|
||||||
|
///测试包文件夹
|
||||||
|
static String get apkDevDir => '../team/cloud_car_internal/dev';
|
||||||
|
|
||||||
|
///正式包文件夹
|
||||||
|
static String get apkDir => '../team/cloud_car_internal/release';
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:grinder/grinder.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:pub_semver/pub_semver.dart';
|
||||||
|
import 'package:yaml/yaml.dart';
|
||||||
|
import 'config.dart';
|
||||||
|
import 'version_tool.dart';
|
||||||
|
import 'package:common_utils/common_utils.dart';
|
||||||
|
part '_build.dart';
|
||||||
|
part '_project_manage.dart';
|
||||||
|
|
||||||
|
main(args) => grind(args);
|
||||||
|
|
||||||
|
@Task('add minor version number')
|
||||||
|
void addVersion() async {
|
||||||
|
String projectPath = Directory('.').absolute.path;
|
||||||
|
String yamlPath = join(projectPath, 'pubspec.yaml');
|
||||||
|
String yamlContent = await File(yamlPath).readAsString();
|
||||||
|
dynamic content = loadYaml(yamlContent);
|
||||||
|
String version = content['version'];
|
||||||
|
//rename version
|
||||||
|
|
||||||
|
Version resultVersion = VersionTool.fromText(version).nextMinorTag('dev');
|
||||||
|
|
||||||
|
String result = yamlContent.replaceFirst(version, resultVersion.toString());
|
||||||
|
await File(yamlPath).writeAsString(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('add path version number')
|
||||||
|
void addVersionPatch() async {
|
||||||
|
String projectPath = Directory('.').absolute.path;
|
||||||
|
String yamlPath = join(projectPath, 'pubspec.yaml');
|
||||||
|
String yamlContent = await File(yamlPath).readAsString();
|
||||||
|
dynamic content = loadYaml(yamlContent);
|
||||||
|
String version = content['version'];
|
||||||
|
//rename version
|
||||||
|
|
||||||
|
Version resultVersion = VersionTool.fromText(version).nextPatchTag('dev');
|
||||||
|
|
||||||
|
String result = yamlContent.replaceFirst(version, resultVersion.toString());
|
||||||
|
await File(yamlPath).writeAsString(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task('add major version number')
|
||||||
|
void addVersionMajor() async {
|
||||||
|
String projectPath = Directory('.').absolute.path;
|
||||||
|
String yamlPath = join(projectPath, 'pubspec.yaml');
|
||||||
|
String yamlContent = await File(yamlPath).readAsString();
|
||||||
|
dynamic content = loadYaml(yamlContent);
|
||||||
|
String version = content['version'];
|
||||||
|
//rename version
|
||||||
|
|
||||||
|
Version resultVersion = VersionTool.fromText(version).nextMajorTag('dev');
|
||||||
|
|
||||||
|
String result = yamlContent.replaceFirst(version, resultVersion.toString());
|
||||||
|
await File(yamlPath).writeAsString(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Task()
|
||||||
|
Future<String> getVersion() async {
|
||||||
|
String projectPath = Directory('.').absolute.path;
|
||||||
|
String yamlPath = join(projectPath, 'pubspec.yaml');
|
||||||
|
String yamlContent = await File(yamlPath).readAsString();
|
||||||
|
dynamic content = loadYaml(yamlContent);
|
||||||
|
String version = content['version'];
|
||||||
|
return version;
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
import 'package:pub_semver/pub_semver.dart';
|
||||||
|
|
||||||
|
enum VersionNumber {
|
||||||
|
major,
|
||||||
|
minor,
|
||||||
|
patch,
|
||||||
|
}
|
||||||
|
|
||||||
|
class VersionTool {
|
||||||
|
Version version;
|
||||||
|
VersionTool(this.version);
|
||||||
|
VersionTool.fromText(String text) : version = Version.parse(text);
|
||||||
|
|
||||||
|
Version get nextMajor => _addBuildNumber(VersionNumber.major);
|
||||||
|
Version get nextMinor => _addBuildNumber(VersionNumber.minor);
|
||||||
|
Version get nextPatch => _addBuildNumber(VersionNumber.patch);
|
||||||
|
|
||||||
|
Version nextMajorTag(String tag) => _addBuildNumber(
|
||||||
|
VersionNumber.major,
|
||||||
|
tag: tag,
|
||||||
|
);
|
||||||
|
Version nextMinorTag(String tag) => _addBuildNumber(
|
||||||
|
VersionNumber.minor,
|
||||||
|
tag: tag,
|
||||||
|
);
|
||||||
|
Version nextPatchTag(String tag) => _addBuildNumber(
|
||||||
|
VersionNumber.patch,
|
||||||
|
tag: tag,
|
||||||
|
);
|
||||||
|
|
||||||
|
Version _addBuildNumber(VersionNumber type, {String? tag}) {
|
||||||
|
switch (type) {
|
||||||
|
case VersionNumber.major:
|
||||||
|
return Version(
|
||||||
|
version.major,
|
||||||
|
version.minor,
|
||||||
|
version.patch + 1,
|
||||||
|
pre: tag,
|
||||||
|
build: '${(version.build.first as int) + 1}',
|
||||||
|
);
|
||||||
|
case VersionNumber.minor:
|
||||||
|
return Version(
|
||||||
|
version.major,
|
||||||
|
version.minor + 1,
|
||||||
|
0,
|
||||||
|
pre: tag,
|
||||||
|
build: '${(version.build.first as int) + 1}',
|
||||||
|
);
|
||||||
|
case VersionNumber.patch:
|
||||||
|
return Version(
|
||||||
|
version.major + 1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
pre: tag,
|
||||||
|
build: '${(version.build.first as int) + 1}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue