mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-31 16:18:22 +08:00
feat: login devices
Closes #1140 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -948,4 +948,7 @@ class Api {
|
||||
|
||||
static const String liveLikeReport =
|
||||
'${HttpString.liveBaseUrl}/xlive/app-ucenter/v1/like_info_v3/like/likeReportV3';
|
||||
|
||||
static const String loginDevices =
|
||||
'${HttpString.passBaseUrl}/x/safecenter/user_login_devices';
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import 'dart:convert';
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/login/model.dart';
|
||||
import 'package:PiliPlus/models_new/login_devices/data.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/app_sign.dart';
|
||||
import 'package:PiliPlus/utils/login_utils.dart';
|
||||
@@ -499,4 +502,31 @@ class LoginHttp {
|
||||
);
|
||||
return {'status': res.data['code'] == 0, 'msg': res.data['message']};
|
||||
}
|
||||
|
||||
static Future<LoadingState<LoginDevicesData>> loginDevices() async {
|
||||
final account = Accounts.main;
|
||||
final buvid = LoginUtils.buvid;
|
||||
final params = {
|
||||
'local_id': buvid,
|
||||
'buvid': buvid,
|
||||
'device_name': 'android',
|
||||
'device_platform': 'android',
|
||||
'csrf': account.csrf,
|
||||
'mobi_app': 'android_hd',
|
||||
'platform': 'android',
|
||||
'access_key': account.accessKey,
|
||||
'statistics': Constants.statistics,
|
||||
'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||
};
|
||||
AppSign.appSign(params);
|
||||
var res = await Request().get(
|
||||
Api.loginDevices,
|
||||
queryParameters: params,
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return Success(LoginDevicesData.fromJson(res.data['data']));
|
||||
} else {
|
||||
return Error(res.data['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
lib/models_new/login_devices/data.dart
Normal file
14
lib/models_new/login_devices/data.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:PiliPlus/models_new/login_devices/device.dart';
|
||||
|
||||
class LoginDevicesData {
|
||||
List<LoginDevice>? devices;
|
||||
|
||||
LoginDevicesData({this.devices});
|
||||
|
||||
factory LoginDevicesData.fromJson(Map<String, dynamic> json) =>
|
||||
LoginDevicesData(
|
||||
devices: (json['devices'] as List<dynamic>?)
|
||||
?.map((e) => LoginDevice.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
32
lib/models_new/login_devices/device.dart
Normal file
32
lib/models_new/login_devices/device.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
class LoginDevice {
|
||||
int? mid;
|
||||
String? localId;
|
||||
String? deviceName;
|
||||
String? devicePlatform;
|
||||
bool? isCurrentDevice;
|
||||
String? latestLoginAt;
|
||||
String? source;
|
||||
int? origin;
|
||||
|
||||
LoginDevice({
|
||||
this.mid,
|
||||
this.localId,
|
||||
this.deviceName,
|
||||
this.devicePlatform,
|
||||
this.isCurrentDevice,
|
||||
this.latestLoginAt,
|
||||
this.source,
|
||||
this.origin,
|
||||
});
|
||||
|
||||
factory LoginDevice.fromJson(Map<String, dynamic> json) => LoginDevice(
|
||||
mid: json['mid'] as int?,
|
||||
localId: json['local_id'] as String?,
|
||||
deviceName: json['device_name'] as String?,
|
||||
devicePlatform: json['device_platform'] as String?,
|
||||
isCurrentDevice: json['is_current_device'] as bool?,
|
||||
latestLoginAt: json['latest_login_at'] as String?,
|
||||
source: json['source'] as String?,
|
||||
origin: json['origin'] as int?,
|
||||
);
|
||||
}
|
||||
23
lib/pages/login_devices/controller.dart
Normal file
23
lib/pages/login_devices/controller.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/login.dart';
|
||||
import 'package:PiliPlus/models_new/login_devices/data.dart';
|
||||
import 'package:PiliPlus/models_new/login_devices/device.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
|
||||
class LoginDevicesController
|
||||
extends CommonListController<LoginDevicesData, LoginDevice> {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
List<LoginDevice>? getDataList(LoginDevicesData response) {
|
||||
return response.devices;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<LoginDevicesData>> customGetData() =>
|
||||
LoginHttp.loginDevices();
|
||||
}
|
||||
97
lib/pages/login_devices/view.dart
Normal file
97
lib/pages/login_devices/view.dart
Normal file
@@ -0,0 +1,97 @@
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models_new/login_devices/device.dart';
|
||||
import 'package:PiliPlus/pages/login_devices/controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class LoginDevicesPage extends StatefulWidget {
|
||||
const LoginDevicesPage({super.key});
|
||||
|
||||
@override
|
||||
State<LoginDevicesPage> createState() => LloginDevicesPageState();
|
||||
}
|
||||
|
||||
class LloginDevicesPageState extends State<LoginDevicesPage> {
|
||||
final _controller = Get.put(LoginDevicesController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('登录设备')),
|
||||
body: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 650),
|
||||
child: refreshIndicator(
|
||||
onRefresh: _controller.onRefresh,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
ViewSliverSafeArea(
|
||||
sliver: MediaQuery.removeViewPadding(
|
||||
context: context,
|
||||
removeLeft: true,
|
||||
removeRight: true,
|
||||
child: Obx(
|
||||
() => _buildBody(
|
||||
colorScheme,
|
||||
_controller.loadingState.value,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(
|
||||
ColorScheme colorScheme,
|
||||
LoadingState<List<LoginDevice>?> loadingState,
|
||||
) {
|
||||
late final divider = Divider(
|
||||
height: 1,
|
||||
color: colorScheme.outline.withValues(alpha: 0.1),
|
||||
);
|
||||
return switch (loadingState) {
|
||||
Loading() => const SliverToBoxAdapter(),
|
||||
Success<List<LoginDevice>?>(:var response) =>
|
||||
response?.isNotEmpty == true
|
||||
? SliverList.separated(
|
||||
itemBuilder: (context, index) {
|
||||
return _buildItem(colorScheme, response[index]);
|
||||
},
|
||||
itemCount: response!.length,
|
||||
separatorBuilder: (_, _) => divider,
|
||||
)
|
||||
: HttpError(onReload: _controller.onReload),
|
||||
Error(:var errMsg) => HttpError(
|
||||
errMsg: errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Widget _buildItem(ColorScheme colorScheme, LoginDevice item) {
|
||||
final style = TextStyle(fontSize: 13, color: colorScheme.outline);
|
||||
return ListTile(
|
||||
dense: true,
|
||||
title: Text(
|
||||
item.deviceName ?? '',
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
subtitle: Text(
|
||||
'${item.latestLoginAt} ${item.source}',
|
||||
style: style,
|
||||
),
|
||||
trailing: item.isCurrentDevice == true
|
||||
? Text('(本机)', style: style)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import 'package:PiliPlus/models_new/space/space/data.dart';
|
||||
import 'package:PiliPlus/pages/coin_log/controller.dart';
|
||||
import 'package:PiliPlus/pages/exp_log/controller.dart';
|
||||
import 'package:PiliPlus/pages/log_table/view.dart';
|
||||
import 'package:PiliPlus/pages/login_devices/view.dart';
|
||||
import 'package:PiliPlus/pages/login_log/controller.dart';
|
||||
import 'package:PiliPlus/pages/member/controller.dart';
|
||||
import 'package:PiliPlus/pages/member/widget/user_info_card.dart';
|
||||
@@ -190,6 +191,17 @@ class _MemberPageState extends State<MemberPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () => Get.to(const LoginDevicesPage()),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.devices, size: 18),
|
||||
SizedBox(width: 10),
|
||||
Text('登录设备'),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () => Get.to(
|
||||
const LogPage(),
|
||||
|
||||
Reference in New Issue
Block a user