个人资料卡
This commit is contained in:
parent
23748737a2
commit
e41cb3fb24
@ -13,6 +13,7 @@ import 'package:yumi/modules/admin/editing/sc_editing_user_room_page.dart';
|
|||||||
import 'package:yumi/modules/admin/search/sc_edit_room_search_admin_page.dart';
|
import 'package:yumi/modules/admin/search/sc_edit_room_search_admin_page.dart';
|
||||||
import 'package:yumi/modules/admin/search/sc_edit_user_search_admin_page.dart';
|
import 'package:yumi/modules/admin/search/sc_edit_user_search_admin_page.dart';
|
||||||
import 'package:yumi/modules/user/profile/person_detail_page.dart';
|
import 'package:yumi/modules/user/profile/person_detail_page.dart';
|
||||||
|
import 'package:yumi/modules/user/profile/profile_gift_wall_detail_page.dart';
|
||||||
import 'package:yumi/modules/search/sc_search_page.dart';
|
import 'package:yumi/modules/search/sc_search_page.dart';
|
||||||
import 'package:yumi/modules/media/image_preview_page.dart';
|
import 'package:yumi/modules/media/image_preview_page.dart';
|
||||||
import 'package:yumi/modules/media/video_player_page.dart';
|
import 'package:yumi/modules/media/video_player_page.dart';
|
||||||
@ -26,6 +27,7 @@ import '../user/edit/edit_user_info_page2.dart';
|
|||||||
class SCMainRoute implements SCIRouterProvider {
|
class SCMainRoute implements SCIRouterProvider {
|
||||||
static String report = '/main/report';
|
static String report = '/main/report';
|
||||||
static String person = '/main/person';
|
static String person = '/main/person';
|
||||||
|
static String giftWall = '/main/person/giftWall';
|
||||||
static String edit = '/main/person/edit';
|
static String edit = '/main/person/edit';
|
||||||
static String follow = '/main/me/follow';
|
static String follow = '/main/me/follow';
|
||||||
static String vistors = '/main/me/vistors';
|
static String vistors = '/main/me/vistors';
|
||||||
@ -63,6 +65,14 @@ class SCMainRoute implements SCIRouterProvider {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
router.define(
|
||||||
|
giftWall,
|
||||||
|
handler: Handler(
|
||||||
|
handlerFunc:
|
||||||
|
(_, params) =>
|
||||||
|
ProfileGiftWallDetailPage(tageId: params['tageId']!.first),
|
||||||
|
),
|
||||||
|
);
|
||||||
router.define(
|
router.define(
|
||||||
mainSearch,
|
mainSearch,
|
||||||
handler: Handler(handlerFunc: (_, params) => SearchPage()),
|
handler: Handler(handlerFunc: (_, params) => SearchPage()),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
495
lib/modules/user/profile/profile_gift_wall_detail_page.dart
Normal file
495
lib/modules/user/profile/profile_gift_wall_detail_page.dart
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_gift_repository_imp.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||||
|
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
|
|
||||||
|
class ProfileGiftWallDetailPage extends StatefulWidget {
|
||||||
|
const ProfileGiftWallDetailPage({super.key, required this.tageId});
|
||||||
|
|
||||||
|
final String tageId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProfileGiftWallDetailPage> createState() =>
|
||||||
|
_ProfileGiftWallDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProfileGiftWallDetailPageState extends State<ProfileGiftWallDetailPage> {
|
||||||
|
static const Color _profileBg = Color(0xff072121);
|
||||||
|
static const Color _cardBg = Color(0xff08251E);
|
||||||
|
static const Color _profileBorder = Color(0xffB2FBCC);
|
||||||
|
|
||||||
|
SocialChatUserProfile? _profile;
|
||||||
|
List<SocialChatGiftRes> _gifts = [];
|
||||||
|
bool _loadingGifts = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_primeProfile();
|
||||||
|
_loadProfile();
|
||||||
|
_loadGiftWall();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _primeProfile() {
|
||||||
|
final currentProfile =
|
||||||
|
Provider.of<SocialChatUserProfileManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).userProfile;
|
||||||
|
if (currentProfile?.id == widget.tageId) {
|
||||||
|
_profile = currentProfile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _loadProfile() {
|
||||||
|
SCAccountRepository()
|
||||||
|
.loadUserInfo(widget.tageId)
|
||||||
|
.then((profile) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_profile = profile;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catchError((_) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadGiftWall() async {
|
||||||
|
final appGeneralManager = Provider.of<SCAppGeneralManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
final result = await SCGiftRepositoryImp().giftWall(widget.tageId);
|
||||||
|
final resolvedGifts = await _resolveGiftWallMeta(
|
||||||
|
result,
|
||||||
|
appGeneralManager,
|
||||||
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_gifts = resolvedGifts;
|
||||||
|
_loadingGifts = false;
|
||||||
|
});
|
||||||
|
} catch (_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_loadingGifts = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<SocialChatGiftRes>> _resolveGiftWallMeta(
|
||||||
|
List<SocialChatGiftRes> gifts,
|
||||||
|
SCAppGeneralManager appGeneralManager,
|
||||||
|
) async {
|
||||||
|
if (gifts.isEmpty) return gifts;
|
||||||
|
if (appGeneralManager.giftResList.isEmpty) {
|
||||||
|
await appGeneralManager.giftList(includeCustomized: false);
|
||||||
|
}
|
||||||
|
return gifts
|
||||||
|
.map((gift) => _mergeGiftMeta(gift, appGeneralManager))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatGiftRes _mergeGiftMeta(
|
||||||
|
SocialChatGiftRes gift,
|
||||||
|
SCAppGeneralManager appGeneralManager,
|
||||||
|
) {
|
||||||
|
final meta = _findGiftMeta(gift, appGeneralManager);
|
||||||
|
if (meta == null) return gift;
|
||||||
|
return gift.copyWith(
|
||||||
|
giftName: _firstNonBlank([gift.giftName, meta.giftName]),
|
||||||
|
giftCandy: gift.giftCandy ?? meta.giftCandy,
|
||||||
|
giftIntegral: gift.giftIntegral ?? meta.giftIntegral,
|
||||||
|
giftPhoto: _firstNonBlank([gift.giftPhoto, meta.giftPhoto]),
|
||||||
|
giftSourceUrl: _firstNonBlank([gift.giftSourceUrl, meta.giftSourceUrl]),
|
||||||
|
giftCode: _firstNonBlank([gift.giftCode, meta.giftCode]),
|
||||||
|
standardId: _firstNonBlank([gift.standardId, meta.standardId]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatGiftRes? _findGiftMeta(
|
||||||
|
SocialChatGiftRes gift,
|
||||||
|
SCAppGeneralManager appGeneralManager,
|
||||||
|
) {
|
||||||
|
final idCandidates = <String>[
|
||||||
|
gift.standardId ?? "",
|
||||||
|
gift.id ?? "",
|
||||||
|
gift.giftCode ?? "",
|
||||||
|
].where((id) => id.trim().isNotEmpty && id.trim() != "0");
|
||||||
|
|
||||||
|
for (final id in idCandidates) {
|
||||||
|
final meta = appGeneralManager.getGiftByIdOrStandardId(id);
|
||||||
|
if (meta != null) return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
final giftPhoto = (gift.giftPhoto ?? "").trim();
|
||||||
|
final giftCode = (gift.giftCode ?? "").trim();
|
||||||
|
for (final meta in appGeneralManager.giftResList) {
|
||||||
|
if (giftPhoto.isNotEmpty && giftPhoto == (meta.giftPhoto ?? "").trim()) {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
if (giftCode.isNotEmpty && giftCode == (meta.giftCode ?? "").trim()) {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _firstNonBlank(Iterable<String?> values) {
|
||||||
|
for (final value in values) {
|
||||||
|
final text = value?.trim();
|
||||||
|
if (text != null && text.isNotEmpty) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get _receivedCount {
|
||||||
|
return _gifts.fold<int>(0, (sum, gift) => sum + _giftQuantity(gift));
|
||||||
|
}
|
||||||
|
|
||||||
|
num get _totalValue {
|
||||||
|
return _gifts.fold<num>(
|
||||||
|
0,
|
||||||
|
(sum, gift) => sum + (_giftAmount(gift) * _giftQuantity(gift)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _giftQuantity(SocialChatGiftRes gift) {
|
||||||
|
return int.tryParse(gift.quantity ?? "") ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
num _giftAmount(SocialChatGiftRes gift) {
|
||||||
|
return gift.giftCandy ?? gift.giftIntegral ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatNumber(num value) {
|
||||||
|
if (value % 1 == 0) return value.toInt().toString();
|
||||||
|
return value.toStringAsFixed(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildGradientBorder({
|
||||||
|
double? width,
|
||||||
|
double? height,
|
||||||
|
required double radius,
|
||||||
|
required Widget child,
|
||||||
|
Color backgroundColor = _cardBg,
|
||||||
|
double endAlpha = 0.42,
|
||||||
|
}) {
|
||||||
|
final borderRadius = BorderRadius.circular(radius);
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [_profileBorder, _profileBorder.withValues(alpha: endAlpha)],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(1.w),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
|
borderRadius: BorderRadius.circular((radius - 1.w).clamp(0, radius)),
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTopBar() {
|
||||||
|
return SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 54.w,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
PositionedDirectional(
|
||||||
|
start: 8.w,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () => SCNavigatorUtils.goBack(context),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 52.w,
|
||||||
|
height: 52.w,
|
||||||
|
child: Icon(
|
||||||
|
Icons.keyboard_arrow_left_rounded,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 38.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Gift Wall",
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 19.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildProfileHeader() {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 12.w),
|
||||||
|
head(
|
||||||
|
url: _profile?.userAvatar ?? "",
|
||||||
|
width: 104.w,
|
||||||
|
headdress: _profile?.getHeaddress()?.sourceUrl,
|
||||||
|
),
|
||||||
|
SizedBox(height: 10.w),
|
||||||
|
socialchatNickNameText(
|
||||||
|
_profile?.userNickname ?? "",
|
||||||
|
maxWidth: 300.w,
|
||||||
|
fontSize: 20,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
type: _profile?.getVIP()?.name ?? "",
|
||||||
|
needScroll: (_profile?.userNickname?.characters.length ?? 0) > 13,
|
||||||
|
),
|
||||||
|
SizedBox(height: 18.w),
|
||||||
|
_buildSummaryCard(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSummaryCard() {
|
||||||
|
return _buildGradientBorder(
|
||||||
|
width: 355.w,
|
||||||
|
height: 51.w,
|
||||||
|
radius: 8.w,
|
||||||
|
backgroundColor: _cardBg,
|
||||||
|
endAlpha: 0.52,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_buildSummaryItem(
|
||||||
|
value: _formatNumber(_receivedCount),
|
||||||
|
label: "Received",
|
||||||
|
),
|
||||||
|
_buildSummaryDivider(),
|
||||||
|
_buildSummaryItem(
|
||||||
|
value: _formatNumber(_totalValue),
|
||||||
|
label: "Total Value",
|
||||||
|
showCoin: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSummaryItem({
|
||||||
|
required String value,
|
||||||
|
required String label,
|
||||||
|
bool showCoin = false,
|
||||||
|
}) {
|
||||||
|
return Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (showCoin) ...[_buildCoinIcon(14.w), SizedBox(width: 3.w)],
|
||||||
|
text(
|
||||||
|
value,
|
||||||
|
fontSize: 17,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
text(label, fontSize: 14, textColor: const Color(0xffB1B1B1)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSummaryDivider() {
|
||||||
|
return Container(
|
||||||
|
height: 28.w,
|
||||||
|
width: 1.w,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Color.fromRGBO(255, 255, 255, 0),
|
||||||
|
Colors.white,
|
||||||
|
Color.fromRGBO(255, 255, 255, 0),
|
||||||
|
],
|
||||||
|
stops: [0, 0.4862, 1],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildGiftGrid() {
|
||||||
|
if (_loadingGifts) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(top: 80.w),
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 24.w,
|
||||||
|
height: 24.w,
|
||||||
|
child: const CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: _profileBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_gifts.isEmpty) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(top: 90.w),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: 0.42,
|
||||||
|
child: Image.asset(
|
||||||
|
"sc_images/room/sc_icon_room_music_empty.png",
|
||||||
|
width: 72.w,
|
||||||
|
height: 72.w,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SliverPadding(
|
||||||
|
padding: EdgeInsets.fromLTRB(10.w, 34.w, 10.w, 0),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) => _buildGiftItem(_gifts[index]),
|
||||||
|
childCount: _gifts.length,
|
||||||
|
),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 3,
|
||||||
|
mainAxisSpacing: 25.w,
|
||||||
|
crossAxisSpacing: 22.w,
|
||||||
|
childAspectRatio: 104 / 130,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildGiftItem(SocialChatGiftRes gift) {
|
||||||
|
final quantity = _giftQuantity(gift);
|
||||||
|
final giftName =
|
||||||
|
(gift.giftName ?? "").trim().isEmpty ? "--" : gift.giftName!.trim();
|
||||||
|
return _buildGradientBorder(
|
||||||
|
radius: 8.w,
|
||||||
|
backgroundColor: _cardBg,
|
||||||
|
endAlpha: 0.62,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(10.w, 10.w, 10.w, 8.w),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: netImage(
|
||||||
|
url: gift.giftPhoto ?? "",
|
||||||
|
width: 82.w,
|
||||||
|
height: 74.w,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
noDefaultImg: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 7.w),
|
||||||
|
text(
|
||||||
|
"$giftName*$quantity",
|
||||||
|
fontSize: 13,
|
||||||
|
textColor: Colors.white,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
SizedBox(height: 5.w),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_buildCoinIcon(12.w),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
Flexible(
|
||||||
|
child: text(
|
||||||
|
_formatNumber(_giftAmount(gift)),
|
||||||
|
fontSize: 11,
|
||||||
|
textColor: Colors.white,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCoinIcon(double size) {
|
||||||
|
return SizedBox(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
child: ClipRect(
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Image.asset("sc_images/room/sc_icon_luckgift_coins_anim.webp"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bottomPadding = MediaQuery.of(context).padding.bottom + 22.w;
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: _profileBg,
|
||||||
|
body: SafeArea(
|
||||||
|
bottom: false,
|
||||||
|
child: CustomScrollView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
slivers: [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Column(children: [_buildTopBar(), _buildProfileHeader()]),
|
||||||
|
),
|
||||||
|
_buildGiftGrid(),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(top: 54.w, bottom: bottomPadding),
|
||||||
|
child: Center(
|
||||||
|
child: text(
|
||||||
|
"No Further Data Available~",
|
||||||
|
fontSize: 13,
|
||||||
|
textColor: const Color(0xffB1B1B1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,182 +1,250 @@
|
|||||||
/// id : 0
|
// id : 0
|
||||||
/// giftCode : ""
|
// giftCode : ""
|
||||||
/// giftPhoto : ""
|
// giftPhoto : ""
|
||||||
/// giftSourceUrl : ""
|
// giftSourceUrl : ""
|
||||||
/// giftCandy : 0.0
|
// giftCandy : 0.0
|
||||||
/// giftIntegral : 0.0
|
// giftIntegral : 0.0
|
||||||
/// special : ""
|
// special : ""
|
||||||
/// type : ""
|
// type : ""
|
||||||
/// giftTab : ""
|
// giftTab : ""
|
||||||
/// standardId : 0
|
// standardId : 0
|
||||||
/// explanationGift : false
|
// explanationGift : false
|
||||||
/// account : ""
|
// account : ""
|
||||||
/// userId : 0
|
// userId : 0
|
||||||
/// giftName : ""
|
// giftName : ""
|
||||||
/// expiredTime : 0
|
// expiredTime : 0
|
||||||
|
|
||||||
class SocialChatGiftRes {
|
class SocialChatGiftRes {
|
||||||
SocialChatGiftRes({
|
SocialChatGiftRes({
|
||||||
String? id,
|
String? id,
|
||||||
String? giftCode,
|
String? giftCode,
|
||||||
String? giftPhoto,
|
String? giftPhoto,
|
||||||
String? giftSourceUrl,
|
String? giftSourceUrl,
|
||||||
num? giftCandy,
|
num? giftCandy,
|
||||||
num? activityId,
|
num? activityId,
|
||||||
num? giftIntegral,
|
num? giftIntegral,
|
||||||
String? special,
|
String? special,
|
||||||
String? type,
|
String? type,
|
||||||
String? giftTab,
|
String? giftTab,
|
||||||
String? standardId,
|
String? standardId,
|
||||||
String? jumpUrl,
|
String? jumpUrl,
|
||||||
String? bannerUrl,
|
String? bannerUrl,
|
||||||
bool? explanationGift,
|
bool? explanationGift,
|
||||||
String? account,
|
String? account,
|
||||||
String? userId,
|
String? userId,
|
||||||
String? giftName,
|
String? giftName,
|
||||||
String? quantity,
|
String? quantity,
|
||||||
num? expiredTime,}){
|
num? expiredTime,
|
||||||
_id = id;
|
}) {
|
||||||
_giftCode = giftCode;
|
_id = id;
|
||||||
_giftPhoto = giftPhoto;
|
_giftCode = giftCode;
|
||||||
_giftSourceUrl = giftSourceUrl;
|
_giftPhoto = giftPhoto;
|
||||||
_giftCandy = giftCandy;
|
_giftSourceUrl = giftSourceUrl;
|
||||||
_activityId = activityId;
|
_giftCandy = giftCandy;
|
||||||
_giftIntegral = giftIntegral;
|
_activityId = activityId;
|
||||||
_special = special;
|
_giftIntegral = giftIntegral;
|
||||||
_type = type;
|
_special = special;
|
||||||
_giftTab = giftTab;
|
_type = type;
|
||||||
_standardId = standardId;
|
_giftTab = giftTab;
|
||||||
_jumpUrl = jumpUrl;
|
_standardId = standardId;
|
||||||
_bannerUrl = bannerUrl;
|
_jumpUrl = jumpUrl;
|
||||||
_explanationGift = explanationGift;
|
_bannerUrl = bannerUrl;
|
||||||
_account = account;
|
_explanationGift = explanationGift;
|
||||||
_userId = userId;
|
_account = account;
|
||||||
_giftName = giftName;
|
_userId = userId;
|
||||||
_quantity = quantity;
|
_giftName = giftName;
|
||||||
_expiredTime = expiredTime;
|
_quantity = quantity;
|
||||||
}
|
_expiredTime = expiredTime;
|
||||||
|
}
|
||||||
SocialChatGiftRes.fromJson(dynamic json) {
|
|
||||||
_id = json['id'];
|
SocialChatGiftRes.fromJson(dynamic json) {
|
||||||
_giftCode = json['giftCode'];
|
final giftConfig =
|
||||||
_giftPhoto = json['giftPhoto'];
|
_giftResMap(json['giftConfig']) ??
|
||||||
_giftSourceUrl = json['giftSourceUrl'];
|
_giftResMap(json['gift']) ??
|
||||||
_giftCandy = json['giftCandy'];
|
_giftResMap(json['giftInfo']) ??
|
||||||
_activityId = json['activityId'];
|
_giftResMap(json['giftVO']);
|
||||||
_giftIntegral = json['giftIntegral'];
|
|
||||||
_special = json['special'];
|
_id = _giftResString(json['id'] ?? giftConfig?['id']);
|
||||||
_type = json['type'];
|
_giftCode = _giftResString(json['giftCode'] ?? giftConfig?['giftCode']);
|
||||||
_giftTab = json['giftTab'];
|
_giftPhoto = _giftResString(
|
||||||
_standardId = json['standardId'];
|
json['giftPhoto'] ??
|
||||||
_jumpUrl = json['jumpUrl'];
|
json['giftCover'] ??
|
||||||
_bannerUrl = json['bannerUrl'];
|
json['cover'] ??
|
||||||
_explanationGift = json['explanationGift'];
|
giftConfig?['giftPhoto'] ??
|
||||||
_account = json['account'];
|
giftConfig?['giftCover'] ??
|
||||||
_userId = json['userId'];
|
giftConfig?['cover'],
|
||||||
_giftName = json['giftName'];
|
);
|
||||||
_quantity = json['quantity'];
|
_giftSourceUrl = _giftResString(
|
||||||
_expiredTime = json['expiredTime'];
|
json['giftSourceUrl'] ??
|
||||||
}
|
json['sourceUrl'] ??
|
||||||
String? _id;
|
giftConfig?['giftSourceUrl'] ??
|
||||||
String? _giftCode;
|
giftConfig?['sourceUrl'],
|
||||||
String? _giftPhoto;
|
);
|
||||||
String? _giftSourceUrl;
|
_giftCandy = _giftResNum(
|
||||||
num? _giftCandy;
|
json['giftCandy'] ??
|
||||||
num? _activityId;
|
json['giftAmount'] ??
|
||||||
num? _giftIntegral;
|
json['giftValue'] ??
|
||||||
String? _special;
|
json['amount'] ??
|
||||||
String? _type;
|
json['price'] ??
|
||||||
String? _giftTab;
|
json['value'] ??
|
||||||
String? _standardId;
|
json['coins'] ??
|
||||||
String? _jumpUrl;
|
giftConfig?['giftCandy'] ??
|
||||||
String? _bannerUrl;
|
giftConfig?['giftAmount'] ??
|
||||||
bool? _explanationGift;
|
giftConfig?['giftValue'] ??
|
||||||
String? _account;
|
giftConfig?['amount'] ??
|
||||||
String? _userId;
|
giftConfig?['price'] ??
|
||||||
String? _giftName;
|
giftConfig?['value'] ??
|
||||||
String? _quantity;
|
giftConfig?['coins'],
|
||||||
num? _expiredTime;
|
);
|
||||||
SocialChatGiftRes copyWith({ String? id,
|
_activityId = _giftResNum(json['activityId'] ?? giftConfig?['activityId']);
|
||||||
String? giftCode,
|
_giftIntegral = _giftResNum(
|
||||||
String? giftPhoto,
|
json['giftIntegral'] ??
|
||||||
String? giftSourceUrl,
|
json['integral'] ??
|
||||||
num? giftCandy,
|
giftConfig?['giftIntegral'] ??
|
||||||
num? activityId,
|
giftConfig?['integral'],
|
||||||
num? giftIntegral,
|
);
|
||||||
String? special,
|
_special = _giftResString(json['special'] ?? giftConfig?['special']);
|
||||||
String? type,
|
_type = _giftResString(json['type'] ?? giftConfig?['type']);
|
||||||
String? giftTab,
|
_giftTab = _giftResString(json['giftTab'] ?? giftConfig?['giftTab']);
|
||||||
String? standardId,
|
_standardId = _giftResString(
|
||||||
String? jumpUrl,
|
json['standardId'] ?? giftConfig?['standardId'],
|
||||||
String? bannerUrl,
|
);
|
||||||
bool? explanationGift,
|
_jumpUrl = _giftResString(json['jumpUrl'] ?? giftConfig?['jumpUrl']);
|
||||||
String? account,
|
_bannerUrl = _giftResString(json['bannerUrl'] ?? giftConfig?['bannerUrl']);
|
||||||
String? userId,
|
_explanationGift = json['explanationGift'];
|
||||||
String? giftName,
|
_account = _giftResString(json['account'] ?? giftConfig?['account']);
|
||||||
String? quantity,
|
_userId = _giftResString(json['userId'] ?? giftConfig?['userId']);
|
||||||
num? expiredTime,
|
_giftName = _giftResString(
|
||||||
}) => SocialChatGiftRes( id: id ?? _id,
|
json['giftName'] ??
|
||||||
giftCode: giftCode ?? _giftCode,
|
json['name'] ??
|
||||||
giftPhoto: giftPhoto ?? _giftPhoto,
|
giftConfig?['giftName'] ??
|
||||||
giftSourceUrl: giftSourceUrl ?? _giftSourceUrl,
|
giftConfig?['name'],
|
||||||
giftCandy: giftCandy ?? _giftCandy,
|
);
|
||||||
activityId: activityId ?? _activityId,
|
_quantity = _giftResString(
|
||||||
giftIntegral: giftIntegral ?? _giftIntegral,
|
json['quantity'] ?? json['giftQuantity'] ?? json['count'] ?? json['num'],
|
||||||
special: special ?? _special,
|
);
|
||||||
type: type ?? _type,
|
_expiredTime = _giftResNum(
|
||||||
giftTab: giftTab ?? _giftTab,
|
json['expiredTime'] ?? giftConfig?['expiredTime'],
|
||||||
standardId: standardId ?? _standardId,
|
);
|
||||||
jumpUrl: jumpUrl ?? _jumpUrl,
|
}
|
||||||
bannerUrl: bannerUrl ?? _bannerUrl,
|
String? _id;
|
||||||
explanationGift: explanationGift ?? _explanationGift,
|
String? _giftCode;
|
||||||
account: account ?? _account,
|
String? _giftPhoto;
|
||||||
userId: userId ?? _userId,
|
String? _giftSourceUrl;
|
||||||
giftName: giftName ?? _giftName,
|
num? _giftCandy;
|
||||||
quantity: quantity ?? _quantity,
|
num? _activityId;
|
||||||
expiredTime: expiredTime ?? _expiredTime,
|
num? _giftIntegral;
|
||||||
);
|
String? _special;
|
||||||
String? get id => _id;
|
String? _type;
|
||||||
String? get giftCode => _giftCode;
|
String? _giftTab;
|
||||||
String? get giftPhoto => _giftPhoto;
|
String? _standardId;
|
||||||
String? get giftSourceUrl => _giftSourceUrl;
|
String? _jumpUrl;
|
||||||
num? get giftCandy => _giftCandy;
|
String? _bannerUrl;
|
||||||
num? get activityId => _activityId;
|
bool? _explanationGift;
|
||||||
num? get giftIntegral => _giftIntegral;
|
String? _account;
|
||||||
String? get special => _special;
|
String? _userId;
|
||||||
String? get type => _type;
|
String? _giftName;
|
||||||
String? get giftTab => _giftTab;
|
String? _quantity;
|
||||||
String? get standardId => _standardId;
|
num? _expiredTime;
|
||||||
String? get jumpUrl => _jumpUrl;
|
SocialChatGiftRes copyWith({
|
||||||
String? get bannerUrl => _bannerUrl;
|
String? id,
|
||||||
bool? get explanationGift => _explanationGift;
|
String? giftCode,
|
||||||
String? get account => _account;
|
String? giftPhoto,
|
||||||
String? get userId => _userId;
|
String? giftSourceUrl,
|
||||||
String? get giftName => _giftName;
|
num? giftCandy,
|
||||||
String? get quantity => _quantity;
|
num? activityId,
|
||||||
num? get expiredTime => _expiredTime;
|
num? giftIntegral,
|
||||||
|
String? special,
|
||||||
Map<String, dynamic> toJson() {
|
String? type,
|
||||||
final map = <String, dynamic>{};
|
String? giftTab,
|
||||||
map['id'] = _id;
|
String? standardId,
|
||||||
map['giftCode'] = _giftCode;
|
String? jumpUrl,
|
||||||
map['giftPhoto'] = _giftPhoto;
|
String? bannerUrl,
|
||||||
map['giftSourceUrl'] = _giftSourceUrl;
|
bool? explanationGift,
|
||||||
map['giftCandy'] = _giftCandy;
|
String? account,
|
||||||
map['activityId'] = _activityId;
|
String? userId,
|
||||||
map['giftIntegral'] = _giftIntegral;
|
String? giftName,
|
||||||
map['special'] = _special;
|
String? quantity,
|
||||||
map['type'] = _type;
|
num? expiredTime,
|
||||||
map['giftTab'] = _giftTab;
|
}) => SocialChatGiftRes(
|
||||||
map['standardId'] = _standardId;
|
id: id ?? _id,
|
||||||
map['jumpUrl'] = _jumpUrl;
|
giftCode: giftCode ?? _giftCode,
|
||||||
map['bannerUrl'] = _bannerUrl;
|
giftPhoto: giftPhoto ?? _giftPhoto,
|
||||||
map['explanationGift'] = _explanationGift;
|
giftSourceUrl: giftSourceUrl ?? _giftSourceUrl,
|
||||||
map['account'] = _account;
|
giftCandy: giftCandy ?? _giftCandy,
|
||||||
map['userId'] = _userId;
|
activityId: activityId ?? _activityId,
|
||||||
map['giftName'] = _giftName;
|
giftIntegral: giftIntegral ?? _giftIntegral,
|
||||||
map['quantity'] = _quantity;
|
special: special ?? _special,
|
||||||
map['expiredTime'] = _expiredTime;
|
type: type ?? _type,
|
||||||
return map;
|
giftTab: giftTab ?? _giftTab,
|
||||||
}
|
standardId: standardId ?? _standardId,
|
||||||
|
jumpUrl: jumpUrl ?? _jumpUrl,
|
||||||
}
|
bannerUrl: bannerUrl ?? _bannerUrl,
|
||||||
|
explanationGift: explanationGift ?? _explanationGift,
|
||||||
|
account: account ?? _account,
|
||||||
|
userId: userId ?? _userId,
|
||||||
|
giftName: giftName ?? _giftName,
|
||||||
|
quantity: quantity ?? _quantity,
|
||||||
|
expiredTime: expiredTime ?? _expiredTime,
|
||||||
|
);
|
||||||
|
String? get id => _id;
|
||||||
|
String? get giftCode => _giftCode;
|
||||||
|
String? get giftPhoto => _giftPhoto;
|
||||||
|
String? get giftSourceUrl => _giftSourceUrl;
|
||||||
|
num? get giftCandy => _giftCandy;
|
||||||
|
num? get activityId => _activityId;
|
||||||
|
num? get giftIntegral => _giftIntegral;
|
||||||
|
String? get special => _special;
|
||||||
|
String? get type => _type;
|
||||||
|
String? get giftTab => _giftTab;
|
||||||
|
String? get standardId => _standardId;
|
||||||
|
String? get jumpUrl => _jumpUrl;
|
||||||
|
String? get bannerUrl => _bannerUrl;
|
||||||
|
bool? get explanationGift => _explanationGift;
|
||||||
|
String? get account => _account;
|
||||||
|
String? get userId => _userId;
|
||||||
|
String? get giftName => _giftName;
|
||||||
|
String? get quantity => _quantity;
|
||||||
|
num? get expiredTime => _expiredTime;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final map = <String, dynamic>{};
|
||||||
|
map['id'] = _id;
|
||||||
|
map['giftCode'] = _giftCode;
|
||||||
|
map['giftPhoto'] = _giftPhoto;
|
||||||
|
map['giftSourceUrl'] = _giftSourceUrl;
|
||||||
|
map['giftCandy'] = _giftCandy;
|
||||||
|
map['activityId'] = _activityId;
|
||||||
|
map['giftIntegral'] = _giftIntegral;
|
||||||
|
map['special'] = _special;
|
||||||
|
map['type'] = _type;
|
||||||
|
map['giftTab'] = _giftTab;
|
||||||
|
map['standardId'] = _standardId;
|
||||||
|
map['jumpUrl'] = _jumpUrl;
|
||||||
|
map['bannerUrl'] = _bannerUrl;
|
||||||
|
map['explanationGift'] = _explanationGift;
|
||||||
|
map['account'] = _account;
|
||||||
|
map['userId'] = _userId;
|
||||||
|
map['giftName'] = _giftName;
|
||||||
|
map['quantity'] = _quantity;
|
||||||
|
map['expiredTime'] = _expiredTime;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic>? _giftResMap(dynamic value) {
|
||||||
|
if (value is Map<String, dynamic>) return value;
|
||||||
|
if (value is Map) return Map<String, dynamic>.from(value);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _giftResString(dynamic value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
num? _giftResNum(dynamic value) {
|
||||||
|
if (value == null) return null;
|
||||||
|
if (value is num) return value;
|
||||||
|
return num.tryParse(value.toString());
|
||||||
|
}
|
||||||
|
|||||||
BIN
sc_images/person/sc_icon_profile_following.png
Normal file
BIN
sc_images/person/sc_icon_profile_following.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
sc_images/person/sc_icon_profile_gift_wall_title.png
Normal file
BIN
sc_images/person/sc_icon_profile_gift_wall_title.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
BIN
sc_images/person/sc_icon_profile_wall_of_honors_title.png
Normal file
BIN
sc_images/person/sc_icon_profile_wall_of_honors_title.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
9
需求进度.md
9
需求进度.md
@ -9,7 +9,14 @@
|
|||||||
- 第一步已去掉个人资料页加载阶段的骨架屏:`ref.userProfile` 或拉黑状态还没返回时,正文区域改为空占位,不再展示灰色骨架块;资料返回后仍按原来的 `Header + About/Giftwall Tab` 结构渲染。
|
- 第一步已去掉个人资料页加载阶段的骨架屏:`ref.userProfile` 或拉黑状态还没返回时,正文区域改为空占位,不再展示灰色骨架块;资料返回后仍按原来的 `Header + About/Giftwall Tab` 结构渲染。
|
||||||
- 第二步已将个人资料页上方默认背景图从旧的 `sc_images/person/sc_icon_my_head_bg_defalt.png` 切换为新素材 `sc_images/person/sc_icon_profile_card_default_bg.png`;用户已有可用 `backgroundPhotos` 时仍优先展示用户自定义背景。
|
- 第二步已将个人资料页上方默认背景图从旧的 `sc_images/person/sc_icon_my_head_bg_defalt.png` 切换为新素材 `sc_images/person/sc_icon_profile_card_default_bg.png`;用户已有可用 `backgroundPhotos` 时仍优先展示用户自定义背景。
|
||||||
- 第三步已调整头部资料区对齐:深绿资料区去掉顶部圆角改为矩形;头像/头像框移动到中间;用户名居中单独一行;ID、复制按钮、性别年龄和国旗收成同一行居中展示,复制按钮点击后仍写入剪贴板并提示 copied。
|
- 第三步已调整头部资料区对齐:深绿资料区去掉顶部圆角改为矩形;头像/头像框移动到中间;用户名居中单独一行;ID、复制按钮、性别年龄和国旗收成同一行居中展示,复制按钮点击后仍写入剪贴板并提示 copied。
|
||||||
- 验证进度:`dart format lib/modules/user/profile/person_detail_page.dart` 已执行;`flutter analyze --no-fatal-infos lib/modules/user/profile/person_detail_page.dart` 无 error/warning,仅保留该旧文件原有的 info 级代码风格提示。下一步再继续逐项对齐资料页 UI。
|
- 第四步已继续对齐设计稿:ID 行下方新增一行徽章预留占位;用户 `autograph/bio` 已前移到徽章占位下方并按 `Introduction:` 展示;Visitor/Follow/Fans 统计块改为 `355*51`、圆角 `8` 的纵向渐隐绿色描边,中间分割线改为高度 `28` 的白色纵向渐隐线。
|
||||||
|
- 第五步已把个人资料页大背景切到 `#072121`,Visitor/Follow/Fans 渐变卡内部保持 `#08251E`;下方 `About me / Gift wall` tab 结构已移除,改为设计图同款纵向 `Wall Of Honors` + `Gift Wall` section。`Wall Of Honors` 现用语音房 music 空态图做占位;两个 section 的 item 统一为 `76*76`、圆角 `8`、纵向渐隐绿色描边,`Gift Wall` 继续复用原礼物墙接口数据。
|
||||||
|
- 第六步已继续对稿:`Wall Of Honors` 与 `Gift Wall` 标题改为图片资源渲染,新增 `sc_icon_profile_wall_of_honors_title.png` 与 `sc_icon_profile_gift_wall_title.png`;荣誉/礼物 item 的描边底部透明度提高,解决下边线不明显的问题;非本人资料页底部操作区改为设计稿样式,`Follow` 为 `104*36`、`Chat` 为 `215*36`,同为 `#0A342D` 背景、`#18F2B1` 1px 边框、22 圆角和 28 图标;在房间用户会额外显示悬浮 `343*38` 房间入口,左侧头像昵称,右侧 `69*27` Enter 按钮,和底部按钮行保持 43 间距。
|
||||||
|
- 第七步已按 2026-05-07 最新反馈微调:重新覆盖 `Wall Of Honors / Gift Wall` 标题 PNG 风格;Visitor/Follow/Fans 统计卡底部描边透明度提高;荣誉/礼物 item 底部描边透明度继续提高;底部按钮区增加 `#072121`、高度 `93` 的背景;`Follow` 宽度增至 `154`,`Chat` 宽度缩至 `165`;Chat 图标改回旧图标 `sc_icon_person_tochat.png`;Following 状态新增并使用 `sc_icon_profile_following.png`。
|
||||||
|
- 第八步已继续按 2026-05-07 最新反馈收口:确认 `Wall Of Honors / Gift Wall` 标题直接使用用户替换后的 PNG 素材,不再自绘标题;非本人资料页底部按钮整体上移并扩大底部安全留白,减少被系统 Home Indicator 遮挡;`Gift Wall` 标题点击已进入新的独立礼物墙页面,页面包含返回栏、头像头像框、昵称、Received/Total Value 统计卡、三列礼物列表和底部 `No Further Data Available~` 文案。
|
||||||
|
- 第九步已修正 Gift Wall 详情页初版问题:顶部导航栏补齐满屏宽度,避免返回箭头挤进标题;头像缩小到 `104`,昵称字号改为正常单次缩放;金币展示改用现有单枚金币动效素材;礼物数据模型兼容 `giftConfig/name/amount/price/value/coins/giftQuantity` 等接口字段,避免礼物名和金额接口有值但页面显示 `-- / 0`。
|
||||||
|
- 第十步已为 Gift Wall 详情页补齐礼物名称/金额解析链路:礼物墙接口返回后会再用全局 `/gift/list` 配置表按 `standardId / id / giftCode / giftPhoto` 做二次匹配,补齐缺失的 `giftName / giftCandy / giftIntegral / giftPhoto`,用于单个礼物卡片和 `Total Value` 统计。
|
||||||
|
- 验证进度:`dart format lib/modules/user/profile/profile_gift_wall_detail_page.dart lib/shared/business_logic/models/res/gift_res.dart` 已执行;`flutter analyze --no-fatal-infos lib/modules/user/profile/profile_gift_wall_detail_page.dart lib/shared/business_logic/models/res/gift_res.dart lib/modules/user/profile/person_detail_page.dart lib/modules/index/main_route.dart` 无 error/warning,仅保留旧文件原有的 info 级代码风格提示。下一步再继续逐项对齐资料页 UI。
|
||||||
|
|
||||||
## 本轮 Android 语言房音乐功能(进行中)
|
## 本轮 Android 语言房音乐功能(进行中)
|
||||||
- 已查阅桌面《Android音乐添加流程.md》,并产出可执行方案文档 `docs/android-room-music-execution-plan.md`,覆盖功能范围、模块拆分、Android 权限、声网混音链路、UI 流程、风险点和 GPT 5.5 预估开发工时。
|
- 已查阅桌面《Android音乐添加流程.md》,并产出可执行方案文档 `docs/android-room-music-execution-plan.md`,覆盖功能范围、模块拆分、Android 权限、声网混音链路、UI 流程、风险点和 GPT 5.5 预估开发工时。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user