@hlw-uni/mp-vue 2.1.13 → 2.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/composables/ad/index.d.ts +5 -0
- package/dist/index.js +12 -2
- package/dist/index.mjs +12 -2
- package/package.json +1 -1
- package/src/app.ts +15 -0
- package/src/components/hlw-card/index.vue +108 -84
- package/src/components/hlw-card-header/index.vue +112 -0
- package/src/components/hlw-page/index.vue +11 -0
- package/src/composables/ad/index.ts +13 -2
|
@@ -35,6 +35,11 @@ export interface AdAdapter {
|
|
|
35
35
|
isAuth?: () => boolean;
|
|
36
36
|
/** 是否 VIP;配合 config.vip_no_ad=1 时屏蔽展示型广告(reward 不受影响) */
|
|
37
37
|
isVip?: () => boolean;
|
|
38
|
+
/**
|
|
39
|
+
* 用户级强制屏蔽:返回 true 直接屏蔽展示型广告(reward 不受影响),
|
|
40
|
+
* 优先级高于 mp.vip_no_ad + isVip 的联合判断;不传 = 不强制。
|
|
41
|
+
*/
|
|
42
|
+
userNoAd?: () => boolean;
|
|
38
43
|
}
|
|
39
44
|
/** 激励视频关闭回调返回 */
|
|
40
45
|
export interface AdCloseResult {
|
package/dist/index.js
CHANGED
|
@@ -658,9 +658,11 @@ var __publicField = (obj, key, value) => {
|
|
|
658
658
|
const store = useAdStore();
|
|
659
659
|
const { loaded } = pinia.storeToRefs(store);
|
|
660
660
|
const config2 = vue.computed(() => {
|
|
661
|
-
var _a;
|
|
661
|
+
var _a, _b;
|
|
662
662
|
const raw = store.config;
|
|
663
|
-
|
|
663
|
+
const userForce = ((_a = adapter$1 == null ? void 0 : adapter$1.userNoAd) == null ? void 0 : _a.call(adapter$1)) === true;
|
|
664
|
+
const vipHide = raw.vip_no_ad === 1 && ((_b = adapter$1 == null ? void 0 : adapter$1.isVip) == null ? void 0 : _b.call(adapter$1)) === true;
|
|
665
|
+
if (userForce || vipHide) {
|
|
664
666
|
return { ...EMPTY, reward_unit_id: raw.reward_unit_id, vip_no_ad: 1 };
|
|
665
667
|
}
|
|
666
668
|
return raw;
|
|
@@ -1815,6 +1817,14 @@ var __publicField = (obj, key, value) => {
|
|
|
1815
1817
|
_interceptorCleanup.forEach((dispose) => dispose());
|
|
1816
1818
|
_interceptorCleanup = [];
|
|
1817
1819
|
const offRequest = http.onRequest((config2) => {
|
|
1820
|
+
const method = (config2.method ?? "GET").toUpperCase();
|
|
1821
|
+
if (method === "GET" && config2.data && typeof config2.data === "object") {
|
|
1822
|
+
const qs = Object.entries(config2.data).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
1823
|
+
if (qs) {
|
|
1824
|
+
config2.url = config2.url + (config2.url.includes("?") ? "&" : "?") + qs;
|
|
1825
|
+
}
|
|
1826
|
+
config2.data = void 0;
|
|
1827
|
+
}
|
|
1818
1828
|
const device = useDevice();
|
|
1819
1829
|
if (device.value) {
|
|
1820
1830
|
const d = device.value;
|
package/dist/index.mjs
CHANGED
|
@@ -657,9 +657,11 @@ function useAd() {
|
|
|
657
657
|
const store = useAdStore();
|
|
658
658
|
const { loaded } = storeToRefs(store);
|
|
659
659
|
const config2 = computed(() => {
|
|
660
|
-
var _a;
|
|
660
|
+
var _a, _b;
|
|
661
661
|
const raw = store.config;
|
|
662
|
-
|
|
662
|
+
const userForce = ((_a = adapter$1 == null ? void 0 : adapter$1.userNoAd) == null ? void 0 : _a.call(adapter$1)) === true;
|
|
663
|
+
const vipHide = raw.vip_no_ad === 1 && ((_b = adapter$1 == null ? void 0 : adapter$1.isVip) == null ? void 0 : _b.call(adapter$1)) === true;
|
|
664
|
+
if (userForce || vipHide) {
|
|
663
665
|
return { ...EMPTY, reward_unit_id: raw.reward_unit_id, vip_no_ad: 1 };
|
|
664
666
|
}
|
|
665
667
|
return raw;
|
|
@@ -1814,6 +1816,14 @@ function setupInterceptors(options = {}) {
|
|
|
1814
1816
|
_interceptorCleanup.forEach((dispose) => dispose());
|
|
1815
1817
|
_interceptorCleanup = [];
|
|
1816
1818
|
const offRequest = http.onRequest((config2) => {
|
|
1819
|
+
const method = (config2.method ?? "GET").toUpperCase();
|
|
1820
|
+
if (method === "GET" && config2.data && typeof config2.data === "object") {
|
|
1821
|
+
const qs = Object.entries(config2.data).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
|
|
1822
|
+
if (qs) {
|
|
1823
|
+
config2.url = config2.url + (config2.url.includes("?") ? "&" : "?") + qs;
|
|
1824
|
+
}
|
|
1825
|
+
config2.data = void 0;
|
|
1826
|
+
}
|
|
1817
1827
|
const device = useDevice();
|
|
1818
1828
|
if (device.value) {
|
|
1819
1829
|
const d = device.value;
|
package/package.json
CHANGED
package/src/app.ts
CHANGED
|
@@ -102,6 +102,21 @@ export function setupInterceptors(options: InterceptorOptions & { sigSecret?: st
|
|
|
102
102
|
* 请求拦截:注入设备信息、签名和 token。
|
|
103
103
|
*/
|
|
104
104
|
const offRequest = http.onRequest((config: RequestConfig) => {
|
|
105
|
+
// GET 请求:把 cfg.data 合并到 url query,并清空 cfg.data。
|
|
106
|
+
// 否则 uni.request 会在 sig 之后再把 data 自动拼到 URL,导致后端 raw query
|
|
107
|
+
// 多了未参与签名的字段(如 id/cate_id),sig 校验必然失败。
|
|
108
|
+
const method = (config.method ?? 'GET').toUpperCase();
|
|
109
|
+
if (method === 'GET' && config.data && typeof config.data === 'object') {
|
|
110
|
+
const qs = Object.entries(config.data as Record<string, unknown>)
|
|
111
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
112
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
|
|
113
|
+
.join('&');
|
|
114
|
+
if (qs) {
|
|
115
|
+
config.url = config.url + (config.url.includes('?') ? '&' : '?') + qs;
|
|
116
|
+
}
|
|
117
|
+
config.data = undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
105
120
|
const device = useDevice();
|
|
106
121
|
if (device.value) {
|
|
107
122
|
const d = device.value;
|
|
@@ -3,35 +3,21 @@
|
|
|
3
3
|
class="hlw-card"
|
|
4
4
|
:class="[
|
|
5
5
|
`hlw-card--radius-${radius}`,
|
|
6
|
-
|
|
6
|
+
...borderClasses,
|
|
7
7
|
]"
|
|
8
|
+
:style="rootStyle"
|
|
8
9
|
>
|
|
9
|
-
<!-- 头部 -->
|
|
10
|
+
<!-- 头部 — 用 #header slot 自定义;常规场景请用 <hlw-card-header> -->
|
|
10
11
|
<view v-if="hasHeader" class="hlw-card-header">
|
|
11
|
-
<slot name="header"
|
|
12
|
-
<view class="hlw-card-header-inner">
|
|
13
|
-
<!-- 头部左侧 -->
|
|
14
|
-
<view class="hlw-card-header-left">
|
|
15
|
-
<slot name="header-left">
|
|
16
|
-
<text v-if="title" class="hlw-card-title">{{ title }}</text>
|
|
17
|
-
</slot>
|
|
18
|
-
</view>
|
|
19
|
-
<!-- 头部右侧 -->
|
|
20
|
-
<view v-if="$slots['header-right'] || extra" class="hlw-card-header-right">
|
|
21
|
-
<slot name="header-right">
|
|
22
|
-
<text v-if="extra" class="hlw-card-extra">{{ extra }}</text>
|
|
23
|
-
</slot>
|
|
24
|
-
</view>
|
|
25
|
-
</view>
|
|
26
|
-
</slot>
|
|
12
|
+
<slot name="header" />
|
|
27
13
|
</view>
|
|
28
14
|
|
|
29
|
-
<!--
|
|
30
|
-
<view v-if="showDivider" class="hlw-card-divider"
|
|
15
|
+
<!-- 头部虚线分隔(有 #header slot 且 divider != false 时显示) -->
|
|
16
|
+
<view v-if="showDivider" class="hlw-card-divider" />
|
|
31
17
|
|
|
32
18
|
<!-- 内容区 -->
|
|
33
19
|
<view class="hlw-card-body" :class="{ 'hlw-card-body--padded': padding }">
|
|
34
|
-
<slot
|
|
20
|
+
<slot />
|
|
35
21
|
</view>
|
|
36
22
|
|
|
37
23
|
<!-- 底部 -->
|
|
@@ -39,10 +25,10 @@
|
|
|
39
25
|
<slot name="footer">
|
|
40
26
|
<view class="hlw-card-footer-inner">
|
|
41
27
|
<view class="hlw-card-footer-left">
|
|
42
|
-
<slot name="footer-left"
|
|
28
|
+
<slot name="footer-left" />
|
|
43
29
|
</view>
|
|
44
30
|
<view v-if="$slots['footer-right']" class="hlw-card-footer-right">
|
|
45
|
-
<slot name="footer-right"
|
|
31
|
+
<slot name="footer-right" />
|
|
46
32
|
</view>
|
|
47
33
|
</view>
|
|
48
34
|
</slot>
|
|
@@ -56,68 +42,106 @@ import { computed, useSlots } from "vue";
|
|
|
56
42
|
/**
|
|
57
43
|
* hlw-card 卡片容器
|
|
58
44
|
*
|
|
59
|
-
*
|
|
45
|
+
* 头部用法已迁出:常规标题 + 副标题请用 <hlw-card-header>,写在 default slot 里
|
|
46
|
+
* (记得 :padding="false" 避免 body padding 把 header 顶出来)。
|
|
47
|
+
*
|
|
48
|
+
* @example 标准头部 + body
|
|
60
49
|
* ```vue
|
|
61
|
-
* <hlw-card
|
|
62
|
-
* <
|
|
50
|
+
* <hlw-card :padding="false">
|
|
51
|
+
* <hlw-card-header title="标题" icon="i-fa6-solid-star" extra="副标题" />
|
|
52
|
+
* <view style="padding: 24rpx 28rpx">content</view>
|
|
63
53
|
* </hlw-card>
|
|
64
54
|
* ```
|
|
65
55
|
*
|
|
66
|
-
* @example
|
|
56
|
+
* @example 完全自定义头部
|
|
67
57
|
* ```vue
|
|
68
58
|
* <hlw-card>
|
|
69
|
-
* <template #header
|
|
70
|
-
* <text>自定义左侧</text>
|
|
71
|
-
* </template>
|
|
72
|
-
* <template #header-right>
|
|
73
|
-
* <button>操作</button>
|
|
74
|
-
* </template>
|
|
59
|
+
* <template #header>...</template>
|
|
75
60
|
* <text>内容</text>
|
|
76
61
|
* </hlw-card>
|
|
77
62
|
* ```
|
|
78
63
|
*
|
|
79
64
|
* @example 自定义底部
|
|
80
65
|
* ```vue
|
|
81
|
-
* <hlw-card
|
|
66
|
+
* <hlw-card>
|
|
82
67
|
* <text>内容</text>
|
|
83
|
-
* <template #footer-left>
|
|
84
|
-
*
|
|
85
|
-
* </template>
|
|
86
|
-
* <template #footer-right>
|
|
87
|
-
* <button>确认</button>
|
|
88
|
-
* </template>
|
|
68
|
+
* <template #footer-left><text>左侧说明</text></template>
|
|
69
|
+
* <template #footer-right><button>确认</button></template>
|
|
89
70
|
* </hlw-card>
|
|
90
71
|
* ```
|
|
91
72
|
*/
|
|
73
|
+
type BorderValue = boolean | string | string[];
|
|
74
|
+
|
|
92
75
|
interface Props {
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 边框,支持三种形式:
|
|
78
|
+
* - `true`(默认)/`false` —— 四边全开 / 全关
|
|
79
|
+
* - 字符串:"t r b l" 或 "top right bottom left",空格分隔,如 `"t b"` = 仅上下
|
|
80
|
+
* - 数组:`['t','l']` 同上
|
|
81
|
+
*/
|
|
82
|
+
border?: BorderValue;
|
|
83
|
+
/** 边框颜色,任意 CSS color:`#f00`、`rgb(...)`、`var(--xxx)`;默认走主题 var(--border-color) */
|
|
84
|
+
borderColor?: string;
|
|
85
|
+
/** 边框线型:solid(默认)/ dashed / dotted / double */
|
|
86
|
+
borderStyle?: "solid" | "dashed" | "dotted" | "double";
|
|
87
|
+
/** 边框宽度,CSS 长度值,默认 `1rpx` */
|
|
88
|
+
borderWidth?: string;
|
|
99
89
|
/** 圆角大小,对应 CSS 变量体系 */
|
|
100
90
|
radius?: "none" | "sm" | "md" | "lg" | "xl";
|
|
101
|
-
/**
|
|
91
|
+
/** 头部与内容之间是否显示虚线分隔,有 #header slot 时默认 true */
|
|
102
92
|
divider?: boolean;
|
|
103
93
|
/** body 是否有内边距,默认 true */
|
|
104
94
|
padding?: boolean;
|
|
105
95
|
}
|
|
106
96
|
|
|
107
97
|
const props = withDefaults(defineProps<Props>(), {
|
|
108
|
-
title: "",
|
|
109
|
-
extra: "",
|
|
110
98
|
border: true,
|
|
99
|
+
borderColor: "",
|
|
100
|
+
borderStyle: "solid",
|
|
101
|
+
borderWidth: "",
|
|
111
102
|
radius: "xl",
|
|
112
103
|
divider: undefined,
|
|
113
104
|
padding: true,
|
|
114
105
|
});
|
|
115
106
|
|
|
107
|
+
const rootStyle = computed<Record<string, string>>(() => {
|
|
108
|
+
const s: Record<string, string> = {};
|
|
109
|
+
if (props.borderColor) s["--card-border-color"] = props.borderColor;
|
|
110
|
+
if (props.borderStyle && props.borderStyle !== "solid") s["--card-border-style"] = props.borderStyle;
|
|
111
|
+
if (props.borderWidth) s["--card-border-width"] = props.borderWidth;
|
|
112
|
+
return s;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const SIDE_MAP: Record<string, string> = {
|
|
116
|
+
t: "top", top: "top",
|
|
117
|
+
r: "right", right: "right",
|
|
118
|
+
b: "bottom", bottom: "bottom",
|
|
119
|
+
l: "left", left: "left",
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const borderClasses = computed<string[]>(() => {
|
|
123
|
+
if (props.border === false) return [];
|
|
124
|
+
if (props.border === true) return ["hlw-card--bordered"];
|
|
125
|
+
|
|
126
|
+
const sides = Array.isArray(props.border)
|
|
127
|
+
? props.border
|
|
128
|
+
: String(props.border).trim().split(/\s+/).filter(Boolean);
|
|
129
|
+
|
|
130
|
+
const seen = new Set<string>();
|
|
131
|
+
const classes: string[] = [];
|
|
132
|
+
for (const s of sides) {
|
|
133
|
+
const side = SIDE_MAP[s.toLowerCase()];
|
|
134
|
+
if (side && !seen.has(side)) {
|
|
135
|
+
seen.add(side);
|
|
136
|
+
classes.push(`hlw-card--border-${side}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return classes.length === 4 ? ["hlw-card--bordered"] : classes;
|
|
140
|
+
});
|
|
141
|
+
|
|
116
142
|
const slots = useSlots();
|
|
117
143
|
|
|
118
|
-
const hasHeader = computed(
|
|
119
|
-
() => !!(props.title || props.extra || slots.header || slots["header-left"] || slots["header-right"]),
|
|
120
|
-
);
|
|
144
|
+
const hasHeader = computed(() => !!slots.header);
|
|
121
145
|
|
|
122
146
|
const hasFooter = computed(
|
|
123
147
|
() => !!(slots.footer || slots["footer-left"] || slots["footer-right"]),
|
|
@@ -142,46 +166,46 @@ const showDivider = computed(() => {
|
|
|
142
166
|
&--radius-lg { border-radius: var(--radius-lg, 24rpx); }
|
|
143
167
|
&--radius-xl { border-radius: var(--radius-xl, 32rpx); }
|
|
144
168
|
|
|
145
|
-
/* 边框 */
|
|
169
|
+
/* 边框 — width / style / color 全部走 CSS 变量,未设置时回落 */
|
|
146
170
|
&--bordered {
|
|
147
|
-
border:
|
|
171
|
+
border:
|
|
172
|
+
var(--card-border-width, 1rpx)
|
|
173
|
+
var(--card-border-style, solid)
|
|
174
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* 边框 — 单边 */
|
|
178
|
+
&--border-top {
|
|
179
|
+
border-top:
|
|
180
|
+
var(--card-border-width, 1rpx)
|
|
181
|
+
var(--card-border-style, solid)
|
|
182
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
183
|
+
}
|
|
184
|
+
&--border-right {
|
|
185
|
+
border-right:
|
|
186
|
+
var(--card-border-width, 1rpx)
|
|
187
|
+
var(--card-border-style, solid)
|
|
188
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
189
|
+
}
|
|
190
|
+
&--border-bottom {
|
|
191
|
+
border-bottom:
|
|
192
|
+
var(--card-border-width, 1rpx)
|
|
193
|
+
var(--card-border-style, solid)
|
|
194
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
195
|
+
}
|
|
196
|
+
&--border-left {
|
|
197
|
+
border-left:
|
|
198
|
+
var(--card-border-width, 1rpx)
|
|
199
|
+
var(--card-border-style, solid)
|
|
200
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
148
201
|
}
|
|
149
202
|
}
|
|
150
203
|
|
|
151
|
-
/* 头部 */
|
|
204
|
+
/* 头部 wrapper(#header slot) */
|
|
152
205
|
.hlw-card-header {
|
|
153
206
|
width: 100%;
|
|
154
207
|
}
|
|
155
208
|
|
|
156
|
-
.hlw-card-header-inner {
|
|
157
|
-
display: flex;
|
|
158
|
-
align-items: center;
|
|
159
|
-
justify-content: space-between;
|
|
160
|
-
padding: 24rpx 28rpx;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
.hlw-card-header-left {
|
|
164
|
-
flex: 1;
|
|
165
|
-
min-width: 0;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.hlw-card-header-right {
|
|
169
|
-
flex-shrink: 0;
|
|
170
|
-
margin-left: 16rpx;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.hlw-card-title {
|
|
174
|
-
font-size: var(--font-sm, 24rpx);
|
|
175
|
-
font-weight: 700;
|
|
176
|
-
color: #1e293b;
|
|
177
|
-
letter-spacing: 0.02em;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.hlw-card-extra {
|
|
181
|
-
font-size: var(--font-xs, 20rpx);
|
|
182
|
-
color: #94a3b8;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
209
|
/* 虚线分隔 */
|
|
186
210
|
.hlw-card-divider {
|
|
187
211
|
width: 100%;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="hlw-card-header">
|
|
3
|
+
<view class="hlw-card-header__left">
|
|
4
|
+
<slot name="left">
|
|
5
|
+
<text v-if="icon" class="hlw-card-header__icon" :class="icon" />
|
|
6
|
+
<text v-if="title" class="hlw-card-header__title">{{ title }}</text>
|
|
7
|
+
</slot>
|
|
8
|
+
</view>
|
|
9
|
+
<view v-if="hasRight" class="hlw-card-header__right">
|
|
10
|
+
<slot name="right">
|
|
11
|
+
<text v-if="extra" class="hlw-card-header__extra">{{ extra }}</text>
|
|
12
|
+
</slot>
|
|
13
|
+
</view>
|
|
14
|
+
</view>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup lang="ts">
|
|
18
|
+
import { computed, useSlots } from "vue";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* hlw-card-header — 卡片头部独立组件
|
|
22
|
+
*
|
|
23
|
+
* 三种用法:
|
|
24
|
+
*
|
|
25
|
+
* @example A. 直接当 <hlw-card> 的 default slot 子元素(注意把 hlw-card 的 padding 关掉,否则会双重 padding)
|
|
26
|
+
* ```vue
|
|
27
|
+
* <hlw-card :padding="false">
|
|
28
|
+
* <hlw-card-header title="标题" icon="i-fa6-solid-heart-pulse text-rose-500" extra="副标题" />
|
|
29
|
+
* <view style="padding: 24rpx 28rpx">body</view>
|
|
30
|
+
* </hlw-card>
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example B. 放进 <hlw-card> 的 #header slot(保留 body 默认 padding)
|
|
34
|
+
* ```vue
|
|
35
|
+
* <hlw-card>
|
|
36
|
+
* <template #header>
|
|
37
|
+
* <hlw-card-header title="标题" icon="i-fa6-solid-star" extra="副标题" />
|
|
38
|
+
* </template>
|
|
39
|
+
* body
|
|
40
|
+
* </hlw-card>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example C. 完全独立(不必嵌在 hlw-card 里)
|
|
44
|
+
* ```vue
|
|
45
|
+
* <hlw-card-header title="独立标题">
|
|
46
|
+
* <template #right><button>操作</button></template>
|
|
47
|
+
* </hlw-card-header>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
interface Props {
|
|
51
|
+
/** 标题文字 */
|
|
52
|
+
title?: string;
|
|
53
|
+
/** 图标 class(iconify 或自定义),如 `"i-fa6-solid-heart-pulse text-rose-500"` */
|
|
54
|
+
icon?: string;
|
|
55
|
+
/** 右侧附加文字(无 right slot 时显示) */
|
|
56
|
+
extra?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
60
|
+
title: "",
|
|
61
|
+
icon: "",
|
|
62
|
+
extra: "",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const slots = useSlots();
|
|
66
|
+
|
|
67
|
+
const hasRight = computed(() => !!(slots.right || props.extra));
|
|
68
|
+
|
|
69
|
+
defineOptions({
|
|
70
|
+
name: "HlwCardHeader",
|
|
71
|
+
});
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<style lang="scss" scoped>
|
|
75
|
+
.hlw-card-header {
|
|
76
|
+
width: 100%;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: space-between;
|
|
80
|
+
padding: 24rpx 28rpx;
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.hlw-card-header__left {
|
|
85
|
+
flex: 1;
|
|
86
|
+
min-width: 0;
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
gap: 12rpx;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.hlw-card-header__right {
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
margin-left: 16rpx;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.hlw-card-header__icon {
|
|
98
|
+
font-size: var(--font-base, 28rpx);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.hlw-card-header__title {
|
|
102
|
+
font-size: var(--font-sm, 24rpx);
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
color: var(--text-primary, #1e293b);
|
|
105
|
+
letter-spacing: 0.02em;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.hlw-card-header__extra {
|
|
109
|
+
font-size: var(--font-xs, 20rpx);
|
|
110
|
+
color: var(--text-subtle, #94a3b8);
|
|
111
|
+
}
|
|
112
|
+
</style>
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
<scroll-view
|
|
15
15
|
class="hlw-page-content"
|
|
16
|
+
:class="bodyClass"
|
|
17
|
+
:style="bodyStyle"
|
|
16
18
|
:scroll-y="true"
|
|
17
19
|
:enable-flex="true"
|
|
18
20
|
:enhanced="true"
|
|
@@ -39,16 +41,25 @@ defineOptions({
|
|
|
39
41
|
inheritAttrs: false,
|
|
40
42
|
});
|
|
41
43
|
|
|
44
|
+
type ClassValue = string | Record<string, boolean> | Array<string | Record<string, boolean>>;
|
|
45
|
+
type StyleValue = string | Record<string, string | number>;
|
|
46
|
+
|
|
42
47
|
interface Props {
|
|
43
48
|
title?: string;
|
|
44
49
|
isBack?: boolean;
|
|
45
50
|
bgClass?: string;
|
|
51
|
+
/** 透传到 scroll-view 内容区的 class,常用于直接挂 .container */
|
|
52
|
+
bodyClass?: ClassValue;
|
|
53
|
+
/** 透传到 scroll-view 内容区的 style */
|
|
54
|
+
bodyStyle?: StyleValue;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
const props = withDefaults(defineProps<Props>(), {
|
|
49
58
|
title: "",
|
|
50
59
|
isBack: false,
|
|
51
60
|
bgClass: "",
|
|
61
|
+
bodyClass: "",
|
|
62
|
+
bodyStyle: "",
|
|
52
63
|
});
|
|
53
64
|
</script>
|
|
54
65
|
|
|
@@ -87,6 +87,11 @@ export interface AdAdapter {
|
|
|
87
87
|
isAuth?: () => boolean;
|
|
88
88
|
/** 是否 VIP;配合 config.vip_no_ad=1 时屏蔽展示型广告(reward 不受影响) */
|
|
89
89
|
isVip?: () => boolean;
|
|
90
|
+
/**
|
|
91
|
+
* 用户级强制屏蔽:返回 true 直接屏蔽展示型广告(reward 不受影响),
|
|
92
|
+
* 优先级高于 mp.vip_no_ad + isVip 的联合判断;不传 = 不强制。
|
|
93
|
+
*/
|
|
94
|
+
userNoAd?: () => boolean;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
/** 激励视频关闭回调返回 */
|
|
@@ -147,10 +152,16 @@ export function useAd() {
|
|
|
147
152
|
const store = useAdStore();
|
|
148
153
|
const { loaded } = storeToRefs(store);
|
|
149
154
|
|
|
150
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* 真实生效的广告配置;以下任一命中即屏蔽展示型 unit_id(reward 保留):
|
|
157
|
+
* 1. adapter.userNoAd() === true —— 用户级强制屏蔽,优先级最高
|
|
158
|
+
* 2. raw.vip_no_ad === 1 && adapter.isVip() —— 小程序级 VIP 隐藏开关
|
|
159
|
+
*/
|
|
151
160
|
const config = computed<AdConfig>(() => {
|
|
152
161
|
const raw = store.config;
|
|
153
|
-
|
|
162
|
+
const userForce = adapter?.userNoAd?.() === true;
|
|
163
|
+
const vipHide = raw.vip_no_ad === 1 && adapter?.isVip?.() === true;
|
|
164
|
+
if (userForce || vipHide) {
|
|
154
165
|
return { ...EMPTY, reward_unit_id: raw.reward_unit_id, vip_no_ad: 1 };
|
|
155
166
|
}
|
|
156
167
|
return raw;
|