角标和占位图替换
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 22 KiB |
@@ -238,7 +238,6 @@ class _DramaListPageState extends State<DramaListPage> with UniqueTagMixin {
|
|||||||
videoModel: model,
|
videoModel: model,
|
||||||
textLines: 1,
|
textLines: 1,
|
||||||
coverRightText: model.dramaInfo?.episodeNumberStatus,
|
coverRightText: model.dramaInfo?.episodeNumberStatus,
|
||||||
showLevelIcon: false,
|
|
||||||
onTap: () => _openDrama(model),
|
onTap: () => _openDrama(model),
|
||||||
onTagTap: (tag) => DramaListPage.toTag(tag),
|
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 'package:tgdj/hj_utils/screen.dart';
|
||||||
|
|
||||||
import '../../../hj_model/video_model.dart';
|
import '../../../hj_model/video_model.dart';
|
||||||
import '../../../hj_utils/free_play_manager.dart';
|
|
||||||
import '../../../routers/jump_router.dart';
|
import '../../../routers/jump_router.dart';
|
||||||
import '../../../tools_base/widget/net_image_widget.dart';
|
import '../../../tools_base/widget/net_image_widget.dart';
|
||||||
import 'level_mark_chip.dart';
|
|
||||||
import 'package:tgdj/extension/extensions.dart';
|
import 'package:tgdj/extension/extensions.dart';
|
||||||
|
|
||||||
/// 横版视频 item:左封面(带售卖角标) + 右标题/收藏数,用在专题的小列表样式
|
/// 横版视频 item:左封面(带售卖角标) + 右标题/收藏数,用在专题的小列表样式
|
||||||
@@ -30,15 +28,6 @@ class VideoHorCell extends StatelessWidget {
|
|||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
imageUrl: videoModel?.cover ?? "",
|
imageUrl: videoModel?.cover ?? "",
|
||||||
),
|
),
|
||||||
Positioned(
|
|
||||||
right: 6,
|
|
||||||
top: 6,
|
|
||||||
// 监听免费次数变化:会话内试看用尽时角标实时刷新,不再残留「免费试看」
|
|
||||||
child: ValueListenableBuilder<int>(
|
|
||||||
valueListenable: FreePlayManager().revision,
|
|
||||||
builder: (_, __, ___) => LevelMarkChip(videoModel),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
12.sizeBoxW,
|
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/splash/ads_model.dart';
|
||||||
import '../../../hj_model/video_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/banner/ads_banner_widget.dart';
|
||||||
import '../../../tools_base/widget/net_image_widget.dart';
|
import '../../../tools_base/widget/net_image_widget.dart';
|
||||||
import 'level_mark_chip.dart';
|
|
||||||
|
|
||||||
/// 竖版视频 item:封面(角标/售卖标/暗网遮罩) + 标题 +(可选)标签/评论数。
|
/// 竖版视频 item:封面(角标/售卖标/暗网遮罩) + 标题 +(可选)标签/评论数。
|
||||||
/// 命中广告数据时整体换成 [VideoSimpleAdsCell]
|
/// 命中广告数据时整体换成 [VideoSimpleAdsCell]
|
||||||
@@ -29,7 +27,6 @@ class VideoSimpleCell extends StatelessWidget {
|
|||||||
final double titleFontSize; // 标题字号,小卡场景传小值
|
final double titleFontSize; // 标题字号,小卡场景传小值
|
||||||
final double coverFontSize; // 封面底部播放量/时长字号,小卡场景传小值避免溢出
|
final double coverFontSize; // 封面底部播放量/时长字号,小卡场景传小值避免溢出
|
||||||
final String? coverRightText; // 封面右下角文案,默认时长;短剧传「共N集」
|
final String? coverRightText; // 封面右下角文案,默认时长;短剧传「共N集」
|
||||||
final bool showLevelIcon; // 封面右上角「金币/VIP/免费试看」角标;短剧按集卖,整部剧标一个对不上,传 false
|
|
||||||
final ValueChanged<TagsBean>? onTagTap; // 左下角标签点击;短剧跳标签详情
|
final ValueChanged<TagsBean>? onTagTap; // 左下角标签点击;短剧跳标签详情
|
||||||
|
|
||||||
const VideoSimpleCell({
|
const VideoSimpleCell({
|
||||||
@@ -44,7 +41,6 @@ class VideoSimpleCell extends StatelessWidget {
|
|||||||
this.titleFontSize = 12,
|
this.titleFontSize = 12,
|
||||||
this.coverFontSize = 10,
|
this.coverFontSize = 10,
|
||||||
this.coverRightText,
|
this.coverRightText,
|
||||||
this.showLevelIcon = true,
|
|
||||||
this.onTagTap,
|
this.onTagTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -134,16 +130,6 @@ class VideoSimpleCell extends StatelessWidget {
|
|||||||
child: isFromHY ? _buildCoverHYBottom() : _buildCoverNormalBottom(),
|
child: isFromHY ? _buildCoverHYBottom() : _buildCoverNormalBottom(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (showLevelIcon)
|
|
||||||
Positioned(
|
|
||||||
right: 6,
|
|
||||||
top: 6,
|
|
||||||
// 监听免费次数变化:会话内试看用尽时角标实时刷新,不再残留「免费试看」
|
|
||||||
child: ValueListenableBuilder<int>(
|
|
||||||
valueListenable: FreePlayManager().revision,
|
|
||||||
builder: (_, __, ___) => LevelMarkChip(videoModel),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_needVipUnlock)
|
if (_needVipUnlock)
|
||||||
Container(
|
Container(
|
||||||
color: Colors.black.withValues(alpha: 0.1),
|
color: Colors.black.withValues(alpha: 0.1),
|
||||||
|
|||||||
@@ -211,7 +211,6 @@ class _SearchVideoResultPageState extends State<SearchVideoResultPage> {
|
|||||||
videoModel: model,
|
videoModel: model,
|
||||||
textLines: 1,
|
textLines: 1,
|
||||||
coverRightText: model.dramaInfo?.episodeNumberStatus,
|
coverRightText: model.dramaInfo?.episodeNumberStatus,
|
||||||
showLevelIcon: false,
|
|
||||||
isFromSearch: true,
|
isFromSearch: true,
|
||||||
onTap: () => _openDrama(model),
|
onTap: () => _openDrama(model),
|
||||||
),
|
),
|
||||||
@@ -299,7 +298,6 @@ class _SearchVideoResultPageState extends State<SearchVideoResultPage> {
|
|||||||
videoModel: videoModel,
|
videoModel: videoModel,
|
||||||
textLines: 1,
|
textLines: 1,
|
||||||
coverRightText: videoModel.dramaInfo?.episodeNumberStatus,
|
coverRightText: videoModel.dramaInfo?.episodeNumberStatus,
|
||||||
showLevelIcon: false,
|
|
||||||
isFromSearch: true,
|
isFromSearch: true,
|
||||||
onTap: () => _openDrama(videoModel),
|
onTap: () => _openDrama(videoModel),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -64,7 +64,11 @@ class _CommonSearchBarViewState extends State<CommonSearchBarView> {
|
|||||||
borderRadius: BorderRadius.circular(99),
|
borderRadius: BorderRadius.circular(99),
|
||||||
),
|
),
|
||||||
child: Config.searchHints.isEmpty
|
child: Config.searchHints.isEmpty
|
||||||
? Text('请输入关键字', style: _hintStyle)
|
//父级给的是 36 的紧约束,Text 会撑满高度后从顶部起排,要 Align 压回垂直居中
|
||||||
|
? Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text('请输入关键字', style: _hintStyle),
|
||||||
|
)
|
||||||
: MarqueeWidget(
|
: MarqueeWidget(
|
||||||
count: Config.searchHints.length,
|
count: Config.searchHints.length,
|
||||||
onIndexChanged: (index) =>
|
onIndexChanged: (index) =>
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ abstract class ColHisBuySubLogic extends ListBaseLogic {
|
|||||||
videoModel: model,
|
videoModel: model,
|
||||||
textLines: 1,
|
textLines: 1,
|
||||||
coverRightText: (model as VideoModel).dramaInfo?.episodeNumberStatus,
|
coverRightText: (model as VideoModel).dramaInfo?.episodeNumberStatus,
|
||||||
showLevelIcon: false,
|
|
||||||
onTap: () => _openDrama(model),
|
onTap: () => _openDrama(model),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -149,7 +149,6 @@ class _VideoCacheCellState extends State<VideoCacheCell> {
|
|||||||
//短剧和「热门短剧」橱窗一个样式:标题是剧名,封面右下角标本条是第几集;
|
//短剧和「热门短剧」橱窗一个样式:标题是剧名,封面右下角标本条是第几集;
|
||||||
//按集卖,整部剧标一个金币/VIP 角标对不上,跟橱窗一样关掉
|
//按集卖,整部剧标一个金币/VIP 角标对不上,跟橱窗一样关掉
|
||||||
coverRightText: isDrama ? '第${model.episodeNo ?? 1}集' : null,
|
coverRightText: isDrama ? '第${model.episodeNo ?? 1}集' : null,
|
||||||
showLevelIcon: !isDrama,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,17 +57,6 @@ class FreePlayManager {
|
|||||||
_bumpRevision();
|
_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,不重复扣。
|
/// 同一视频再次调用命中 [usedIds] 直接返回 true,不重复扣。
|
||||||
|
|||||||
Reference in New Issue
Block a user