角标和占位图替换
This commit is contained in:
@@ -238,7 +238,6 @@ class _DramaListPageState extends State<DramaListPage> with UniqueTagMixin {
|
||||
videoModel: model,
|
||||
textLines: 1,
|
||||
coverRightText: model.dramaInfo?.episodeNumberStatus,
|
||||
showLevelIcon: false,
|
||||
onTap: () => _openDrama(model),
|
||||
onTagTap: (tag) => DramaListPage.toTag(tag),
|
||||
);
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../config/config.dart';
|
||||
import '../../../hj_model/video_model.dart';
|
||||
import '../../../hj_utils/free_play_manager.dart';
|
||||
|
||||
/// 封面右上角的售卖角标:免费试看 / 金币 / VIP,三者互斥,都不满足就不占位。
|
||||
/// 展不展示还受 ping 下发的 freeMark / coinMark / vipMark 三个开关控制
|
||||
class LevelMarkChip extends StatelessWidget {
|
||||
final VideoModel? videoModel;
|
||||
|
||||
const LevelMarkChip(this.videoModel, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (videoModel?.freeArea == true) return const SizedBox();
|
||||
// 免费试看:ping 免费类角标总开关 freeMark + 实时试看权益(次数用尽即不再展示)
|
||||
if (Config.freeMark && FreePlayManager().canShowFreeTrialBadge(videoModel)) {
|
||||
return _chip("免费试看", const Color(0xff141414), const [Color(0xff35DEBC), Color(0xff22BB9C)]);
|
||||
}
|
||||
final isCoin = videoModel?.isCoinVideo() == true;
|
||||
if (isCoin && Config.coinMark) {
|
||||
return _chip("金币", const Color(0xff8B3E00), const [Color(0xffFFE580), Color(0xffFACC15)]);
|
||||
}
|
||||
if (!isCoin && Config.vipMark) {
|
||||
return _chip("VIP", const Color(0xff141414), const [Color(0xffF8F3AE), Color(0xffDDAC44)]);
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
Widget _chip(String text, Color textColor, List<Color> gradientColors) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
gradient: LinearGradient(colors: gradientColors),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(color: textColor, fontSize: 10, height: 1.6),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,8 @@ import 'package:tgdj/hj_utils/api_service/common_service.dart';
|
||||
import 'package:tgdj/hj_utils/screen.dart';
|
||||
|
||||
import '../../../hj_model/video_model.dart';
|
||||
import '../../../hj_utils/free_play_manager.dart';
|
||||
import '../../../routers/jump_router.dart';
|
||||
import '../../../tools_base/widget/net_image_widget.dart';
|
||||
import 'level_mark_chip.dart';
|
||||
import 'package:tgdj/extension/extensions.dart';
|
||||
|
||||
/// 横版视频 item:左封面(带售卖角标) + 右标题/收藏数,用在专题的小列表样式
|
||||
@@ -30,15 +28,6 @@ class VideoHorCell extends StatelessWidget {
|
||||
borderRadius: 4,
|
||||
imageUrl: videoModel?.cover ?? "",
|
||||
),
|
||||
Positioned(
|
||||
right: 6,
|
||||
top: 6,
|
||||
// 监听免费次数变化:会话内试看用尽时角标实时刷新,不再残留「免费试看」
|
||||
child: ValueListenableBuilder<int>(
|
||||
valueListenable: FreePlayManager().revision,
|
||||
builder: (_, __, ___) => LevelMarkChip(videoModel),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
12.sizeBoxW,
|
||||
|
||||
@@ -11,10 +11,8 @@ import 'package:tgdj/tools_base/global_store/store.dart';
|
||||
|
||||
import '../../../hj_model/splash/ads_model.dart';
|
||||
import '../../../hj_model/video_model.dart';
|
||||
import '../../../hj_utils/free_play_manager.dart';
|
||||
import '../../../tools_base/banner/ads_banner_widget.dart';
|
||||
import '../../../tools_base/widget/net_image_widget.dart';
|
||||
import 'level_mark_chip.dart';
|
||||
|
||||
/// 竖版视频 item:封面(角标/售卖标/暗网遮罩) + 标题 +(可选)标签/评论数。
|
||||
/// 命中广告数据时整体换成 [VideoSimpleAdsCell]
|
||||
@@ -29,7 +27,6 @@ class VideoSimpleCell extends StatelessWidget {
|
||||
final double titleFontSize; // 标题字号,小卡场景传小值
|
||||
final double coverFontSize; // 封面底部播放量/时长字号,小卡场景传小值避免溢出
|
||||
final String? coverRightText; // 封面右下角文案,默认时长;短剧传「共N集」
|
||||
final bool showLevelIcon; // 封面右上角「金币/VIP/免费试看」角标;短剧按集卖,整部剧标一个对不上,传 false
|
||||
final ValueChanged<TagsBean>? onTagTap; // 左下角标签点击;短剧跳标签详情
|
||||
|
||||
const VideoSimpleCell({
|
||||
@@ -44,7 +41,6 @@ class VideoSimpleCell extends StatelessWidget {
|
||||
this.titleFontSize = 12,
|
||||
this.coverFontSize = 10,
|
||||
this.coverRightText,
|
||||
this.showLevelIcon = true,
|
||||
this.onTagTap,
|
||||
});
|
||||
|
||||
@@ -134,16 +130,6 @@ class VideoSimpleCell extends StatelessWidget {
|
||||
child: isFromHY ? _buildCoverHYBottom() : _buildCoverNormalBottom(),
|
||||
),
|
||||
),
|
||||
if (showLevelIcon)
|
||||
Positioned(
|
||||
right: 6,
|
||||
top: 6,
|
||||
// 监听免费次数变化:会话内试看用尽时角标实时刷新,不再残留「免费试看」
|
||||
child: ValueListenableBuilder<int>(
|
||||
valueListenable: FreePlayManager().revision,
|
||||
builder: (_, __, ___) => LevelMarkChip(videoModel),
|
||||
),
|
||||
),
|
||||
if (_needVipUnlock)
|
||||
Container(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
|
||||
@@ -211,7 +211,6 @@ class _SearchVideoResultPageState extends State<SearchVideoResultPage> {
|
||||
videoModel: model,
|
||||
textLines: 1,
|
||||
coverRightText: model.dramaInfo?.episodeNumberStatus,
|
||||
showLevelIcon: false,
|
||||
isFromSearch: true,
|
||||
onTap: () => _openDrama(model),
|
||||
),
|
||||
@@ -299,7 +298,6 @@ class _SearchVideoResultPageState extends State<SearchVideoResultPage> {
|
||||
videoModel: videoModel,
|
||||
textLines: 1,
|
||||
coverRightText: videoModel.dramaInfo?.episodeNumberStatus,
|
||||
showLevelIcon: false,
|
||||
isFromSearch: true,
|
||||
onTap: () => _openDrama(videoModel),
|
||||
),
|
||||
|
||||
@@ -64,7 +64,11 @@ class _CommonSearchBarViewState extends State<CommonSearchBarView> {
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
child: Config.searchHints.isEmpty
|
||||
? Text('请输入关键字', style: _hintStyle)
|
||||
//父级给的是 36 的紧约束,Text 会撑满高度后从顶部起排,要 Align 压回垂直居中
|
||||
? Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text('请输入关键字', style: _hintStyle),
|
||||
)
|
||||
: MarqueeWidget(
|
||||
count: Config.searchHints.length,
|
||||
onIndexChanged: (index) =>
|
||||
|
||||
@@ -82,7 +82,6 @@ abstract class ColHisBuySubLogic extends ListBaseLogic {
|
||||
videoModel: model,
|
||||
textLines: 1,
|
||||
coverRightText: (model as VideoModel).dramaInfo?.episodeNumberStatus,
|
||||
showLevelIcon: false,
|
||||
onTap: () => _openDrama(model),
|
||||
),
|
||||
};
|
||||
|
||||
@@ -149,7 +149,6 @@ class _VideoCacheCellState extends State<VideoCacheCell> {
|
||||
//短剧和「热门短剧」橱窗一个样式:标题是剧名,封面右下角标本条是第几集;
|
||||
//按集卖,整部剧标一个金币/VIP 角标对不上,跟橱窗一样关掉
|
||||
coverRightText: isDrama ? '第${model.episodeNo ?? 1}集' : null,
|
||||
showLevelIcon: !isDrama,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,17 +57,6 @@ class FreePlayManager {
|
||||
_bumpRevision();
|
||||
}
|
||||
|
||||
/// 该视频当前是否仍应展示「免费试看」角标(无扣次副作用,供列表实时判断):
|
||||
/// - 后端未下发该角标:不展示;
|
||||
/// - 次数未知(remain 未拉到):信任后端下发的角标,避免误隐藏;
|
||||
/// - 次数已知:仅在还有剩余(>0)时展示,用尽即隐藏(含已试看过的视频,一并回落到真实角标)。
|
||||
bool canShowFreeTrialBadge(VideoModel? video) {
|
||||
if (video?.showFreeTrialBadge != true) return false;
|
||||
final left = remain?.watchCount;
|
||||
if (left == null) return true;
|
||||
return left > 0;
|
||||
}
|
||||
|
||||
/// 尝试消费一次免费观看权益,返回本视频能否免费播放。
|
||||
/// ⚠️ 有副作用:首次命中会扣次数 + 写本地缓存 + 上报服务端;
|
||||
/// 同一视频再次调用命中 [usedIds] 直接返回 true,不重复扣。
|
||||
|
||||
Reference in New Issue
Block a user